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;