diff --git a/src/errno.cr b/src/errno.cr index dcb2b34f2b6d..dcfa435c6c9b 100644 --- a/src/errno.cr +++ b/src/errno.cr @@ -30,7 +30,7 @@ enum Errno ECANCELED EIDRM ENOMSG EILSEQ EBADMSG EMULTIHOP ENODATA ENOLINK ENOSR ENOSTR EPROTO ETIME EOPNOTSUPP ENOTRECOVERABLE EOWNERDEAD - WSABASEERR WSAEINPROGRESS WSAEINTR) %} + WSABASEERR WSAEINPROGRESS WSAEINTR WSAENOPROTOOPT) %} {% if LibC.has_constant?(value) %} {{value.id}} = LibC::{{value.id}} {% end %} diff --git a/src/lib_c/x86_64-windows-msvc/c/errno.cr b/src/lib_c/x86_64-windows-msvc/c/errno.cr index fea8e80dabfe..d225acc552c8 100644 --- a/src/lib_c/x86_64-windows-msvc/c/errno.cr +++ b/src/lib_c/x86_64-windows-msvc/c/errno.cr @@ -42,20 +42,21 @@ lib LibC # source https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 WSAECONNABORTED = 10053 - ECONNABORTED = 10053 - WSAECONNRESET = 10054 - ECONNRESET = 10054 + ECONNABORTED = 10053 + WSAECONNRESET = 10054 + ECONNRESET = 10054 WSAECONNREFUSED = 10061 - ECONNREFUSED = 10061 - WSAEADDRINUSE = 10048 - EADDRINUSE = 10048 + ECONNREFUSED = 10061 + WSAEADDRINUSE = 10048 + EADDRINUSE = 10048 - WSABASEERR = 10000 + WSABASEERR = 10000 WSAEINPROGRESS = WSABASEERR + 36 - WSAEINTR = WSABASEERR + 4 + WSAEINTR = WSABASEERR + 4 + ENOPROTOOPT = 10042 - EISCONN = 106 - EALREADY = 114 + EISCONN = 106 + EALREADY = 114 EINPROGRESS = 115 alias ErrnoT = Int diff --git a/src/lib_c/x86_64-windows-msvc/c/netdb.cr b/src/lib_c/x86_64-windows-msvc/c/netdb.cr index 5f957b7a94d3..075afc8f7b3d 100644 --- a/src/lib_c/x86_64-windows-msvc/c/netdb.cr +++ b/src/lib_c/x86_64-windows-msvc/c/netdb.cr @@ -4,69 +4,69 @@ require "./sys/socket" lib LibC - 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_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 # lin AI_NUMERICSERV = 0x0400 # move to sys/socket - AF_UNSPEC = 0 - AF_INET = 2 - AF_IPX = 6 + AF_UNSPEC = 0 + AF_INET = 2 + AF_IPX = 6 AF_APPLETALK = 16 - AF_NETBIOS = 17 - AF_INET6 = 23 - AF_IRDA = 26 - AF_BTH = 32 - + AF_NETBIOS = 17 + AF_INET6 = 23 + AF_IRDA = 26 + AF_BTH = 32 + # lin - PF_INET = 2 - PF_INET6 = 10 - PF_UNIX = LibC::PF_LOCAL - PF_UNSPEC = 0 - PF_LOCAL = 1 - AF_UNIX = LibC::PF_UNIX + PF_INET = 2 + PF_INET6 = 10 + PF_UNIX = LibC::PF_LOCAL + PF_UNSPEC = 0 + PF_LOCAL = 1 + AF_UNIX = LibC::PF_UNIX - ## lin - EAI_AGAIN = -3 - EAI_BADFLAGS = -1 - EAI_FAIL = -4 - EAI_FAMILY = -6 - EAI_MEMORY = -10 - EAI_NONAME = -2 - EAI_SERVICE = -8 - EAI_SOCKTYPE = -7 - EAI_SYSTEM = -11 - EAI_OVERFLOW = -12 + # # lin + EAI_AGAIN = -3 + EAI_BADFLAGS = -1 + EAI_FAIL = -4 + EAI_FAMILY = -6 + EAI_MEMORY = -10 + EAI_NONAME = -2 + EAI_SERVICE = -8 + EAI_SOCKTYPE = -7 + EAI_SYSTEM = -11 + EAI_OVERFLOW = -12 # move to sys/socket - SOCK_STREAM = 1 - SOCK_DGRAM = 2 - SOCK_RAW = 3 - SOCK_RDM = 4 + SOCK_STREAM = 1 + SOCK_DGRAM = 2 + SOCK_RAW = 3 + SOCK_RDM = 4 SOCK_SEQPACKET = 5 # move to netinet/in - IPPROTO_TCP = 6 - IPPROTO_UDP = 17 - IPPROTO_RM = 113 - IPPROTO_IGMP = 2 + IPPROTO_TCP = 6 + IPPROTO_UDP = 17 + IPPROTO_RM = 113 + IPPROTO_IGMP = 2 # ipcp - BTHPROTO_RFCOMM = 3 - IPPROTO_ICMPV6 = 58 + BTHPROTO_RFCOMM = 3 + IPPROTO_ICMPV6 = 58 - ## lin - IPPROTO_IP = 0 + # # lin + IPPROTO_IP = 0 IPPROTO_RAW = 255 IPPROTO_ICMP = 1 @@ -87,7 +87,7 @@ lib LibC fun freeaddrinfo(pAddrInfo : PADDRINFOA) : VOID fun getaddrinfo(pNodeName : PCSTR, pServiceName : PCSTR, pHints : ADDRINFOA*, ppResult : PADDRINFOA*) : INT fun getnameinfo(pSockaddr : SOCKADDR*, sockaddrLength : SocklenT, pNodeBuffer : PCHAR, nodeBufferSize : DWORD, pServiceBuffer : PCHAR, serviceBufferSize : DWORD, flags : INT) : INT - + # fun gai_strerror = gai_strerrorA(ecode : Int) : UInt8* # See src/socket/addrinfo.cr for `gai_strerrorA` function definition end diff --git a/src/lib_c/x86_64-windows-msvc/c/sys/socket.cr b/src/lib_c/x86_64-windows-msvc/c/sys/socket.cr index 895bb024a165..d1dc6ac6a744 100644 --- a/src/lib_c/x86_64-windows-msvc/c/sys/socket.cr +++ b/src/lib_c/x86_64-windows-msvc/c/sys/socket.cr @@ -1,4 +1,5 @@ require "./types" +require "./un" @[Link("WS2_32")] lib LibC @@ -8,7 +9,7 @@ lib LibC SO_REUSEADDR = 0x0004 SO_BROADCAST = 0x0020 - SOL_SOCKET = 0xFFFF + SOL_SOCKET = 0xFFFF # -2147195266 is the value after convertion to long, actual value 2147772030 with type unsigned FIONBIO = -2147195266 @@ -20,6 +21,26 @@ lib LibC alias SOCKADDR = Sockaddr + struct WSAData + vVersion : WORD + wHighVersion : WORD + szDescription : StaticArray(UInt8, 257) + szSystemStatus : StaticArray(UInt8, 129) + iMaxSockets : UInt16 + iMaxUdpDg : UInt16 + lpVendorInfo : UInt8* + end + + struct SockaddrStorage + ss_family : Short + __ss_pad1 : StaticArray(Char, 6) + __ss_align : Int64 + __ss_pad2 : StaticArray(Char, 112) + end + + alias LPWSADATA = WSAData + + fun wsastartup = WSAStartup(wVersionRequired : WORD, lpWSAData : LPWSADATA) : Int fun socket(af : Int, type : Int, protocol : Int) : SOCKET fun bind(s : SOCKET, addr : Sockaddr*, namelen : Int) : Int fun closesocket(s : SOCKET) : Int @@ -34,4 +55,25 @@ lib LibC fun connect(s : SOCKET, name : Sockaddr*, namelen : Int) : Int fun getsockname(s : SOCKET, name : Sockaddr*, namelen : Int*) : Int fun htons(hostshort : UShort) : UShort + fun getsockopt(s : SOCKET, level : Int, optname : Int, optval : UInt8*, optlen : Int*) : Int + fun sendto(s : SOCKET, buf : UInt8*, len : Int, flags : Int, to : Sockaddr*, tolen : Int) : Int + fun recvfrom(s : SOCKET, buf : Char*, len : Int, flags : Int, from : Sockaddr*, fromlen : Int*) : Int + + SO_RCVBUF = 0x1002 + TCP_NODELAY = 0x0001 + TCP_KEEPIDLE = 3 + TCP_KEEPCNT = 16 + TCP_KEEPINTVL = 17 + IP_MULTICAST_LOOP = 11 + IPV6_MULTICAST_LOOP = 11 + IPPROTO_IPV6 = 41 + IP_MULTICAST_TTL = 10 + IP_MULTICAST_IF = 9 + IPV6_MULTICAST_IF = 9 + IPV6_MULTICAST_HOPS = 10 + IP_ADD_MEMBERSHIP = 12 end + +wsadata = uninitialized LibC::WSAData +wsaVersion = 514 +LibC.wsastartup(wsaVersion, wsadata) diff --git a/src/lib_c/x86_64-windows-msvc/c/sys/un.cr b/src/lib_c/x86_64-windows-msvc/c/sys/un.cr index e69de29bb2d1..24703158d583 100644 --- a/src/lib_c/x86_64-windows-msvc/c/sys/un.cr +++ b/src/lib_c/x86_64-windows-msvc/c/sys/un.cr @@ -0,0 +1,6 @@ +lib LibC + # struct IpMreq + # imr_multiaddr : IN_ADDR + # imr_interface : IN_ADDR + # end +end 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 3b6a215497a8..da09c7082453 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winbase.cr @@ -13,7 +13,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 + 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/socket.cr b/src/socket.cr index 934be5abd727..af06126c8b56 100644 --- a/src/socket.cr +++ b/src/socket.cr @@ -395,11 +395,19 @@ class Socket < IO # sock.connect Socket::UNIXAddress.new("/tmp/service.sock") # sock.send(Bytes[0]) # ``` - def send(message) : Int32 - evented_send(message.to_slice, "Error sending datagram") do |slice| - LibC.send(fd, slice.to_unsafe.as(Void*), slice.size, 0) + {% if flag?(:win32) %} + def send(message) : Int32 + evented_send(message.to_slice, "Error sending datagram") do |slice| + LibC.send(socket, slice.to_unsafe.as(UInt8*), slice.size, 0) + end end - end + {% else %} + def send(message) : Int32 + evented_send(message.to_slice, "Error sending datagram") do |slice| + LibC.send(fd, slice.to_unsafe.as(UInt8*), slice.size, 0) + end + end + {% end %} # Sends a message to the specified remote address. # @@ -411,13 +419,23 @@ class Socket < IO # sock.connect("example.com", 2000) # sock.send("text query", to: server) # ``` - def send(message, to addr : Address) : Int32 - slice = message.to_slice - bytes_sent = LibC.sendto(fd, slice.to_unsafe.as(Void*), slice.size, 0, addr, addr.size) - raise Socket::Error.from_errno("Error sending datagram to #{addr}") if bytes_sent == -1 - # to_i32 is fine because string/slice sizes are an Int32 - bytes_sent.to_i32 - end + {% if flag?(:win32) %} + def send(message, to addr : Address) : Int32 + slice = message.to_slice + bytes_sent = LibC.sendto(socket, slice.to_unsafe.as(UInt8*), slice.size, 0, addr, addr.size) + raise Socket::Error.from_errno("Error sending datagram to #{addr}") if bytes_sent == -1 + # to_i32 is fine because string/slice sizes are an Int32 + bytes_sent.to_i32 + end + {% else %} + def send(message, to addr : Address) : Int32 + slice = message.to_slice + bytes_sent = LibC.sendto(fd, slice.to_unsafe.as(Void*), slice.size, 0, addr, addr.size) + raise Socket::Error.from_errno("Error sending datagram to #{addr}") if bytes_sent == -1 + # to_i32 is fine because string/slice sizes are an Int32 + bytes_sent.to_i32 + end + {% end %} # Receives a text message from the previously bound address. # @@ -455,16 +473,29 @@ class Socket < IO {bytes_read, Address.from(sockaddr, addrlen)} end - protected def recvfrom(bytes) - sockaddr = Pointer(LibC::SockaddrStorage).malloc.as(LibC::Sockaddr*) - addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrStorage)) + {% if flag?(:win32) %} + protected def recvfrom(bytes) + sockaddr = Pointer(LibC::SockaddrStorage).malloc.as(LibC::Sockaddr*) + addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrStorage)) + + bytes_read = evented_read(bytes, "Error receiving datagram") do |slice| + LibC.recvfrom(socket, slice.to_unsafe.as(LibC::Char*), slice.size, 0, sockaddr, pointerof(addrlen)) + end - bytes_read = evented_read(bytes, "Error receiving datagram") do |slice| - LibC.recvfrom(fd, slice.to_unsafe.as(Void*), slice.size, 0, sockaddr, pointerof(addrlen)) + {bytes_read, sockaddr, addrlen} end + {% else %} + protected def recvfrom(bytes) + sockaddr = Pointer(LibC::SockaddrStorage).malloc.as(LibC::Sockaddr*) + addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrStorage)) - {bytes_read, sockaddr, addrlen} - end + bytes_read = evented_read(bytes, "Error receiving datagram") do |slice| + LibC.recvfrom(fd, slice.to_unsafe.as(Void*), slice.size, 0, sockaddr, pointerof(addrlen)) + end + + {bytes_read, sockaddr, addrlen} + end + {% end %} # Calls `shutdown(2)` with `SHUT_RD` def close_read @@ -518,17 +549,33 @@ class Socket < IO setsockopt_bool LibC::SO_REUSEADDR, val end - def reuse_port? - getsockopt(LibC::SO_REUSEPORT, 0) do |value| - return value != 0 + {% if flag?(:win32) %} + def reuse_port? + # TODO + # Check function return value + getsockopt(LibC::SO_REUSEADDR, 0) do |value| + return value != 0 + end + + if Errno.value == Errno::ENOPROTOOPT + return false + else + raise Socket::Error.from_errno("getsockopt") + end end + {% else %} + def reuse_port? + getsockopt(LibC::SO_REUSEPORT, 0) do |value| + return value != 0 + end - if Errno.value == Errno::ENOPROTOOPT - return false - else - raise Socket::Error.from_errno("getsockopt") + if Errno.value == Errno::ENOPROTOOPT + return false + else + raise Socket::Error.from_errno("getsockopt") + end end - end + {% end %} # TODO # Care @@ -593,12 +640,21 @@ class Socket < IO raise Socket::Error.from_errno("getsockopt") end - protected def getsockopt(optname, optval, level = LibC::SOL_SOCKET) - optsize = LibC::SocklenT.new(sizeof(typeof(optval))) - ret = LibC.getsockopt(fd, level, optname, (pointerof(optval).as(Void*)), pointerof(optsize)) - yield optval if ret == 0 - ret - end + {% if flag?(:win32) %} + protected def getsockopt(optname, optval, level = LibC::SOL_SOCKET) + optsize = LibC::SocklenT.new(sizeof(typeof(optval))) + ret = LibC.getsockopt(socket, level, optname, (pointerof(optval).as(UInt8*)), pointerof(optsize)) + yield optval if ret == 0 + ret + end + {% else %} + protected def getsockopt(optname, optval, level = LibC::SOL_SOCKET) + optsize = LibC::SocklenT.new(sizeof(typeof(optval))) + ret = LibC.getsockopt(fd, level, optname, (pointerof(optval).as(Void*)), pointerof(optsize)) + yield optval if ret == 0 + ret + end + {% end %} # NOTE: *optval* is restricted to `Int32` until sizeof works on variables. {% if flag?(:win32) %} diff --git a/src/spec/expectations.cr b/src/spec/expectations.cr index 168facbfb42f..bf29e058e13c 100644 --- a/src/spec/expectations.cr +++ b/src/spec/expectations.cr @@ -395,14 +395,12 @@ module Spec when Regex unless (ex_to_s =~ message) backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass} with message matching #{message.inspect}, " \ - "got #<#{ex.class}: #{ex_to_s}> with backtrace:\n#{backtrace}", file, line + fail "Expected #{klass} with message matching #{message.inspect}, got #<#{ex.class}: #{ex_to_s}> with backtrace:\n#{backtrace}", file, line end when String unless ex_to_s.includes?(message) backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass} with #{message.inspect}, got #<#{ex.class}: " \ - "#{ex_to_s}> with backtrace:\n#{backtrace}", file, line + fail "Expected #{klass} with #{message.inspect}, got #<#{ex.class}: #{ex_to_s}> with backtrace:\n#{backtrace}", file, line end when Nil # No need to check the message @@ -411,8 +409,7 @@ module Spec ex rescue ex backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass}, got #<#{ex.class}: #{ex}> with backtrace:\n" \ - "#{backtrace}", file, line + fail "Expected #{klass}, got #<#{ex.class}: #{ex}> with backtrace:\n#{backtrace}", file, line else fail "Expected #{klass} but nothing was raised", file, line end