Skip to content

Commit

Permalink
std.net.getAddressList: call WSAStartup on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Jan 17, 2023
1 parent 749b335 commit 1eb311a
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 29 deletions.
89 changes: 75 additions & 14 deletions lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
33 changes: 18 additions & 15 deletions lib/std/os/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down

0 comments on commit 1eb311a

Please sign in to comment.