diff --git a/spec/std/socket/addrinfo_spec.cr b/spec/std/socket/addrinfo_spec.cr index 69f062cd517e..4383fe0439b3 100644 --- a/spec/std/socket/addrinfo_spec.cr +++ b/spec/std/socket/addrinfo_spec.cr @@ -1,5 +1,5 @@ require "spec" -require "socket" +require "socket/addrinfo" describe Socket::Addrinfo do describe ".resolve" do diff --git a/spec/win32_std_spec.cr b/spec/win32_std_spec.cr index 8afb7bf70435..217fd50ee684 100644 --- a/spec/win32_std_spec.cr +++ b/spec/win32_std_spec.cr @@ -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) diff --git a/src/lib_c/x86_64-windows-msvc/c/winbase.cr b/src/lib_c/x86_64-windows-msvc/c/winbase.cr index 2c0b4188a7e0..d33b3c162098 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winbase.cr @@ -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 diff --git a/src/lib_c/x86_64-windows-msvc/c/winsock2.cr b/src/lib_c/x86_64-windows-msvc/c/winsock2.cr index 7d40c83d719f..dc87e85109d9 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winsock2.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winsock2.cr @@ -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 diff --git a/src/lib_c/x86_64-windows-msvc/c/ws2def.cr b/src/lib_c/x86_64-windows-msvc/c/ws2def.cr index 30021ae63909..28d56bdf93ae 100644 --- a/src/lib_c/x86_64-windows-msvc/c/ws2def.cr +++ b/src/lib_c/x86_64-windows-msvc/c/ws2def.cr @@ -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 diff --git a/src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr b/src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr index 663fb70f2454..338063ccf6f6 100644 --- a/src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr +++ b/src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr @@ -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 diff --git a/src/socket.cr b/src/socket.cr index 3bbb38910cda..990970010839 100644 --- a/src/socket.cr +++ b/src/socket.cr @@ -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 diff --git a/src/socket/addrinfo.cr b/src/socket/addrinfo.cr index 9885a9b48f3d..c3deff908561 100644 --- a/src/socket/addrinfo.cr +++ b/src/socket/addrinfo.cr @@ -1,4 +1,5 @@ require "uri/punycode" +require "./address" class Socket # Domain name resolver. @@ -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) diff --git a/src/socket/common.cr b/src/socket/common.cr index 2bf555a897d9..1094f01c03c9 100644 --- a/src/socket/common.cr +++ b/src/socket/common.cr @@ -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 @@ -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