From d80554cedf313bc78f0419faf84776bd987263df Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Wed, 2 Sep 2020 23:53:29 +0200 Subject: [PATCH 1/7] Made some changes and additions to the networking to make it work on windows. --- lib/std/os.zig | 262 ++++++++++++++++++++-------------- lib/std/os/bits/windows.zig | 2 +- lib/std/os/windows.zig | 27 ++++ lib/std/os/windows/ws2_32.zig | 20 ++- 4 files changed, 201 insertions(+), 110 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index e8431c386b..ee0c8848f3 100644 --- a/lib/std/os.zig +++ b/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, + }; + } } } diff --git a/lib/std/os/bits/windows.zig b/lib/std/os/bits/windows.zig index 836d273c1c..de17fd83fa 100644 --- a/lib/std/os/bits/windows.zig +++ b/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; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index bd9dc8b32e..df15907482 100644 --- a/lib/std/os/windows.zig +++ b/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, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index cfc212d15a..e226dfa967 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/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; From 0a40a61548ad9f666ed5300a8910f9040cc1390b Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Thu, 3 Sep 2020 13:23:48 +0200 Subject: [PATCH 2/7] os.send(to) and os.recv(from) functions made to work on windows. --- lib/std/os.zig | 114 +++++++++++++++++++++------------- lib/std/os/windows.zig | 21 +++++++ lib/std/os/windows/ws2_32.zig | 2 +- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index ee0c8848f3..abf72529ad 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4630,7 +4630,7 @@ pub const SendError = error{ /// possible to send more data. pub fn sendto( /// The file descriptor of the sending socket. - sockfd: fd_t, + sockfd: socket_t, /// Message to send. buf: []const u8, flags: u32, @@ -4639,32 +4639,43 @@ pub fn sendto( ) SendError!usize { while (true) { const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); - switch (errno(rc)) { - 0 => return @intCast(usize, rc), - - EACCES => return error.AccessDenied, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(sockfd); - continue; + 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), + } } else { - return error.WouldBlock; - }, - EALREADY => return error.FastOpenAlreadyInProgress, - EBADF => unreachable, // always a race condition - ECONNRESET => return error.ConnectionResetByPeer, - EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. - EFAULT => unreachable, // An invalid user space address was specified for an argument. - EINTR => continue, - EINVAL => unreachable, // Invalid argument passed. - EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified - EMSGSIZE => return error.MessageTooBig, - ENOBUFS => return error.SystemResources, - ENOMEM => return error.SystemResources, - ENOTCONN => unreachable, // The socket is not connected, and no target has been given. - ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. - EPIPE => return error.BrokenPipe, - else => |err| return unexpectedErrno(err), + return @intCast(usize, rc); + } + } else { + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + + EACCES => return error.AccessDenied, + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdWritable(sockfd); + continue; + } else { + return error.WouldBlock; + }, + EALREADY => return error.FastOpenAlreadyInProgress, + EBADF => unreachable, // always a race condition + ECONNRESET => return error.ConnectionResetByPeer, + EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. + EFAULT => unreachable, // An invalid user space address was specified for an argument. + EINTR => continue, + EINVAL => unreachable, // Invalid argument passed. + EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified + EMSGSIZE => return error.MessageTooBig, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + ENOTCONN => unreachable, // The socket is not connected, and no target has been given. + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. + EPIPE => return error.BrokenPipe, + else => |err| return unexpectedErrno(err), + } } } } @@ -4690,7 +4701,7 @@ pub fn sendto( /// possible to send more data. pub fn send( /// The file descriptor of the sending socket. - sockfd: fd_t, + sockfd: socket_t, buf: []const u8, flags: u32, ) SendError!usize { @@ -5125,8 +5136,12 @@ pub const RecvFromError = error{ SystemResources, } || UnexpectedError; +pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize { + return recvfrom(sock, buf, flags, null, null); +} + pub fn recvfrom( - sockfd: fd_t, + sockfd: socket_t, buf: []u8, flags: u32, src_addr: ?*sockaddr, @@ -5134,23 +5149,34 @@ pub fn recvfrom( ) RecvFromError!usize { while (true) { const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); - switch (errno(rc)) { - 0 => return @intCast(usize, rc), - EBADF => unreachable, // always a race condition - EFAULT => unreachable, - EINVAL => unreachable, - ENOTCONN => unreachable, - ENOTSOCK => unreachable, - EINTR => continue, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(sockfd); - continue; + 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), + } } else { - return error.WouldBlock; - }, - ENOMEM => return error.SystemResources, - ECONNREFUSED => return error.ConnectionRefused, - else => |err| return unexpectedErrno(err), + return @intCast(usize, rc); + } + } else { + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EBADF => unreachable, // always a race condition + EFAULT => unreachable, + EINVAL => unreachable, + ENOTCONN => unreachable, + ENOTSOCK => unreachable, + EINTR => continue, + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(sockfd); + continue; + } else { + return error.WouldBlock; + }, + ENOMEM => return error.SystemResources, + ECONNREFUSED => return error.ConnectionRefused, + else => |err| return unexpectedErrno(err), + } } } } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index df15907482..2da8d05ec6 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1190,6 +1190,27 @@ pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.so return rc; } +pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { + var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) }; + var bytes_send: DWORD = undefined; + if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, to_len, null, null) == ws2_32.SOCKET_ERROR) { + return ws2_32.SOCKET_ERROR; + } else { + return @as(i32, @intCast(u31, bytes_send)); + } +} + +pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 { + var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = buf }; + var bytes_received: DWORD = undefined; + var flags_inout = flags; + if (ws2_32.WSARecvFrom(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_received, &flags_inout, from, from_len, null, null) == ws2_32.SOCKET_ERROR) { + return ws2_32.SOCKET_ERROR; + } else { + return @as(i32, @intCast(u31, bytes_received)); + } +} + pub fn WSAIoctl( s: ws2_32.SOCKET, dwIoControlCode: DWORD, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index e226dfa967..11fcf6c70d 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -786,7 +786,7 @@ pub extern "ws2_32" fn WSASendTo( lpNumberOfBytesSent: ?*DWORD, dwFlags: DWORD, lpTo: ?*const sockaddr, - iTolen: socklen_t, + iTolen: c_int, lpOverlapped: ?*WSAOVERLAPPED, lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, ) callconv(.Stdcall) c_int; From 9094d6cbb83571119e4145e69223f9bd8ddcc48b Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Tue, 8 Sep 2020 12:15:44 +0200 Subject: [PATCH 3/7] fix identation with zig-fmt --- lib/std/os.zig | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 08021cdefe..21e2f8b711 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2906,21 +2906,21 @@ pub fn accept( loop.waitUntilFdReadable(sock); continue; } else { - return error.WouldBlock; - }, - EBADF => unreachable, // always a race condition - ECONNABORTED => return error.ConnectionAborted, - EFAULT => unreachable, - EINVAL => return error.SocketNotListening, - 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), + return error.WouldBlock; + }, + EBADF => unreachable, // always a race condition + ECONNABORTED => return error.ConnectionAborted, + EFAULT => unreachable, + EINVAL => return error.SocketNotListening, + 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; @@ -3120,7 +3120,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne .WSAECONNREFUSED => return error.ConnectionRefused, .WSAETIMEDOUT => return error.ConnectionTimedOut, .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well? - , .WSAENETUNREACH => return error.NetworkUnreachable, + , .WSAENETUNREACH => return error.NetworkUnreachable, .WSAEFAULT => unreachable, .WSAEINVAL => unreachable, .WSAEISCONN => unreachable, From 857613fb65d2ba2d24d9f26d8b3fefd73f8abda6 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Tue, 8 Sep 2020 12:16:03 +0200 Subject: [PATCH 4/7] add unreachable --- lib/std/os.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index 21e2f8b711..80bdca6bdb 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2761,6 +2761,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi // TODO: handle errors else => |err| return windows.unexpectedWSAError(err), } + unreachable; } return; } else { @@ -2782,6 +2783,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi else => |err| return unexpectedErrno(err), } } + unreachable; } const ListenError = error{ From 44f877d18bef6c82d727efeb5ff78ea97d3307bf Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Tue, 8 Sep 2020 12:24:39 +0200 Subject: [PATCH 5/7] change socklen_t to u32 and add appropriate casts when calling WSA --- lib/std/os.zig | 2 +- lib/std/os/windows.zig | 18 ++++-------------- lib/std/os/windows/ws2_32.zig | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 80bdca6bdb..cfb7a62615 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3114,7 +3114,7 @@ pub const ConnectError = error{ /// Initiate a connection on a socket. 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(sock, sock_addr, len); + const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(i32, len)); if (rc == 0) return; switch (windows.ws2_32.WSAGetLastError()) { .WSAEADDRINUSE => return error.AddressInUse, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 2da8d05ec6..69bfd0fbcf 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1154,7 +1154,7 @@ pub fn WSASocketW( } 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); + return ws2_32.bind(s, name, @intCast(i32, namelen)); } pub fn listen(s: ws2_32.SOCKET, backlog: u31) i32 { @@ -1173,27 +1173,17 @@ 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); - } + return ws2_32.accept(s, name, @ptrCast(?*i32, namelen)); } 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; + return ws2_32.getsockname(s, name, @ptrCast(*i32, namelen)); } pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) }; var bytes_send: DWORD = undefined; - if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, to_len, null, null) == ws2_32.SOCKET_ERROR) { + if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) { return ws2_32.SOCKET_ERROR; } else { return @as(i32, @intCast(u31, bytes_send)); diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index 2afe42bb3b..a54a29ed5e 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/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 -pub const socklen_t = u31; +pub const socklen_t = u32; pub const AF_UNSPEC = 0; pub const AF_UNIX = 1; From f5b9e445aa9d2b344b87d4868f9fbb8021577f55 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Wed, 9 Sep 2020 18:55:45 +0200 Subject: [PATCH 6/7] Handle some WSA errors --- lib/std/os.zig | 116 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index cfb7a62615..54c80eb788 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2750,6 +2750,13 @@ pub const BindError = error{ /// The socket inode would reside on a read-only filesystem. ReadOnlyFileSystem, + + /// The network subsystem has failed. + NetworkSubsystemFailed, + + FileDescriptorNotASocket, + + AlreadyBound, } || UnexpectedError; /// addr is `*const T` where T is one of the sockaddr @@ -2758,7 +2765,15 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSANOTINITIALISED => unreachable, // not initialized WSA + .WSAEACCES => return error.AccessDenied, + .WSAEADDRINUSE => return error.AddressInUse, + .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, + .WSAENOTSOCK => return error.FileDescriptorNotASocket, + .WSAEFAULT => unreachable, // invalid pointers + .WSAEINVAL => return error.AlreadyBound, + .WSAENOBUFS => return error.SystemResources, + .WSAENETDOWN => return error.NetworkSubsystemFailed, else => |err| return windows.unexpectedWSAError(err), } unreachable; @@ -2799,6 +2814,19 @@ const ListenError = error{ /// The socket is not of a type that supports the listen() operation. OperationNotSupported, + + /// The network subsystem has failed. + NetworkSubsystemFailed, + + /// Ran out of system resources + /// On Windows it can either run out of socket descriptors or buffer space + SystemResources, + + /// Already connected + AlreadyConnected, + + /// Socket has not been bound yet + SocketNotBound, } || UnexpectedError; pub fn listen(sock: socket_t, backlog: u31) ListenError!void { @@ -2806,7 +2834,15 @@ pub fn listen(sock: socket_t, backlog: u31) ListenError!void { if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSANOTINITIALISED => unreachable, // not initialized WSA + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAEADDRINUSE => return error.AddressInUse, + .WSAEISCONN => return error.AlreadyConnected, + .WSAEINVAL => return error.SocketNotBound, + .WSAEMFILE, .WSAENOBUFS => return error.SystemResources, + .WSAENOTSOCK => return error.FileDescriptorNotASocket, + .WSAEOPNOTSUPP => return error.OperationNotSupported, + .WSAEINPROGRESS => unreachable, else => |err| return windows.unexpectedWSAError(err), } } @@ -2826,6 +2862,9 @@ pub fn listen(sock: socket_t, backlog: u31) ListenError!void { pub const AcceptError = error{ ConnectionAborted, + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + /// The per-process limit on the number of open file descriptors has been reached. ProcessFdQuotaExceeded, @@ -2851,6 +2890,16 @@ pub const AcceptError = error{ /// Permission to create a socket of the specified type and/or /// protocol is denied. PermissionDenied, + + /// An incoming connection was indicated, but was subsequently terminated by the + /// remote peer prior to accepting the call. + ConnectionResetByPeer, + + /// The network subsystem has failed. + NetworkSubsystemFailed, + + /// The referenced socket is not a type that supports connection-oriented service. + OperationNotSupported, } || UnexpectedError; /// Accept a connection on a socket. @@ -2892,7 +2941,15 @@ pub fn accept( if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.INVALID_SOCKET) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSANOTINITIALISED => unreachable, // not initialized WSA + .WSAECONNRESET => return error.ConnectionResetByPeer, + .WSAEFAULT => unreachable, + .WSAEINVAL => return error.SocketNotListening, + .WSAEMFILE => return error.ProcessFdQuotaExceeded, + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAENOBUFS => return error.FileDescriptorNotASocket, + .WSAEOPNOTSUPP => return error.OperationNotSupported, + .WSAEWOULDBLOCK => return error.WouldBlock, else => |err| return windows.unexpectedWSAError(err), } } else { @@ -3044,6 +3101,14 @@ pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { pub const GetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, + + /// The network subsystem has failed. + NetworkSubsystemFailed, + + /// Socket hasn't been bound yet + SocketNotBound, + + FileDescriptorNotASocket, } || UnexpectedError; pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { @@ -3051,7 +3116,11 @@ pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSock if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSANOTINITIALISED => unreachable, + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value + .WSAENOTSOCK => return error.FileDescriptorNotASocket, + .WSAEINVAL => return error.SocketNotBound, else => |err| return windows.unexpectedWSAError(err), } } @@ -3064,7 +3133,7 @@ pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSock EBADF => unreachable, // always a race condition EFAULT => unreachable, EINVAL => unreachable, // invalid parameters - ENOTSOCK => unreachable, + ENOTSOCK => return error.FileDescriptorNotASocket, ENOBUFS => return error.SystemResources, } } @@ -4011,7 +4080,10 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { 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 + .WSANOTINITIALISED => unreachable, + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAENOTSOCK => return error.FileDescriptorNotASocket, + // TODO: handle more errors else => |err| return windows.unexpectedWSAError(err), } } @@ -4625,6 +4697,8 @@ pub const SendError = error{ /// The local end has been shut down on a connection oriented socket. In this case, the /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. BrokenPipe, + + FileDescriptorNotASocket, } || UnexpectedError; /// Transmit a message to another socket. @@ -4666,7 +4740,12 @@ pub fn sendto( if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSAEACCES => return error.AccessDenied, + .WSAECONNRESET => return error.ConnectionResetByPeer, + .WSAEMSGSIZE => return error.MessageTooBig, + .WSAENOBUFS => return error.SystemResources, + .WSAENOTSOCK => return error.FileDescriptorNotASocket, + // TODO: handle more errors else => |err| return windows.unexpectedWSAError(err), } } else { @@ -5158,6 +5237,20 @@ pub const RecvFromError = error{ /// Could not allocate kernel memory. SystemResources, + + ConnectionResetByPeer, + + /// The socket has not been bound. + SocketNotBound, + + /// The UDP message was too big for the buffer and part of it has been discarded + MessageTooBig, + + /// The network subsystem has failed. + NetworkSubsystemFailed, + + /// The socket is not connected (connection-oriented sockets only). + SocketNotConnected, } || UnexpectedError; pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize { @@ -5176,7 +5269,14 @@ pub fn recvfrom( if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { - // TODO: handle errors + .WSANOTINITIALISED => unreachable, + .WSAECONNRESET => return error.ConnectionResetByPeer, + .WSAEINVAL => return error.SocketNotBound, + .WSAEMSGSIZE => return error.MessageTooBig, + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAENOTCONN => return error.SocketNotConnected, + .WSAEWOULDBLOCK => return error.WouldBlock, + // TODO: handle more errors else => |err| return windows.unexpectedWSAError(err), } } else { From 127fa8009011e19d552ffdd834c6a607498f1c1e Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Thu, 10 Sep 2020 20:20:27 +0200 Subject: [PATCH 7/7] implement poll for windows with WSAPoll (only available on vista and higher) --- lib/std/os.zig | 32 +++++++++++++++++++++++++------- lib/std/os/bits/windows.zig | 13 +++++++++++++ lib/std/os/windows.zig | 4 ++++ lib/std/os/windows/ws2_32.zig | 26 ++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 54c80eb788..fdd25fde35 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -5208,6 +5208,9 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len } pub const PollError = error{ + /// The network subsystem has failed. + NetworkSubsystemFailed, + /// The kernel had no space to allocate file descriptor tables. SystemResources, } || UnexpectedError; @@ -5215,14 +5218,29 @@ pub const PollError = error{ pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { while (true) { const rc = system.poll(fds.ptr, fds.len, timeout); - switch (errno(rc)) { - 0 => return @intCast(usize, rc), - EFAULT => unreachable, - EINTR => continue, - EINVAL => unreachable, - ENOMEM => return error.SystemResources, - else => |err| return unexpectedErrno(err), + if (builtin.os.tag == .windows) { + if (rc == windows.ws2_32.SOCKET_ERROR) { + switch (windows.ws2_32.WSAGetLastError()) { + .WSANOTINITIALISED => unreachable, + .WSAENETDOWN => return error.NetworkSubsystemFailed, + .WSAENOBUFS => return error.SystemResources, + // TODO: handle more errors + else => |err| return windows.unexpectedWSAError(err), + } + } else { + return @intCast(usize, rc); + } + } else { + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EFAULT => unreachable, + EINTR => continue, + EINVAL => unreachable, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } } + unreachable; } } diff --git a/lib/std/os/bits/windows.zig b/lib/std/os/bits/windows.zig index de17fd83fa..a92961881e 100644 --- a/lib/std/os/bits/windows.zig +++ b/lib/std/os/bits/windows.zig @@ -243,6 +243,19 @@ pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP; pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6; pub const IPPROTO_RM = ws2_32.IPPROTO_RM; +pub const pollfd = ws2_32.pollfd; + +pub const POLLRDNORM = ws2_32.POLLRDNORM; +pub const POLLRDBAND = ws2_32.POLLRDBAND; +pub const POLLIN = ws2_32.POLLIN; +pub const POLLPRI = ws2_32.POLLPRI; +pub const POLLWRNORM = ws2_32.POLLWRNORM; +pub const POLLOUT = ws2_32.POLLOUT; +pub const POLLWRBAND = ws2_32.POLLWRBAND; +pub const POLLERR = ws2_32.POLLERR; +pub const POLLHUP = ws2_32.POLLHUP; +pub const POLLNVAL = ws2_32.POLLNVAL; + pub const O_RDONLY = 0o0; pub const O_WRONLY = 0o1; pub const O_RDWR = 0o2; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 69bfd0fbcf..f53979fc7c 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1201,6 +1201,10 @@ pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws } } +pub fn poll(fds: [*]ws2_32.pollfd, n: usize, timeout: i32) i32 { + return ws2_32.WSAPoll(fds, @intCast(u32, n), timeout); +} + pub fn WSAIoctl( s: ws2_32.SOCKET, dwIoControlCode: DWORD, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index a54a29ed5e..0f6381a9a1 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -234,6 +234,27 @@ pub const WSAMSG = extern struct { dwFlags: DWORD, }; +pub const pollfd = extern struct { + fd: SOCKET, + events: SHORT, + revents: SHORT, +}; + +// Event flag definitions for WSAPoll(). + +pub const POLLRDNORM = 0x0100; +pub const POLLRDBAND = 0x0200; +pub const POLLIN = (POLLRDNORM | POLLRDBAND); +pub const POLLPRI = 0x0400; + +pub const POLLWRNORM = 0x0010; +pub const POLLOUT = (POLLWRNORM); +pub const POLLWRBAND = 0x0020; + +pub const POLLERR = 0x0001; +pub const POLLHUP = 0x0002; +pub const POLLNVAL = 0x0004; + // https://docs.microsoft.com/en-au/windows/win32/winsock/windows-sockets-error-codes-2 pub const WinsockError = extern enum(u16) { /// Specified event object handle is invalid. @@ -790,6 +811,11 @@ pub extern "ws2_32" fn WSASendTo( lpOverlapped: ?*WSAOVERLAPPED, lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, ) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSAPoll( + fdArray: [*]pollfd, + fds: c_ulong, + timeout: c_int, +) callconv(.Stdcall) c_int; pub extern "ws2_32" fn getaddrinfo( pNodeName: [*:0]const u8, pServiceName: [*:0]const u8,