Commit ab003cd054

Andrew Kelley <andrew@ziglang.org>
2025-10-21 19:30:06
std.Io.Threaded: implement netLookup for Windows
1 parent dab8dd5
Changed files (2)
lib
std
lib/std/Io/Threaded.zig
@@ -4058,8 +4058,81 @@ fn netLookupFallible(
     assert(name.len <= HostName.max_len);
 
     if (is_windows) {
-        // TODO use GetAddrInfoExW / GetAddrInfoExCancel
-        @compileError("TODO");
+        var name_buffer: [HostName.max_len + 1]u16 = undefined;
+        const name_len = std.unicode.wtf8ToWtf16Le(&name_buffer, host_name.bytes) catch
+            unreachable; // HostName is prevalidated.
+        name_buffer[name_len] = 0;
+        const name_w = name_buffer[0..name_len :0];
+
+        var port_buffer: [8]u8 = undefined;
+        var port_buffer_wide: [8]u16 = undefined;
+        const port = std.fmt.bufPrint(&port_buffer, "{d}", .{options.port}) catch
+            unreachable; // `port_buffer` is big enough for decimal u16.
+        for (port, port_buffer[0..port.len]) |byte, *wide| wide.* = byte;
+        port_buffer_wide[port.len] = 0;
+        const port_w = port_buffer_wide[0..port.len :0];
+
+        const hints: ws2_32.ADDRINFOEXW = .{
+            .flags = .{ .NUMERICSERV = true },
+            .family = posix.AF.UNSPEC,
+            .socktype = posix.SOCK.STREAM,
+            .protocol = posix.IPPROTO.TCP,
+            .canonname = null,
+            .addr = null,
+            .addrlen = 0,
+            .blob = null,
+            .bloblen = 0,
+            .provider = null,
+            .next = null,
+        };
+        var res: *ws2_32.ADDRINFOEXW = undefined;
+        const timeout: ?*ws2_32.timeval = null;
+        while (true) {
+            try t.checkCancel(); // TODO make requestCancel call GetAddrInfoExCancel
+            // TODO make this append to the queue eagerly rather than blocking until
+            // the whole thing finishes
+            const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null));
+            switch (rc) {
+                @as(ws2_32.WinsockError, @enumFromInt(0)) => break,
+                .EINTR => continue,
+                .ECANCELLED, .E_CANCELLED => return error.Canceled,
+                .NOTINITIALISED => {
+                    try initializeWsa(t);
+                    continue;
+                },
+                .TRY_AGAIN => return error.NameServerFailure,
+                .EINVAL => |err| return wsaErrorBug(err),
+                .NO_RECOVERY => return error.NameServerFailure,
+                .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
+                .NOT_ENOUGH_MEMORY => return error.SystemResources,
+                .HOST_NOT_FOUND => return error.UnknownHostName,
+                .TYPE_NOT_FOUND => return error.ProtocolUnsupportedByAddressFamily,
+                .ESOCKTNOSUPPORT => return error.ProtocolUnsupportedBySystem,
+                else => |err| return windows.unexpectedWSAError(err),
+            }
+        }
+        defer ws2_32.FreeAddrInfoExW(res);
+
+        var it: ?*ws2_32.ADDRINFOEXW = res;
+        var canon_name: ?[*:0]const u16 = null;
+        while (it) |info| : (it = info.next) {
+            const addr = info.addr orelse continue;
+            const storage: WsaAddress = .{ .any = addr.* };
+            try resolved.putOne(t_io, .{ .address = addressFromWsa(&storage) });
+
+            if (info.canonname) |n| {
+                if (canon_name == null) {
+                    canon_name = n;
+                }
+            }
+        }
+        if (canon_name) |n| {
+            const len = std.unicode.wtf16LeToWtf8(options.canonical_name_buffer, std.mem.sliceTo(n, 0));
+            try resolved.putOne(t_io, .{ .canonical_name = .{
+                .bytes = options.canonical_name_buffer[0..len],
+            } });
+        }
+        return;
     }
 
     // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics.
lib/std/os/windows/ws2_32.zig
@@ -702,28 +702,32 @@ pub const FIONBIO = -2147195266;
 pub const ADDRINFOEX_VERSION_2 = 2;
 pub const ADDRINFOEX_VERSION_3 = 3;
 pub const ADDRINFOEX_VERSION_4 = 4;
-pub const NS_ALL = 0;
-pub const NS_SAP = 1;
-pub const NS_NDS = 2;
-pub const NS_PEER_BROWSE = 3;
-pub const NS_SLP = 5;
-pub const NS_DHCP = 6;
-pub const NS_TCPIP_LOCAL = 10;
-pub const NS_TCPIP_HOSTS = 11;
-pub const NS_DNS = 12;
-pub const NS_NETBT = 13;
-pub const NS_WINS = 14;
-pub const NS_NLA = 15;
-pub const NS_NBP = 20;
-pub const NS_MS = 30;
-pub const NS_STDA = 31;
-pub const NS_NTDS = 32;
-pub const NS_EMAIL = 37;
-pub const NS_X500 = 40;
-pub const NS_NIS = 41;
-pub const NS_NISPLUS = 42;
-pub const NS_WRQ = 50;
-pub const NS_NETDES = 60;
+
+pub const NS = enum(u32) {
+    ALL = 0,
+    SAP = 1,
+    NDS = 2,
+    PEER_BROWSE = 3,
+    SLP = 5,
+    DHCP = 6,
+    TCPIP_LOCAL = 10,
+    TCPIP_HOSTS = 11,
+    DNS = 12,
+    NETBT = 13,
+    WINS = 14,
+    NLA = 15,
+    NBP = 20,
+    MS = 30,
+    STDA = 31,
+    NTDS = 32,
+    EMAIL = 37,
+    X500 = 40,
+    NIS = 41,
+    NISPLUS = 42,
+    WRQ = 50,
+    NETDES = 60,
+};
+
 pub const NI_NOFQDN = 1;
 pub const NI_NUMERICHOST = 2;
 pub const NI_NAMEREQD = 4;
@@ -1086,12 +1090,12 @@ pub const ADDRINFOEXW = extern struct {
     socktype: i32,
     protocol: i32,
     addrlen: usize,
-    canonname: [*:0]u16,
-    addr: *sockaddr,
-    blob: *anyopaque,
+    canonname: ?[*:0]u16,
+    addr: ?*sockaddr,
+    blob: ?*anyopaque,
     bloblen: usize,
-    provider: *GUID,
-    next: *ADDRINFOEXW,
+    provider: ?*GUID,
+    next: ?*ADDRINFOEXW,
 };
 
 pub const sockaddr = extern struct {
@@ -2101,7 +2105,7 @@ pub extern "mswsock" fn EnumProtocolsW(
 ) callconv(.winapi) i32;
 
 pub extern "mswsock" fn GetAddressByNameW(
-    dwNameSpace: u32,
+    dwNameSpace: NS,
     lpServiceType: *GUID,
     lpServiceName: ?[*:0]u16,
     lpiProtocols: ?*i32,
@@ -2127,7 +2131,7 @@ pub extern "mswsock" fn GetNameByTypeW(
 pub extern "ws2_32" fn GetAddrInfoExW(
     pName: ?[*:0]const u16,
     pServiceName: ?[*:0]const u16,
-    dwNameSpace: DWORD,
+    dwNameSpace: NS,
     lpNspId: ?*GUID,
     hints: ?*const ADDRINFOEXW,
     ppResult: **ADDRINFOEXW,