Commit d80554cedf

Bas van den Berg <BarabasGitHub@users.noreply.github.com>
2020-09-02 23:53:29
Made some changes and additions to the networking to make it work on windows.
1 parent 12ce6eb
Changed files (4)
lib/std/os/bits/windows.zig
@@ -172,7 +172,7 @@ pub const AT_REMOVEDIR = 0x200;
 
 pub const in_port_t = u16;
 pub const sa_family_t = ws2_32.ADDRESS_FAMILY;
-pub const socklen_t = u32;
+pub const socklen_t = ws2_32.socklen_t;
 
 pub const sockaddr = ws2_32.sockaddr;
 pub const sockaddr_in = ws2_32.sockaddr_in;
lib/std/os/windows/ws2_32.zig
@@ -116,7 +116,7 @@ pub const WSAOVERLAPPED_COMPLETION_ROUTINE = fn (dwError: DWORD, cbTransferred:
 pub const ADDRESS_FAMILY = u16;
 
 // Microsoft use the signed c_int for this, but it should never be negative
-const socklen_t = u32;
+pub const socklen_t = u31;
 
 pub const AF_UNSPEC = 0;
 pub const AF_UNIX = 1;
@@ -734,12 +734,21 @@ pub extern "ws2_32" fn WSAIoctl(
 pub extern "ws2_32" fn accept(
     s: SOCKET,
     addr: ?*sockaddr,
-    addrlen: socklen_t,
+    addrlen: ?*c_int,
 ) callconv(.Stdcall) SOCKET;
+pub extern "ws2_32" fn bind(
+    s: SOCKET,
+    addr: ?*const sockaddr,
+    addrlen: c_int,
+) callconv(.Stdcall) c_int;
 pub extern "ws2_32" fn connect(
     s: SOCKET,
     name: *const sockaddr,
-    namelen: socklen_t,
+    namelen: c_int,
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn listen(
+    s: SOCKET,
+    backlog: c_int,
 ) callconv(.Stdcall) c_int;
 pub extern "ws2_32" fn WSARecv(
     s: SOCKET,
@@ -795,3 +804,8 @@ pub extern "ws2_32" fn ioctlsocket(
     cmd: c_long,
     argp: *c_ulong,
 ) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn getsockname(
+    s: SOCKET,
+    name: *sockaddr,
+    namelen: *c_int,
+) callconv(.Stdcall) c_int;
lib/std/os/windows.zig
@@ -1153,6 +1153,14 @@ pub fn WSASocketW(
     return rc;
 }
 
+pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
+    return ws2_32.bind(s, name, namelen);
+}
+
+pub fn listen(s: ws2_32.SOCKET, backlog: u31) i32 {
+    return ws2_32.listen(s, backlog);
+}
+
 pub fn closesocket(s: ws2_32.SOCKET) !void {
     switch (ws2_32.closesocket(s)) {
         0 => {},
@@ -1163,6 +1171,25 @@ pub fn closesocket(s: ws2_32.SOCKET) !void {
     }
 }
 
+pub fn accept(s: ws2_32.SOCKET, name: ?*ws2_32.sockaddr, namelen: ?*ws2_32.socklen_t) ws2_32.SOCKET {
+    assert((name == null) == (namelen == null));
+    if (namelen) |name_length| {
+        var os_namelen: c_int = name_length.*;
+        const sock = ws2_32.accept(s, name, &os_namelen);
+        name_length.* = @intCast(ws2_32.socklen_t, os_namelen);
+        return sock;
+    } else {
+        return ws2_32.accept(s, null, null);
+    }
+}
+
+pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
+    var os_namelen: c_int = namelen.*;
+    const rc = ws2_32.getsockname(s, name, &os_namelen);
+    namelen.* = @intCast(ws2_32.socklen_t, os_namelen);
+    return rc;
+}
+
 pub fn WSAIoctl(
     s: ws2_32.SOCKET,
     dwIoControlCode: DWORD,
lib/std/os.zig
@@ -2657,14 +2657,7 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t
         // 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),
-        };
+        const rc = try windows.WSASocketW(@bitCast(i32, domain), @bitCast(i32, filtered_sock_type), @bitCast(i32, protocol), null, 0, flags);
         errdefer windows.closesocket(rc) catch unreachable;
         if ((socket_type & SOCK_NONBLOCK) != 0) {
             var mode: c_ulong = 1; // nonblocking
@@ -2741,24 +2734,34 @@ pub const BindError = error{
 } || UnexpectedError;
 
 /// addr is `*const T` where T is one of the sockaddr
-pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
-    const rc = system.bind(sockfd, addr, len);
-    switch (errno(rc)) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EADDRINUSE => return error.AddressInUse,
-        EBADF => unreachable, // always a race condition if this error is returned
-        EINVAL => unreachable, // invalid parameters
-        ENOTSOCK => unreachable, // invalid `sockfd`
-        EADDRNOTAVAIL => return error.AddressNotAvailable,
-        EFAULT => unreachable, // invalid `addr` pointer
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOTDIR => return error.NotDir,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
+pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
+    const rc = system.bind(sock, addr, len);
+    if (builtin.os.tag == .windows) {
+        if (rc == windows.ws2_32.SOCKET_ERROR) {
+            switch (windows.ws2_32.WSAGetLastError()) {
+                // TODO: handle errors
+                else => |err| return windows.unexpectedWSAError(err),
+            }
+        }
+        return;
+    } else {
+        switch (errno(rc)) {
+            0 => return,
+            EACCES => return error.AccessDenied,
+            EADDRINUSE => return error.AddressInUse,
+            EBADF => unreachable, // always a race condition if this error is returned
+            EINVAL => unreachable, // invalid parameters
+            ENOTSOCK => unreachable, // invalid `sockfd`
+            EADDRNOTAVAIL => return error.AddressNotAvailable,
+            EFAULT => unreachable, // invalid `addr` pointer
+            ELOOP => return error.SymLinkLoop,
+            ENAMETOOLONG => return error.NameTooLong,
+            ENOENT => return error.FileNotFound,
+            ENOMEM => return error.SystemResources,
+            ENOTDIR => return error.NotDir,
+            EROFS => return error.ReadOnlyFileSystem,
+            else => |err| return unexpectedErrno(err),
+        }
     }
 }
 
@@ -2777,15 +2780,25 @@ const ListenError = error{
     OperationNotSupported,
 } || UnexpectedError;
 
-pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void {
-    const rc = system.listen(sockfd, backlog);
-    switch (errno(rc)) {
-        0 => return,
-        EADDRINUSE => return error.AddressInUse,
-        EBADF => unreachable,
-        ENOTSOCK => return error.FileDescriptorNotASocket,
-        EOPNOTSUPP => return error.OperationNotSupported,
-        else => |err| return unexpectedErrno(err),
+pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
+    const rc = system.listen(sock, backlog);
+    if (builtin.os.tag == .windows) {
+        if (rc == windows.ws2_32.SOCKET_ERROR) {
+            switch (windows.ws2_32.WSAGetLastError()) {
+                // TODO: handle errors
+                else => |err| return windows.unexpectedWSAError(err),
+            }
+        }
+        return;
+    } else {
+        switch (errno(rc)) {
+            0 => return,
+            EADDRINUSE => return error.AddressInUse,
+            EBADF => unreachable,
+            ENOTSOCK => return error.FileDescriptorNotASocket,
+            EOPNOTSUPP => return error.OperationNotSupported,
+            else => |err| return unexpectedErrno(err),
+        }
     }
 }
 
@@ -2822,19 +2835,19 @@ pub const AcceptError = error{
 pub fn accept(
     /// This argument is a socket that has been created with `socket`, bound to a local address
     /// with `bind`, and is listening for connections after a `listen`.
-    sockfd: fd_t,
+    sock: socket_t,
     /// This argument is a pointer to a sockaddr structure.  This structure is filled in with  the
     /// address  of  the  peer  socket, as known to the communications layer.  The exact format of the
     /// address returned addr is determined by the socket's address  family  (see  `socket`  and  the
     /// respective  protocol  man  pages).
-    addr: *sockaddr,
+    addr: ?*sockaddr,
     /// This argument is a value-result argument: the caller must initialize it to contain  the
     /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
     /// of the peer address.
     ///
     /// The returned address is truncated if the buffer provided is too small; in this  case,  `addr_size`
     /// will return a value greater than was supplied to the call.
-    addr_size: *socklen_t,
+    addr_size: ?*socklen_t,
     /// The following values can be bitwise ORed in flags to obtain different behavior:
     /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
     ///   referred  to by the new file descriptor.  Using this flag saves extra calls to `fcntl` to achieve
@@ -2842,46 +2855,58 @@ pub fn accept(
     /// * `SOCK_CLOEXEC`  - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.   See  the
     ///   description  of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
     flags: u32,
-) AcceptError!fd_t {
-    const have_accept4 = comptime !std.Target.current.isDarwin();
+) AcceptError!socket_t {
+    const have_accept4 = comptime !(std.Target.current.isDarwin() or builtin.os.tag == .windows);
     assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s)
 
-    while (true) {
+    const accepted_sock = while (true) {
         const rc = if (have_accept4)
-            system.accept4(sockfd, addr, addr_size, flags)
+            system.accept4(sock, addr, addr_size, flags)
         else
-            system.accept(sockfd, addr, addr_size);
+            system.accept(sock, addr, addr_size);
 
-        switch (errno(rc)) {
-            0 => {
-                const fd = @intCast(fd_t, rc);
-                if (!have_accept4) {
-                    try setSockFlags(fd, flags);
+        if (builtin.os.tag == .windows) {
+            if (rc == windows.ws2_32.INVALID_SOCKET) {
+                switch (windows.ws2_32.WSAGetLastError()) {
+                    // TODO: handle errors
+                    else => |err| return windows.unexpectedWSAError(err),
                 }
-                return fd;
-            },
-            EINTR => continue,
-            EAGAIN => if (std.event.Loop.instance) |loop| {
-                loop.waitUntilFdReadable(sockfd);
-                continue;
             } else {
-                return error.WouldBlock;
-            },
-            EBADF => unreachable, // always a race condition
-            ECONNABORTED => return error.ConnectionAborted,
-            EFAULT => unreachable,
-            EINVAL => unreachable,
-            ENOTSOCK => unreachable,
-            EMFILE => return error.ProcessFdQuotaExceeded,
-            ENFILE => return error.SystemFdQuotaExceeded,
-            ENOBUFS => return error.SystemResources,
-            ENOMEM => return error.SystemResources,
-            EOPNOTSUPP => unreachable,
-            EPROTO => return error.ProtocolFailure,
-            EPERM => return error.BlockedByFirewall,
-            else => |err| return unexpectedErrno(err),
+                break rc;
+            }
+        } else {
+            switch (errno(rc)) {
+                0 => {
+                    break @intCast(socket_t, rc);
+                },
+                EINTR => continue,
+                EAGAIN => if (std.event.Loop.instance) |loop| {
+                    loop.waitUntilFdReadable(sock);
+                    continue;
+                } else {
+                    return error.WouldBlock;
+                },
+                EBADF => unreachable, // always a race condition
+                ECONNABORTED => return error.ConnectionAborted,
+                EFAULT => unreachable,
+                EINVAL => unreachable,
+                ENOTSOCK => unreachable,
+                EMFILE => return error.ProcessFdQuotaExceeded,
+                ENFILE => return error.SystemFdQuotaExceeded,
+                ENOBUFS => return error.SystemResources,
+                ENOMEM => return error.SystemResources,
+                EOPNOTSUPP => unreachable,
+                EPROTO => return error.ProtocolFailure,
+                EPERM => return error.BlockedByFirewall,
+                else => |err| return unexpectedErrno(err),
+            }
         }
+    } else unreachable;
+
+    if (!have_accept4) {
+        try setSockFlags(accepted_sock, flags);
     }
+    return accepted_sock;
 }
 
 pub const EpollCreateError = error{
@@ -2997,16 +3022,27 @@ pub const GetSockNameError = error{
     SystemResources,
 } || UnexpectedError;
 
-pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
-    switch (errno(system.getsockname(sockfd, addr, addrlen))) {
-        0 => return,
-        else => |err| return unexpectedErrno(err),
+pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
+    const rc = system.getsockname(sock, addr, addrlen);
+    if (builtin.os.tag == .windows) {
+        if (rc == windows.ws2_32.SOCKET_ERROR) {
+            switch (windows.ws2_32.WSAGetLastError()) {
+                // TODO: handle errors
+                else => |err| return windows.unexpectedWSAError(err),
+            }
+        }
+        return;
+    } else {
+        switch (errno(rc)) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
 
-        EBADF => unreachable, // always a race condition
-        EFAULT => unreachable,
-        EINVAL => unreachable, // invalid parameters
-        ENOTSOCK => unreachable,
-        ENOBUFS => return error.SystemResources,
+            EBADF => unreachable, // always a race condition
+            EFAULT => unreachable,
+            EINVAL => unreachable, // invalid parameters
+            ENOTSOCK => unreachable,
+            ENOBUFS => return error.SystemResources,
+        }
     }
 }
 
@@ -3052,9 +3088,9 @@ pub const ConnectError = error{
 } || UnexpectedError;
 
 /// Initiate a connection on a socket.
-pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
+pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
     if (builtin.os.tag == .windows) {
-        const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
+        const rc = windows.ws2_32.connect(sock, sock_addr, len);
         if (rc == 0) return;
         switch (windows.ws2_32.WSAGetLastError()) {
             .WSAEADDRINUSE => return error.AddressInUse,
@@ -3077,7 +3113,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
     }
 
     while (true) {
-        switch (errno(system.connect(sockfd, sock_addr, len))) {
+        switch (errno(system.connect(sock, sock_addr, len))) {
             0 => return,
             EACCES => return error.PermissionDenied,
             EPERM => return error.PermissionDenied,
@@ -3086,8 +3122,8 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
             EAFNOSUPPORT => return error.AddressFamilyNotSupported,
             EAGAIN, EINPROGRESS => {
                 const loop = std.event.Loop.instance orelse return error.WouldBlock;
-                loop.waitUntilFdWritable(sockfd);
-                return getsockoptError(sockfd);
+                loop.waitUntilFdWritable(sock);
+                return getsockoptError(sock);
             },
             EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
             EBADF => unreachable, // sockfd is not a valid open file descriptor.
@@ -3928,32 +3964,46 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
     }
 }
 
-fn setSockFlags(fd: fd_t, flags: u32) !void {
+fn setSockFlags(sock: socket_t, flags: u32) !void {
     if ((flags & SOCK_CLOEXEC) != 0) {
-        var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) {
-            error.FileBusy => unreachable,
-            error.Locked => unreachable,
-            else => |e| return e,
-        };
-        fd_flags |= FD_CLOEXEC;
-        _ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) {
-            error.FileBusy => unreachable,
-            error.Locked => unreachable,
-            else => |e| return e,
-        };
+        if (builtin.os.tag == .windows) {
+            // TODO: Find out if this is supported for sockets
+        } else {
+            var fd_flags = fcntl(sock, F_GETFD, 0) catch |err| switch (err) {
+                error.FileBusy => unreachable,
+                error.Locked => unreachable,
+                else => |e| return e,
+            };
+            fd_flags |= FD_CLOEXEC;
+            _ = fcntl(sock, F_SETFD, fd_flags) catch |err| switch (err) {
+                error.FileBusy => unreachable,
+                error.Locked => unreachable,
+                else => |e| return e,
+            };
+        }
     }
     if ((flags & SOCK_NONBLOCK) != 0) {
-        var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) {
-            error.FileBusy => unreachable,
-            error.Locked => unreachable,
-            else => |e| return e,
-        };
-        fl_flags |= O_NONBLOCK;
-        _ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) {
-            error.FileBusy => unreachable,
-            error.Locked => unreachable,
-            else => |e| return e,
-        };
+        if (builtin.os.tag == .windows) {
+            var mode: c_ulong = 1;
+            if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
+                switch (windows.ws2_32.WSAGetLastError()) {
+                    // TODO: handle errors
+                    else => |err| return windows.unexpectedWSAError(err),
+                }
+            }
+        } else {
+            var fl_flags = fcntl(sock, F_GETFL, 0) catch |err| switch (err) {
+                error.FileBusy => unreachable,
+                error.Locked => unreachable,
+                else => |e| return e,
+            };
+            fl_flags |= O_NONBLOCK;
+            _ = fcntl(sock, F_SETFL, fl_flags) catch |err| switch (err) {
+                error.FileBusy => unreachable,
+                error.Locked => unreachable,
+                else => |e| return e,
+            };
+        }
     }
 }