diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 4b942e2379..ed7966d014 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -252,7 +252,7 @@ pub fn io(t: *Threaded) Io { else => netBindIpPosix, }, .netConnectIp = switch (builtin.os.tag) { - .windows => @panic("TODO"), + .windows => netConnectIpWindows, else => netConnectIpPosix, }, .netConnectUnix = netConnectUnix, @@ -2810,28 +2810,10 @@ fn netListenIpWindows( if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); const family = posixAddressFamily(&address); - const mode = posixSocketMode(options.mode); - const protocol = posixProtocol(options.protocol); - - const socket_handle = while (true) { - try t.checkCancel(); - const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; - const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags); - if (rc != ws2_32.INVALID_SOCKET) break rc; - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); - continue; - }, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - .EMFILE => return error.ProcessFdQuotaExceeded, - .ENOBUFS => return error.SystemResources, - .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, - else => |err| return windows.unexpectedWSAError(err), - } - }; + const socket_handle = try openSocketWsa(t, family, .{ + .mode = options.mode, + .protocol = options.protocol, + }); errdefer closeSocketWindows(socket_handle); if (options.reuse_address) @@ -2885,24 +2867,7 @@ fn netListenIpWindows( } } - while (true) { - try t.checkCancel(); - const rc = ws2_32.getsockname(socket_handle, &storage.any, &addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); - continue; - }, - .ENETDOWN => return error.NetworkDown, - .EFAULT => |err| return wsaErrorBug(err), - .ENOTSOCK => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), - } - } + try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); return .{ .socket = .{ @@ -3076,6 +3041,27 @@ fn posixGetSockName(t: *Threaded, socket_fd: posix.fd_t, addr: *posix.sockaddr, } } +fn wsaGetSockName(t: *Threaded, handle: ws2_32.SOCKET, addr: *ws2_32.sockaddr, addr_len: *i32) !void { + while (true) { + try t.checkCancel(); + const rc = ws2_32.getsockname(handle, addr, addr_len); + if (rc != ws2_32.SOCKET_ERROR) break; + switch (ws2_32.WSAGetLastError()) { + .EINTR => continue, + .ECANCELLED, .E_CANCELLED => return error.Canceled, + .NOTINITIALISED => { + try initializeWsa(t); + continue; + }, + .ENETDOWN => return error.NetworkDown, + .EFAULT => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + } +} + fn setSocketOption(t: *Threaded, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void { const o: []const u8 = @ptrCast(&option); while (true) { @@ -3139,6 +3125,61 @@ fn netConnectIpPosix( } }; } +fn netConnectIpWindows( + userdata: ?*anyopaque, + address: *const IpAddress, + options: IpAddress.ConnectOptions, +) IpAddress.ConnectError!net.Stream { + if (!have_networking) return error.NetworkDown; + if (options.timeout != .none) @panic("TODO"); + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const family = posixAddressFamily(address); + const socket_handle = try openSocketWsa(t, family, .{ + .mode = options.mode, + .protocol = options.protocol, + }); + errdefer closeSocketWindows(socket_handle); + + var storage: WsaAddress = undefined; + var addr_len = addressToWsa(address, &storage); + + while (true) { + const rc = ws2_32.connect(socket_handle, &storage.any, addr_len); + if (rc != ws2_32.SOCKET_ERROR) break; + switch (ws2_32.WSAGetLastError()) { + .EINTR => continue, + .ECANCELLED, .E_CANCELLED => return error.Canceled, + .NOTINITIALISED => { + try initializeWsa(t); + continue; + }, + + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ECONNREFUSED => return error.ConnectionRefused, + .ECONNRESET => return error.ConnectionResetByPeer, + .ETIMEDOUT => return error.Timeout, + .EHOSTUNREACH => return error.HostUnreachable, + .ENETUNREACH => return error.NetworkUnreachable, + .EFAULT => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EISCONN => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EWOULDBLOCK => return error.WouldBlock, + .EACCES => return error.AccessDenied, + .ENOBUFS => return error.SystemResources, + .EAFNOSUPPORT => return error.AddressFamilyUnsupported, + else => |err| return windows.unexpectedWSAError(err), + } + } + + try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); + + return .{ .socket = .{ + .handle = socket_handle, + .address = addressFromWsa(&storage), + } }; +} + fn netConnectUnix( userdata: ?*anyopaque, address: *const net.UnixAddress, @@ -3184,28 +3225,12 @@ fn netBindIpWindows( if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); const family = posixAddressFamily(address); - const mode = posixSocketMode(options.mode); - const protocol = posixProtocol(options.protocol); - const socket_handle = while (true) { - try t.checkCancel(); - const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; - const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags); - if (rc != ws2_32.INVALID_SOCKET) break rc; - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); - continue; - }, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - .EMFILE => return error.ProcessFdQuotaExceeded, - .ENOBUFS => return error.SystemResources, - .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, - else => |err| return windows.unexpectedWSAError(err), - } - }; + const socket_handle = try openSocketWsa(t, family, .{ + .mode = options.mode, + .protocol = options.protocol, + }); errdefer closeSocketWindows(socket_handle); + var storage: WsaAddress = undefined; var addr_len = addressToWsa(address, &storage); @@ -3231,24 +3256,7 @@ fn netBindIpWindows( } } - while (true) { - try t.checkCancel(); - const rc = ws2_32.getsockname(socket_handle, &storage.any, &addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); - continue; - }, - .ENETDOWN => return error.NetworkDown, - .EFAULT => |err| return wsaErrorBug(err), - .ENOTSOCK => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), - } - } + try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); return .{ .handle = socket_handle, @@ -3317,6 +3325,30 @@ fn openSocketPosix( return socket_fd; } +fn openSocketWsa(t: *Threaded, family: posix.sa_family_t, options: IpAddress.BindOptions) !ws2_32.SOCKET { + const mode = posixSocketMode(options.mode); + const protocol = posixProtocol(options.protocol); + const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; + while (true) { + try t.checkCancel(); + const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags); + if (rc != ws2_32.INVALID_SOCKET) return rc; + switch (ws2_32.WSAGetLastError()) { + .EINTR => continue, + .ECANCELLED, .E_CANCELLED => return error.Canceled, + .NOTINITIALISED => { + try initializeWsa(t); + continue; + }, + .EAFNOSUPPORT => return error.AddressFamilyUnsupported, + .EMFILE => return error.ProcessFdQuotaExceeded, + .ENOBUFS => return error.SystemResources, + .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, + else => |err| return windows.unexpectedWSAError(err), + } + } +} + fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); @@ -3376,11 +3408,11 @@ fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net while (true) { try t.checkCancel(); const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len); - if (rc != windows.ws2_32.INVALID_SOCKET) return .{ .socket = .{ + if (rc != ws2_32.INVALID_SOCKET) return .{ .socket = .{ .handle = rc, .address = addressFromWsa(&storage), } }; - switch (windows.ws2_32.WSAGetLastError()) { + switch (ws2_32.WSAGetLastError()) { .EINTR => continue, .ECANCELLED, .E_CANCELLED => return error.Canceled, .NOTINITIALISED => { diff --git a/lib/std/posix.zig b/lib/std/posix.zig index dd0ad16695..bb556be133 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -3757,86 +3757,11 @@ pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSock } } -pub const ConnectError = error{ - /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket - /// file, or search permission is denied for one of the directories in the path prefix. - /// or - /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or - /// the connection request failed because of a local firewall rule. - AccessDenied, +pub const ConnectError = std.Io.net.IpAddress.ConnectError || std.Io.net.UnixAddress.ConnectError; - /// See AccessDenied - PermissionDenied, - - /// Local address is already in use. - AddressInUse, - - /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an - /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers - /// in the ephemeral port range are currently in use. See the discussion of - /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). - AddressUnavailable, - - /// The passed address didn't have the correct address family in its sa_family field. - AddressFamilyUnsupported, - - /// Insufficient entries in the routing cache. - SystemResources, - - /// A connect() on a stream socket found no one listening on the remote address. - ConnectionRefused, - - /// Network is unreachable. - NetworkUnreachable, - - /// Timeout while attempting connection. The server may be too busy to accept new connections. Note - /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. - Timeout, - - /// This error occurs when no global event loop is configured, - /// and connecting to the socket would block. - WouldBlock, - - /// The given path for the unix socket does not exist. - FileNotFound, - - /// Connection was reset by peer before connect could complete. - ConnectionResetByPeer, - - /// Socket is non-blocking and already has a pending connection in progress. - ConnectionPending, - - /// Socket was already connected - AlreadyConnected, -} || UnexpectedError; - -/// Initiate a connection on a socket. -/// If `sockfd` is opened in non blocking mode, the function will -/// return error.WouldBlock when EAGAIN or EINPROGRESS is received. pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { if (native_os == .windows) { - const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len)); - if (rc == 0) return; - switch (windows.ws2_32.WSAGetLastError()) { - .EADDRINUSE => return error.AddressInUse, - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ECONNREFUSED => return error.ConnectionRefused, - .ECONNRESET => return error.ConnectionResetByPeer, - .ETIMEDOUT => return error.Timeout, - .EHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well? - .ENETUNREACH, - => return error.NetworkUnreachable, - .EFAULT => unreachable, - .EINVAL => unreachable, - .EISCONN => return error.AlreadyConnected, - .ENOTSOCK => unreachable, - .EWOULDBLOCK => return error.WouldBlock, - .EACCES => unreachable, - .ENOBUFS => return error.SystemResources, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - else => |err| return windows.unexpectedWSAError(err), - } - return; + @compileError("use std.Io instead"); } while (true) {