Skip to content

Commit

Permalink
Port Socket::Addrinfo to win32 (#10650)
Browse files Browse the repository at this point in the history
  • Loading branch information
straight-shoota authored May 18, 2021
1 parent b9be7ff commit 9cb47a7
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 10 deletions.
2 changes: 1 addition & 1 deletion spec/std/socket/addrinfo_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "socket"
require "socket/addrinfo"

describe Socket::Addrinfo do
describe ".resolve" do
Expand Down
2 changes: 1 addition & 1 deletion spec/win32_std_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ require "./std/set_spec.cr"
# require "./std/signal_spec.cr" (failed codegen)
require "./std/slice_spec.cr"
require "./std/socket/address_spec.cr"
# require "./std/socket/addrinfo_spec.cr" (failed codegen)
require "./std/socket/addrinfo_spec.cr"
# require "./std/socket/socket_spec.cr" (failed codegen)
# require "./std/socket/tcp_server_spec.cr" (failed codegen)
# require "./std/socket/tcp_socket_spec.cr" (failed codegen)
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-windows-msvc/c/winbase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ lib LibC
FORMAT_MESSAGE_FROM_HMODULE = 0x00000800_u32
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000_u32
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000_u32
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF_u32

fun FormatMessageW(dwFlags : DWORD, lpSource : Void*, dwMessageId : DWORD, dwLanguageId : DWORD,
lpBuffer : LPWSTR, nSize : DWORD, arguments : Void*) : DWORD
Expand Down
17 changes: 17 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/winsock2.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,27 @@ lib LibC
AF_IRDA = 26
AF_BTH = 32

SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5

struct InAddr
s_addr : UInt32
end

struct WSAData
wVersion : WORD
wHighVersion : WORD
szDescription : Char[257]
szSystemStatus : Char[129]
iMaxSockets : UInt16
iMaxUdpDg : UInt16
lpVendorInfo : Char*
end

fun htons(hostshort : UShort) : UShort
fun ntohs(netshort : UShort) : UShort
fun WSAStartup(wVersionRequired : WORD, lpWSAData : WSAData*) : Int
end
24 changes: 24 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/ws2def.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,32 @@ lib LibC
IPPROTO_ICMPV6 = 58
IPPROTO_RAW = 255

AI_PASSIVE = 0x0001
AI_CANONNAME = 0x0002
AI_NUMERICHOST = 0x0004
AI_ALL = 0x0100
AI_ADDRCONFIG = 0x0400
AI_V4MAPPED = 0x0800
AI_NON_AUTHORITATIVE = 0x04000
AI_SECURE = 0x08000
AI_RETURN_PREFERRED_NAMES = 0x010000
AI_FQDN = 0x00020000
AI_FILESERVER = 0x00040000
AI_NUMERICSERV = 0x00000008

struct Sockaddr
sa_family : UInt8
sa_data : Char[14]
end

struct Addrinfo
ai_flags : Int
ai_family : Int
ai_socktype : Int
ai_protocol : Int
ai_addrlen : SizeT
ai_canonname : Char*
ai_addr : Sockaddr*
ai_next : Addrinfo*
end
end
13 changes: 13 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ require "./winsock2"
require "./ws2ipdef"

lib LibC
EAI_AGAIN = WinError::WSATRY_AGAIN
EAI_BADFLAGS = WinError::WSAEINVAL
EAI_FAIL = WinError::WSANO_RECOVERY
EAI_FAMILY = WinError::WSAEAFNOSUPPORT
EAI_MEMORY = WinError::WSA_NOT_ENOUGH_MEMORY
EAI_NOSECURENAME = WinError::WSA_SECURE_HOST_NOT_FOUND
EAI_NONAME = WinError::WSAHOST_NOT_FOUND
EAI_SERVICE = WinError::WSATYPE_NOT_FOUND
EAI_SOCKTYPE = WinError::WSAESOCKTNOSUPPORT
EAI_IPSECPOLICY = WinError::WSA_IPSEC_NAME_POLICY_ERROR

fun freeaddrinfo(pAddrInfo : Addrinfo*) : Void
fun getaddrinfo(pNodeName : Char*, pServiceName : Char*, pHints : Addrinfo*, ppResult : Addrinfo**) : Int
fun inet_ntop(family : Int, pAddr : Void*, pStringBuf : Char*, stringBufSize : SizeT) : Char*
fun inet_pton(family : Int, pszAddrString : Char*, pAddrBuf : Void*) : Int
end
7 changes: 0 additions & 7 deletions src/socket.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ class Socket < IO
include IO::Buffered
include IO::Evented

enum Type
STREAM = LibC::SOCK_STREAM
DGRAM = LibC::SOCK_DGRAM
RAW = LibC::SOCK_RAW
SEQPACKET = LibC::SOCK_SEQPACKET
end

# :nodoc:
SOMAXCONN = 128

Expand Down
13 changes: 12 additions & 1 deletion src/socket/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "uri/punycode"
require "./address"

class Socket
# Domain name resolver.
Expand Down Expand Up @@ -83,12 +84,22 @@ class Socket
getter error_code : Int32

def self.new(error_code, domain)
new error_code, String.new(LibC.gai_strerror(error_code)), domain
new error_code, error_string(error_code), domain
end

def initialize(@error_code, message, domain)
super("Hostname lookup for #{domain} failed: #{message}")
end

def self.error_string(error_code)
{% if flag?(:win32) %}
# gai_strerror is defined as a macro in WS2tcpip.h, we can just use
# WinError for this
return WinError.new(error_code.to_u32).message
{% else %}
String.new(LibC.gai_strerror(error_code))
{% end %}
end
end

private def self.getaddrinfo(domain, service, family, type, protocol, timeout)
Expand Down
22 changes: 22 additions & 0 deletions src/socket/common.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@
{% end %}

class Socket
{% if flag?(:win32) %}
begin
# Initialize Windows Socket API and expect version 2.2
wsa_version = 0x202
err = LibC.WSAStartup(wsa_version, out wsadata)
unless err.zero?
raise IO::Error.from_winerror("WSAStartup", WinError.new(err.to_u32))
end

if wsadata.wVersion != wsa_version
raise IO::Error.new("Unsuitable version of the Windows Socket API: 0x#{wsadata.wVersion.to_s(16)}")
end
end
{% end %}

enum Protocol
IP = LibC::IPPROTO_IP
TCP = LibC::IPPROTO_TCP
Expand All @@ -30,6 +45,13 @@ class Socket
INET6 = LibC::AF_INET6
end

enum Type
STREAM = LibC::SOCK_STREAM
DGRAM = LibC::SOCK_DGRAM
RAW = LibC::SOCK_RAW
SEQPACKET = LibC::SOCK_SEQPACKET
end

class Error < IO::Error
private def self.new_from_errno(message, errno, **opts)
case errno
Expand Down

0 comments on commit 9cb47a7

Please sign in to comment.