diff --git a/lib/std/net.zig b/lib/std/net.zig index da4cf30046..c4cee40a1d 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -746,7 +746,79 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A const arena = result.arena.allocator(); errdefer result.deinit(); - if (builtin.target.os.tag == .windows or builtin.link_libc) { + if (builtin.target.os.tag == .windows) { + const name_c = try std.cstr.addNullByte(allocator, name); + defer allocator.free(name_c); + + const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); + defer allocator.free(port_c); + + const ws2_32 = os.windows.ws2_32; + const hints = os.addrinfo{ + .flags = ws2_32.AI.NUMERICSERV, + .family = os.AF.UNSPEC, + .socktype = os.SOCK.STREAM, + .protocol = os.IPPROTO.TCP, + .canonname = null, + .addr = null, + .addrlen = 0, + .next = null, + }; + var res: *os.addrinfo = undefined; + var first = true; + while (true) { + const rc = ws2_32.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res); + switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) { + @intToEnum(os.windows.ws2_32.WinsockError, 0) => break, + .WSATRY_AGAIN => return error.TemporaryNameServerFailure, + .WSANO_RECOVERY => return error.NameServerFailure, + .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, + .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory, + .WSAHOST_NOT_FOUND => return error.UnknownHostName, + .WSATYPE_NOT_FOUND => return error.ServiceUnavailable, + .WSAEINVAL => unreachable, + .WSAESOCKTNOSUPPORT => unreachable, + .WSANOTINITIALISED => { + if (!first) return error.Unexpected; + first = false; + try os.windows.callWSAStartup(); + continue; + }, + else => |err| return os.windows.unexpectedWSAError(err), + } + } + defer ws2_32.freeaddrinfo(res); + + const addr_count = blk: { + var count: usize = 0; + var it: ?*os.addrinfo = res; + while (it) |info| : (it = info.next) { + if (info.addr != null) { + count += 1; + } + } + break :blk count; + }; + result.addrs = try arena.alloc(Address, addr_count); + + var it: ?*os.addrinfo = res; + var i: usize = 0; + while (it) |info| : (it = info.next) { + const addr = info.addr orelse continue; + result.addrs[i] = Address.initPosix(@alignCast(4, addr)); + + if (info.canonname) |n| { + if (result.canon_name == null) { + result.canon_name = try arena.dupe(u8, mem.sliceTo(n, 0)); + } + } + i += 1; + } + + return result; + } + + if (builtin.link_libc) { const name_c = try std.cstr.addNullByte(allocator, name); defer allocator.free(name_c); @@ -765,19 +837,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A .next = null, }; var res: *os.addrinfo = undefined; - const rc = sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res); - if (builtin.target.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) { - @intToEnum(os.windows.ws2_32.WinsockError, 0) => {}, - .WSATRY_AGAIN => return error.TemporaryNameServerFailure, - .WSANO_RECOVERY => return error.NameServerFailure, - .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, - .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory, - .WSAHOST_NOT_FOUND => return error.UnknownHostName, - .WSATYPE_NOT_FOUND => return error.ServiceUnavailable, - .WSAEINVAL => unreachable, - .WSAESOCKTNOSUPPORT => unreachable, - else => |err| return os.windows.unexpectedWSAError(err), - } else switch (rc) { + switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { @intToEnum(sys.EAI, 0) => {}, .ADDRFAMILY => return error.HostLacksNetworkAddresses, .AGAIN => return error.TemporaryNameServerFailure, @@ -824,6 +884,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A return result; } + if (builtin.target.os.tag == .linux) { const flags = std.c.AI.NUMERICSERV; const family = os.AF.UNSPEC; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 44b42375e1..a88b911bcb 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1296,6 +1296,23 @@ pub fn WSACleanup() !void { var wsa_startup_mutex: std.Thread.Mutex = .{}; +pub fn callWSAStartup() !void { + wsa_startup_mutex.lock(); + defer wsa_startup_mutex.unlock(); + + // Here we could use a flag to prevent multiple threads to prevent + // multiple calls to WSAStartup, but it doesn't matter. We're globally + // leaking the resource intentionally, and the mutex already prevents + // data races within the WSAStartup function. + _ = WSAStartup(2, 2) catch |err| switch (err) { + error.SystemNotAvailable => return error.SystemResources, + error.VersionNotSupported => return error.Unexpected, + error.BlockingOperationInProgress => return error.Unexpected, + error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, + error.Unexpected => return error.Unexpected, + }; +} + /// Microsoft requires WSAStartup to be called to initialize, or else /// WSASocketW will return WSANOTINITIALISED. /// Since this is a standard library, we do not have the luxury of @@ -1338,21 +1355,7 @@ pub fn WSASocketW( .WSANOTINITIALISED => { if (!first) return error.Unexpected; first = false; - - wsa_startup_mutex.lock(); - defer wsa_startup_mutex.unlock(); - - // Here we could use a flag to prevent multiple threads to prevent - // multiple calls to WSAStartup, but it doesn't matter. We're globally - // leaking the resource intentionally, and the mutex already prevents - // data races within the WSAStartup function. - _ = WSAStartup(2, 2) catch |err| switch (err) { - error.SystemNotAvailable => return error.SystemResources, - error.VersionNotSupported => return error.Unexpected, - error.BlockingOperationInProgress => return error.Unexpected, - error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, - error.Unexpected => return error.Unexpected, - }; + try callWSAStartup(); continue; }, else => |err| return unexpectedWSAError(err),