authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-16 17:11:07-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-17 00:08:42-07:00
log86308ba1e11a083f4ec91cf3b0e81a791892f851
tree735632091fefa922056cd0a497870799f629722b
parent62e3fdcf4faf6bdfdfd468d54b540eec74a8bdae

std.net.getAddressList: call WSAStartup on Windows


2 files changed, 93 insertions(+), 29 deletions(-)

lib/std/net.zig+75-14
...@@ -746,7 +746,79 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A...@@ -746,7 +746,79 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
746 const arena = result.arena.allocator();746 const arena = result.arena.allocator();
747 errdefer result.deinit();747 errdefer result.deinit();
748748
749 if (builtin.target.os.tag == .windows or builtin.link_libc) {749 if (builtin.target.os.tag == .windows) {
750 const name_c = try std.cstr.addNullByte(allocator, name);
751 defer allocator.free(name_c);
752
753 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
754 defer allocator.free(port_c);
755
756 const ws2_32 = os.windows.ws2_32;
757 const hints = os.addrinfo{
758 .flags = ws2_32.AI.NUMERICSERV,
759 .family = os.AF.UNSPEC,
760 .socktype = os.SOCK.STREAM,
761 .protocol = os.IPPROTO.TCP,
762 .canonname = null,
763 .addr = null,
764 .addrlen = 0,
765 .next = null,
766 };
767 var res: *os.addrinfo = undefined;
768 var first = true;
769 while (true) {
770 const rc = ws2_32.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);
771 switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
772 @intToEnum(os.windows.ws2_32.WinsockError, 0) => break,
773 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
774 .WSANO_RECOVERY => return error.NameServerFailure,
775 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
776 .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
777 .WSAHOST_NOT_FOUND => return error.UnknownHostName,
778 .WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
779 .WSAEINVAL => unreachable,
780 .WSAESOCKTNOSUPPORT => unreachable,
781 .WSANOTINITIALISED => {
782 if (!first) return error.Unexpected;
783 first = false;
784 try os.windows.callWSAStartup();
785 continue;
786 },
787 else => |err| return os.windows.unexpectedWSAError(err),
788 }
789 }
790 defer ws2_32.freeaddrinfo(res);
791
792 const addr_count = blk: {
793 var count: usize = 0;
794 var it: ?*os.addrinfo = res;
795 while (it) |info| : (it = info.next) {
796 if (info.addr != null) {
797 count += 1;
798 }
799 }
800 break :blk count;
801 };
802 result.addrs = try arena.alloc(Address, addr_count);
803
804 var it: ?*os.addrinfo = res;
805 var i: usize = 0;
806 while (it) |info| : (it = info.next) {
807 const addr = info.addr orelse continue;
808 result.addrs[i] = Address.initPosix(@alignCast(4, addr));
809
810 if (info.canonname) |n| {
811 if (result.canon_name == null) {
812 result.canon_name = try arena.dupe(u8, mem.sliceTo(n, 0));
813 }
814 }
815 i += 1;
816 }
817
818 return result;
819 }
820
821 if (builtin.link_libc) {
750 const name_c = try std.cstr.addNullByte(allocator, name);822 const name_c = try std.cstr.addNullByte(allocator, name);
751 defer allocator.free(name_c);823 defer allocator.free(name_c);
752824
...@@ -765,19 +837,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A...@@ -765,19 +837,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
765 .next = null,837 .next = null,
766 };838 };
767 var res: *os.addrinfo = undefined;839 var res: *os.addrinfo = undefined;
768 const rc = sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);840 switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
769 if (builtin.target.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
770 @intToEnum(os.windows.ws2_32.WinsockError, 0) => {},
771 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
772 .WSANO_RECOVERY => return error.NameServerFailure,
773 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
774 .WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
775 .WSAHOST_NOT_FOUND => return error.UnknownHostName,
776 .WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
777 .WSAEINVAL => unreachable,
778 .WSAESOCKTNOSUPPORT => unreachable,
779 else => |err| return os.windows.unexpectedWSAError(err),
780 } else switch (rc) {
781 @intToEnum(sys.EAI, 0) => {},841 @intToEnum(sys.EAI, 0) => {},
782 .ADDRFAMILY => return error.HostLacksNetworkAddresses,842 .ADDRFAMILY => return error.HostLacksNetworkAddresses,
783 .AGAIN => return error.TemporaryNameServerFailure,843 .AGAIN => return error.TemporaryNameServerFailure,
...@@ -824,6 +884,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A...@@ -824,6 +884,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
824884
825 return result;885 return result;
826 }886 }
887
827 if (builtin.target.os.tag == .linux) {888 if (builtin.target.os.tag == .linux) {
828 const flags = std.c.AI.NUMERICSERV;889 const flags = std.c.AI.NUMERICSERV;
829 const family = os.AF.UNSPEC;890 const family = os.AF.UNSPEC;
lib/std/os/windows.zig+18-15
...@@ -1296,6 +1296,23 @@ pub fn WSACleanup() !void {...@@ -1296,6 +1296,23 @@ pub fn WSACleanup() !void {
12961296
1297var wsa_startup_mutex: std.Thread.Mutex = .{};1297var wsa_startup_mutex: std.Thread.Mutex = .{};
12981298
1299pub fn callWSAStartup() !void {
1300 wsa_startup_mutex.lock();
1301 defer wsa_startup_mutex.unlock();
1302
1303 // Here we could use a flag to prevent multiple threads to prevent
1304 // multiple calls to WSAStartup, but it doesn't matter. We're globally
1305 // leaking the resource intentionally, and the mutex already prevents
1306 // data races within the WSAStartup function.
1307 _ = WSAStartup(2, 2) catch |err| switch (err) {
1308 error.SystemNotAvailable => return error.SystemResources,
1309 error.VersionNotSupported => return error.Unexpected,
1310 error.BlockingOperationInProgress => return error.Unexpected,
1311 error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
1312 error.Unexpected => return error.Unexpected,
1313 };
1314}
1315
1299/// Microsoft requires WSAStartup to be called to initialize, or else1316/// Microsoft requires WSAStartup to be called to initialize, or else
1300/// WSASocketW will return WSANOTINITIALISED.1317/// WSASocketW will return WSANOTINITIALISED.
1301/// Since this is a standard library, we do not have the luxury of1318/// Since this is a standard library, we do not have the luxury of
...@@ -1338,21 +1355,7 @@ pub fn WSASocketW(...@@ -1338,21 +1355,7 @@ pub fn WSASocketW(
1338 .WSANOTINITIALISED => {1355 .WSANOTINITIALISED => {
1339 if (!first) return error.Unexpected;1356 if (!first) return error.Unexpected;
1340 first = false;1357 first = false;
13411358 try callWSAStartup();
1342 wsa_startup_mutex.lock();
1343 defer wsa_startup_mutex.unlock();
1344
1345 // Here we could use a flag to prevent multiple threads to prevent
1346 // multiple calls to WSAStartup, but it doesn't matter. We're globally
1347 // leaking the resource intentionally, and the mutex already prevents
1348 // data races within the WSAStartup function.
1349 _ = WSAStartup(2, 2) catch |err| switch (err) {
1350 error.SystemNotAvailable => return error.SystemResources,
1351 error.VersionNotSupported => return error.Unexpected,
1352 error.BlockingOperationInProgress => return error.Unexpected,
1353 error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
1354 error.Unexpected => return error.Unexpected,
1355 };
1356 continue;1359 continue;
1357 },1360 },
1358 else => |err| return unexpectedWSAError(err),1361 else => |err| return unexpectedWSAError(err),