Commit 58fb5b29b6

Jonathan Marler <johnnymarler@gmail.com>
2020-05-31 18:07:51
more windows network fixes
* support posix SOCK_NONBLOCK and SOCK_CLOEXEC flags on windows * fix bugs in os.socket and os.connect to return at the correct place
1 parent 2bae91e
Changed files (4)
lib
lib/std/os/bits/windows.zig
@@ -221,6 +221,15 @@ pub const SOCK_RAW = ws2_32.SOCK_RAW;
 pub const SOCK_RDM = ws2_32.SOCK_RDM;
 pub const SOCK_SEQPACKET = ws2_32.SOCK_SEQPACKET;
 
+/// WARNING: this flag is not supported by windows socket functions directly,
+///          it is only supported by std.os.socket. Be sure that this value does
+///          not share any bits with any of the SOCK_* values.
+pub const SOCK_CLOEXEC = 0x10000;
+/// WARNING: this flag is not supported by windows socket functions directly,
+///          it is only supported by std.os.socket. Be sure that this value does
+///          not share any bits with any of the SOCK_* values.
+pub const SOCK_NONBLOCK = 0x20000;
+
 pub const IPPROTO_ICMP = ws2_32.IPPROTO_ICMP;
 pub const IPPROTO_IGMP = ws2_32.IPPROTO_IGMP;
 pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM;
lib/std/os/windows/ws2_32.zig
@@ -174,6 +174,8 @@ pub const AI_SECURE                 = 0x08000;
 pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
 pub const AI_DISABLE_IDN_ENCODING   = 0x80000;
 
+pub const FIONBIO = -2147195266;
+
 pub const sockaddr = extern struct {
     family: ADDRESS_FAMILY,
     data: [14]u8,
@@ -724,11 +726,6 @@ pub extern "ws2_32" fn WSAIoctl(
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
 ) callconv(.Stdcall) c_int;
-pub extern "ws2_32" fn socket(
-    af: c_int,
-    type: c_int,
-    protocol: c_int,
-) callconv(.Stdcall) SOCKET;
 pub extern "ws2_32" fn accept(
     s: SOCKET,
     addr: ?*sockaddr,
@@ -788,3 +785,8 @@ pub extern "ws2_32" fn getaddrinfo(
 pub extern "ws2_32" fn freeaddrinfo(
     pAddrInfo: *addrinfo,
 ) callconv(.Stdcall) void;
+pub extern "ws2_32" fn ioctlsocket(
+    s: SOCKET,
+    cmd: c_long,
+    argp: *c_ulong,
+) callconv(.Stdcall) c_int;
lib/std/net.zig
@@ -452,37 +452,33 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
         };
         var res: *os.addrinfo = undefined;
         const rc = sys.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res);
-        if (builtin.os.tag == .windows) {
-            const ws2_32 = os.windows.ws2_32;
-            if (rc != 0) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
-                .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
-                .WSANO_RECOVERY => return error.NameServerFailure,
-                .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
-                .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
-                .WSAHOST_NOT_FOUND => return error.UnknownHostName,
-                .WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
-                .WSAEINVAL => unreachable,
-                .WSAESOCKTNOSUPPORT => unreachable,
-                else => |err| return os.windows.unexpectedWSAError(err),
-            };
-        } else {
-            switch (rc) {
-                @intToEnum(sys.EAI, 0) => {},
-                .ADDRFAMILY => return error.HostLacksNetworkAddresses,
-                .AGAIN => return error.TemporaryNameServerFailure,
-                .BADFLAGS => unreachable, // Invalid hints
-                .FAIL => return error.NameServerFailure,
-                .FAMILY => return error.AddressFamilyNotSupported,
-                .MEMORY => return error.OutOfMemory,
-                .NODATA => return error.HostLacksNetworkAddresses,
-                .NONAME => return error.UnknownHostName,
-                .SERVICE => return error.ServiceUnavailable,
-                .SOCKTYPE => unreachable, // Invalid socket type requested in hints
-                .SYSTEM => switch (os.errno(-1)) {
-                    else => |e| return os.unexpectedErrno(e),
-                },
-                else => unreachable,
-            }
+        if (builtin.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
+            @intToEnum(os.windows.ws2_32.WinsockError, 0) => {},
+            .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
+            .WSANO_RECOVERY => return error.NameServerFailure,
+            .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
+            .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
+            .WSAHOST_NOT_FOUND => return error.UnknownHostName,
+            .WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
+            .WSAEINVAL => unreachable,
+            .WSAESOCKTNOSUPPORT => unreachable,
+            else => |err| return os.windows.unexpectedWSAError(err),
+        } else switch (rc) {
+            @intToEnum(sys.EAI, 0) => {},
+            .ADDRFAMILY => return error.HostLacksNetworkAddresses,
+            .AGAIN => return error.TemporaryNameServerFailure,
+            .BADFLAGS => unreachable, // Invalid hints
+            .FAIL => return error.NameServerFailure,
+            .FAMILY => return error.AddressFamilyNotSupported,
+            .MEMORY => return error.OutOfMemory,
+            .NODATA => return error.HostLacksNetworkAddresses,
+            .NONAME => return error.UnknownHostName,
+            .SERVICE => return error.ServiceUnavailable,
+            .SOCKTYPE => unreachable, // Invalid socket type requested in hints
+            .SYSTEM => switch (os.errno(-1)) {
+                else => |e| return os.unexpectedErrno(e),
+            },
+            else => unreachable,
         }
         defer sys.freeaddrinfo(res);
 
lib/std/os.zig
@@ -2447,17 +2447,29 @@ pub const SocketError = error{
 
 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
     if (builtin.os.tag == .windows) {
-        // NOTE: cannot remove SOCK_NONBLOCK and SOCK_CLOEXEC from socket_type because
-        //       windows does not define this flags yet
-        const rc = windows.ws2_32.socket(@intCast(c_int, domain), @intCast(c_int, socket_type), @intCast(c_int, protocol));
-        if (rc != windows.ws2_32.INVALID_SOCKET) return rc;
-        switch (windows.ws2_32.WSAGetLastError()) {
+        // NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations
+        const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
+        const flags : u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0;
+        const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type),
+            @intCast(c_int, protocol), null, 0, flags);
+        if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) {
             .WSAEMFILE => return error.ProcessFdQuotaExceeded,
             .WSAENOBUFS => return error.SystemResources,
             .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
             .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
             else => |err| return windows.unexpectedWSAError(err),
+        };
+        errdefer windows.closesocket(rc) catch unreachable;
+        if ((socket_type & SOCK_NONBLOCK) != 0) {
+            var mode : c_ulong = 1; // nonblocking
+            if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
+                switch (windows.ws2_32.WSAGetLastError()) {
+                    // have not identified any error codes that should be handled yet
+                    else => unreachable,
+                }
+            }
         }
+        return rc;
     }
 
     const have_sock_flags = comptime !std.Target.current.isDarwin();
@@ -2855,6 +2867,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
             .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
             else => |err| return windows.unexpectedWSAError(err),
         }
+        return;
     }
 
     while (true) {