authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-21 04:30:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
log4ed74a9f8ab97f3358fc881d0e2f6359e22186d6
treef411b99d69f195714599f59a81d44b0bc106a32e
parent67df66c26cdf63a637c9ac86b64f447d2053b6b3

std.Io.Threaded: implement netConnectIp for Windows


2 files changed, 116 insertions(+), 159 deletions(-)

lib/std/Io/Threaded.zig+114-82
......@@ -252,7 +252,7 @@ pub fn io(t: *Threaded) Io {
252252 else => netBindIpPosix,
253253 },
254254 .netConnectIp = switch (builtin.os.tag) {
255 .windows => @panic("TODO"),
255 .windows => netConnectIpWindows,
256256 else => netConnectIpPosix,
257257 },
258258 .netConnectUnix = netConnectUnix,
......@@ -2810,28 +2810,10 @@ fn netListenIpWindows(
28102810 if (!have_networking) return error.NetworkDown;
28112811 const t: *Threaded = @ptrCast(@alignCast(userdata));
28122812 const family = posixAddressFamily(&address);
2813 const mode = posixSocketMode(options.mode);
2814 const protocol = posixProtocol(options.protocol);
2815
2816 const socket_handle = while (true) {
2817 try t.checkCancel();
2818 const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
2819 const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
2820 if (rc != ws2_32.INVALID_SOCKET) break rc;
2821 switch (ws2_32.WSAGetLastError()) {
2822 .EINTR => continue,
2823 .ECANCELLED, .E_CANCELLED => return error.Canceled,
2824 .NOTINITIALISED => {
2825 try initializeWsa(t);
2826 continue;
2827 },
2828 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
2829 .EMFILE => return error.ProcessFdQuotaExceeded,
2830 .ENOBUFS => return error.SystemResources,
2831 .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
2832 else => |err| return windows.unexpectedWSAError(err),
2833 }
2834 };
2813 const socket_handle = try openSocketWsa(t, family, .{
2814 .mode = options.mode,
2815 .protocol = options.protocol,
2816 });
28352817 errdefer closeSocketWindows(socket_handle);
28362818
28372819 if (options.reuse_address)
......@@ -2885,24 +2867,7 @@ fn netListenIpWindows(
28852867 }
28862868 }
28872869
2888 while (true) {
2889 try t.checkCancel();
2890 const rc = ws2_32.getsockname(socket_handle, &storage.any, &addr_len);
2891 if (rc != ws2_32.SOCKET_ERROR) break;
2892 switch (ws2_32.WSAGetLastError()) {
2893 .EINTR => continue,
2894 .ECANCELLED, .E_CANCELLED => return error.Canceled,
2895 .NOTINITIALISED => {
2896 try initializeWsa(t);
2897 continue;
2898 },
2899 .ENETDOWN => return error.NetworkDown,
2900 .EFAULT => |err| return wsaErrorBug(err),
2901 .ENOTSOCK => |err| return wsaErrorBug(err),
2902 .EINVAL => |err| return wsaErrorBug(err),
2903 else => |err| return windows.unexpectedWSAError(err),
2904 }
2905 }
2870 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
29062871
29072872 return .{
29082873 .socket = .{
......@@ -3076,6 +3041,27 @@ fn posixGetSockName(t: *Threaded, socket_fd: posix.fd_t, addr: *posix.sockaddr,
30763041 }
30773042}
30783043
3044fn wsaGetSockName(t: *Threaded, handle: ws2_32.SOCKET, addr: *ws2_32.sockaddr, addr_len: *i32) !void {
3045 while (true) {
3046 try t.checkCancel();
3047 const rc = ws2_32.getsockname(handle, addr, addr_len);
3048 if (rc != ws2_32.SOCKET_ERROR) break;
3049 switch (ws2_32.WSAGetLastError()) {
3050 .EINTR => continue,
3051 .ECANCELLED, .E_CANCELLED => return error.Canceled,
3052 .NOTINITIALISED => {
3053 try initializeWsa(t);
3054 continue;
3055 },
3056 .ENETDOWN => return error.NetworkDown,
3057 .EFAULT => |err| return wsaErrorBug(err),
3058 .ENOTSOCK => |err| return wsaErrorBug(err),
3059 .EINVAL => |err| return wsaErrorBug(err),
3060 else => |err| return windows.unexpectedWSAError(err),
3061 }
3062 }
3063}
3064
30793065fn setSocketOption(t: *Threaded, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void {
30803066 const o: []const u8 = @ptrCast(&option);
30813067 while (true) {
......@@ -3139,6 +3125,61 @@ fn netConnectIpPosix(
31393125 } };
31403126}
31413127
3128fn netConnectIpWindows(
3129 userdata: ?*anyopaque,
3130 address: *const IpAddress,
3131 options: IpAddress.ConnectOptions,
3132) IpAddress.ConnectError!net.Stream {
3133 if (!have_networking) return error.NetworkDown;
3134 if (options.timeout != .none) @panic("TODO");
3135 const t: *Threaded = @ptrCast(@alignCast(userdata));
3136 const family = posixAddressFamily(address);
3137 const socket_handle = try openSocketWsa(t, family, .{
3138 .mode = options.mode,
3139 .protocol = options.protocol,
3140 });
3141 errdefer closeSocketWindows(socket_handle);
3142
3143 var storage: WsaAddress = undefined;
3144 var addr_len = addressToWsa(address, &storage);
3145
3146 while (true) {
3147 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
3148 if (rc != ws2_32.SOCKET_ERROR) break;
3149 switch (ws2_32.WSAGetLastError()) {
3150 .EINTR => continue,
3151 .ECANCELLED, .E_CANCELLED => return error.Canceled,
3152 .NOTINITIALISED => {
3153 try initializeWsa(t);
3154 continue;
3155 },
3156
3157 .EADDRNOTAVAIL => return error.AddressUnavailable,
3158 .ECONNREFUSED => return error.ConnectionRefused,
3159 .ECONNRESET => return error.ConnectionResetByPeer,
3160 .ETIMEDOUT => return error.Timeout,
3161 .EHOSTUNREACH => return error.HostUnreachable,
3162 .ENETUNREACH => return error.NetworkUnreachable,
3163 .EFAULT => |err| return wsaErrorBug(err),
3164 .EINVAL => |err| return wsaErrorBug(err),
3165 .EISCONN => |err| return wsaErrorBug(err),
3166 .ENOTSOCK => |err| return wsaErrorBug(err),
3167 .EWOULDBLOCK => return error.WouldBlock,
3168 .EACCES => return error.AccessDenied,
3169 .ENOBUFS => return error.SystemResources,
3170 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
3171 else => |err| return windows.unexpectedWSAError(err),
3172 }
3173 }
3174
3175 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
3176
3177 return .{ .socket = .{
3178 .handle = socket_handle,
3179 .address = addressFromWsa(&storage),
3180 } };
3181}
3182
31423183fn netConnectUnix(
31433184 userdata: ?*anyopaque,
31443185 address: *const net.UnixAddress,
......@@ -3184,28 +3225,12 @@ fn netBindIpWindows(
31843225 if (!have_networking) return error.NetworkDown;
31853226 const t: *Threaded = @ptrCast(@alignCast(userdata));
31863227 const family = posixAddressFamily(address);
3187 const mode = posixSocketMode(options.mode);
3188 const protocol = posixProtocol(options.protocol);
3189 const socket_handle = while (true) {
3190 try t.checkCancel();
3191 const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
3192 const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
3193 if (rc != ws2_32.INVALID_SOCKET) break rc;
3194 switch (ws2_32.WSAGetLastError()) {
3195 .EINTR => continue,
3196 .ECANCELLED, .E_CANCELLED => return error.Canceled,
3197 .NOTINITIALISED => {
3198 try initializeWsa(t);
3199 continue;
3200 },
3201 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
3202 .EMFILE => return error.ProcessFdQuotaExceeded,
3203 .ENOBUFS => return error.SystemResources,
3204 .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
3205 else => |err| return windows.unexpectedWSAError(err),
3206 }
3207 };
3228 const socket_handle = try openSocketWsa(t, family, .{
3229 .mode = options.mode,
3230 .protocol = options.protocol,
3231 });
32083232 errdefer closeSocketWindows(socket_handle);
3233
32093234 var storage: WsaAddress = undefined;
32103235 var addr_len = addressToWsa(address, &storage);
32113236
......@@ -3231,24 +3256,7 @@ fn netBindIpWindows(
32313256 }
32323257 }
32333258
3234 while (true) {
3235 try t.checkCancel();
3236 const rc = ws2_32.getsockname(socket_handle, &storage.any, &addr_len);
3237 if (rc != ws2_32.SOCKET_ERROR) break;
3238 switch (ws2_32.WSAGetLastError()) {
3239 .EINTR => continue,
3240 .ECANCELLED, .E_CANCELLED => return error.Canceled,
3241 .NOTINITIALISED => {
3242 try initializeWsa(t);
3243 continue;
3244 },
3245 .ENETDOWN => return error.NetworkDown,
3246 .EFAULT => |err| return wsaErrorBug(err),
3247 .ENOTSOCK => |err| return wsaErrorBug(err),
3248 .EINVAL => |err| return wsaErrorBug(err),
3249 else => |err| return windows.unexpectedWSAError(err),
3250 }
3251 }
3259 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
32523260
32533261 return .{
32543262 .handle = socket_handle,
......@@ -3317,6 +3325,30 @@ fn openSocketPosix(
33173325 return socket_fd;
33183326}
33193327
3328fn openSocketWsa(t: *Threaded, family: posix.sa_family_t, options: IpAddress.BindOptions) !ws2_32.SOCKET {
3329 const mode = posixSocketMode(options.mode);
3330 const protocol = posixProtocol(options.protocol);
3331 const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
3332 while (true) {
3333 try t.checkCancel();
3334 const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
3335 if (rc != ws2_32.INVALID_SOCKET) return rc;
3336 switch (ws2_32.WSAGetLastError()) {
3337 .EINTR => continue,
3338 .ECANCELLED, .E_CANCELLED => return error.Canceled,
3339 .NOTINITIALISED => {
3340 try initializeWsa(t);
3341 continue;
3342 },
3343 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
3344 .EMFILE => return error.ProcessFdQuotaExceeded,
3345 .ENOBUFS => return error.SystemResources,
3346 .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
3347 else => |err| return windows.unexpectedWSAError(err),
3348 }
3349 }
3350}
3351
33203352fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream {
33213353 if (!have_networking) return error.NetworkDown;
33223354 const t: *Threaded = @ptrCast(@alignCast(userdata));
......@@ -3376,11 +3408,11 @@ fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net
33763408 while (true) {
33773409 try t.checkCancel();
33783410 const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len);
3379 if (rc != windows.ws2_32.INVALID_SOCKET) return .{ .socket = .{
3411 if (rc != ws2_32.INVALID_SOCKET) return .{ .socket = .{
33803412 .handle = rc,
33813413 .address = addressFromWsa(&storage),
33823414 } };
3383 switch (windows.ws2_32.WSAGetLastError()) {
3415 switch (ws2_32.WSAGetLastError()) {
33843416 .EINTR => continue,
33853417 .ECANCELLED, .E_CANCELLED => return error.Canceled,
33863418 .NOTINITIALISED => {
lib/std/posix.zig+2-77
......@@ -3757,86 +3757,11 @@ pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSock
37573757 }
37583758}
37593759
3760pub const ConnectError = error{
3761 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
3762 /// file, or search permission is denied for one of the directories in the path prefix.
3763 /// or
3764 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
3765 /// the connection request failed because of a local firewall rule.
3766 AccessDenied,
3767
3768 /// See AccessDenied
3769 PermissionDenied,
3770
3771 /// Local address is already in use.
3772 AddressInUse,
3773
3774 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
3775 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
3776 /// in the ephemeral port range are currently in use. See the discussion of
3777 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
3778 AddressUnavailable,
3760pub const ConnectError = std.Io.net.IpAddress.ConnectError || std.Io.net.UnixAddress.ConnectError;
37793761
3780 /// The passed address didn't have the correct address family in its sa_family field.
3781 AddressFamilyUnsupported,
3782
3783 /// Insufficient entries in the routing cache.
3784 SystemResources,
3785
3786 /// A connect() on a stream socket found no one listening on the remote address.
3787 ConnectionRefused,
3788
3789 /// Network is unreachable.
3790 NetworkUnreachable,
3791
3792 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
3793 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
3794 Timeout,
3795
3796 /// This error occurs when no global event loop is configured,
3797 /// and connecting to the socket would block.
3798 WouldBlock,
3799
3800 /// The given path for the unix socket does not exist.
3801 FileNotFound,
3802
3803 /// Connection was reset by peer before connect could complete.
3804 ConnectionResetByPeer,
3805
3806 /// Socket is non-blocking and already has a pending connection in progress.
3807 ConnectionPending,
3808
3809 /// Socket was already connected
3810 AlreadyConnected,
3811} || UnexpectedError;
3812
3813/// Initiate a connection on a socket.
3814/// If `sockfd` is opened in non blocking mode, the function will
3815/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
38163762pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
38173763 if (native_os == .windows) {
3818 const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len));
3819 if (rc == 0) return;
3820 switch (windows.ws2_32.WSAGetLastError()) {
3821 .EADDRINUSE => return error.AddressInUse,
3822 .EADDRNOTAVAIL => return error.AddressUnavailable,
3823 .ECONNREFUSED => return error.ConnectionRefused,
3824 .ECONNRESET => return error.ConnectionResetByPeer,
3825 .ETIMEDOUT => return error.Timeout,
3826 .EHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
3827 .ENETUNREACH,
3828 => return error.NetworkUnreachable,
3829 .EFAULT => unreachable,
3830 .EINVAL => unreachable,
3831 .EISCONN => return error.AlreadyConnected,
3832 .ENOTSOCK => unreachable,
3833 .EWOULDBLOCK => return error.WouldBlock,
3834 .EACCES => unreachable,
3835 .ENOBUFS => return error.SystemResources,
3836 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
3837 else => |err| return windows.unexpectedWSAError(err),
3838 }
3839 return;
3764 @compileError("use std.Io instead");
38403765 }
38413766
38423767 while (true) {