Skip to content

Commit c6764fd

Browse files
authored
Merge pull request #5475 from marler8997/windowsDns
support name resolution on windows
2 parents 8f4bc77 + 0d32f4a commit c6764fd

File tree

6 files changed

+140
-10
lines changed

6 files changed

+140
-10
lines changed

lib/std/c.zig

+14
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,20 @@ pub usingnamespace switch (builtin.os.tag) {
194194
pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
195195
pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
196196
},
197+
.windows => struct {
198+
// TODO: copied the else case and removed the socket function (because its in ws2_32)
199+
// need to verify which of these is actually supported on windows
200+
pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;
201+
pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;
202+
pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int;
203+
pub extern "c" fn getrusage(who: c_int, usage: *rusage) c_int;
204+
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
205+
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
206+
pub extern "c" fn sched_yield() c_int;
207+
pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
208+
pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
209+
pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
210+
},
197211
else => struct {
198212
pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;
199213
pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;

lib/std/net.zig

+20-7
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
406406

407407
pub fn tcpConnectToAddress(address: Address) !fs.File {
408408
const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
409-
const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
409+
const sock_flags = os.SOCK_STREAM | nonblock |
410+
(if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC);
410411
const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
411412
errdefer os.close(sockfd);
412413
try os.connect(sockfd, &address.any, address.getOsSockLen());
@@ -431,16 +432,16 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
431432
const arena = &result.arena.allocator;
432433
errdefer result.arena.deinit();
433434

434-
if (builtin.link_libc) {
435-
const c = std.c;
435+
if (builtin.os.tag == .windows or builtin.link_libc) {
436436
const name_c = try std.cstr.addNullByte(allocator, name);
437437
defer allocator.free(name_c);
438438

439439
const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port});
440440
defer allocator.free(port_c);
441441

442+
const sys = if (builtin.os.tag == .windows) os.windows.ws2_32 else os.system;
442443
const hints = os.addrinfo{
443-
.flags = c.AI_NUMERICSERV,
444+
.flags = sys.AI_NUMERICSERV,
444445
.family = os.AF_UNSPEC,
445446
.socktype = os.SOCK_STREAM,
446447
.protocol = os.IPPROTO_TCP,
@@ -450,8 +451,20 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
450451
.next = null,
451452
};
452453
var res: *os.addrinfo = undefined;
453-
switch (os.system.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res)) {
454-
@intToEnum(os.system.EAI, 0) => {},
454+
const rc = sys.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res);
455+
if (builtin.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
456+
@intToEnum(os.windows.ws2_32.WinsockError, 0) => {},
457+
.WSATRY_AGAIN => return error.TemporaryNameServerFailure,
458+
.WSANO_RECOVERY => return error.NameServerFailure,
459+
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
460+
.WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
461+
.WSAHOST_NOT_FOUND => return error.UnknownHostName,
462+
.WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
463+
.WSAEINVAL => unreachable,
464+
.WSAESOCKTNOSUPPORT => unreachable,
465+
else => |err| return os.windows.unexpectedWSAError(err),
466+
} else switch (rc) {
467+
@intToEnum(sys.EAI, 0) => {},
455468
.ADDRFAMILY => return error.HostLacksNetworkAddresses,
456469
.AGAIN => return error.TemporaryNameServerFailure,
457470
.BADFLAGS => unreachable, // Invalid hints
@@ -467,7 +480,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
467480
},
468481
else => unreachable,
469482
}
470-
defer os.system.freeaddrinfo(res);
483+
defer sys.freeaddrinfo(res);
471484

472485
const addr_count = blk: {
473486
var count: usize = 0;

lib/std/net/test.zig

+4-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ test "parse and render IPv4 addresses" {
6868
}
6969

7070
test "resolve DNS" {
71-
if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {
71+
if (std.builtin.os.tag == .windows) {
72+
_ = try std.os.windows.WSAStartup(2, 2);
73+
}
74+
if (builtin.os.tag == .wasi) {
7275
// DNS resolution not implemented on Windows yet.
7376
return error.SkipZigTest;
7477
}

lib/std/os.zig

+53-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ else switch (builtin.os.tag) {
6969

7070
pub usingnamespace @import("os/bits.zig");
7171

72+
pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
73+
7274
/// See also `getenv`. Populated by startup code before main().
7375
/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
7476
/// https://github.com/ziglang/zig/issues/4524
@@ -2445,7 +2447,33 @@ pub const SocketError = error{
24452447
SocketTypeNotSupported,
24462448
} || UnexpectedError;
24472449

2448-
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
2450+
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
2451+
if (builtin.os.tag == .windows) {
2452+
// NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations
2453+
const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
2454+
const flags : u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0;
2455+
const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type),
2456+
@intCast(c_int, protocol), null, 0, flags);
2457+
if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) {
2458+
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
2459+
.WSAENOBUFS => return error.SystemResources,
2460+
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
2461+
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
2462+
else => |err| return windows.unexpectedWSAError(err),
2463+
};
2464+
errdefer windows.closesocket(rc) catch unreachable;
2465+
if ((socket_type & SOCK_NONBLOCK) != 0) {
2466+
var mode : c_ulong = 1; // nonblocking
2467+
if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
2468+
switch (windows.ws2_32.WSAGetLastError()) {
2469+
// have not identified any error codes that should be handled yet
2470+
else => unreachable,
2471+
}
2472+
}
2473+
}
2474+
return rc;
2475+
}
2476+
24492477
const have_sock_flags = comptime !std.Target.current.isDarwin();
24502478
const filtered_sock_type = if (!have_sock_flags)
24512479
socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC)
@@ -2820,7 +2848,30 @@ pub const ConnectError = error{
28202848
} || UnexpectedError;
28212849

28222850
/// Initiate a connection on a socket.
2823-
pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
2851+
pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
2852+
if (builtin.os.tag == .windows) {
2853+
const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
2854+
if (rc == 0) return;
2855+
switch (windows.ws2_32.WSAGetLastError()) {
2856+
.WSAEADDRINUSE => return error.AddressInUse,
2857+
.WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
2858+
.WSAECONNREFUSED => return error.ConnectionRefused,
2859+
.WSAETIMEDOUT => return error.ConnectionTimedOut,
2860+
.WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?
2861+
,.WSAENETUNREACH => return error.NetworkUnreachable,
2862+
.WSAEFAULT => unreachable,
2863+
.WSAEINVAL => unreachable,
2864+
.WSAEISCONN => unreachable,
2865+
.WSAENOTSOCK => unreachable,
2866+
.WSAEWOULDBLOCK => unreachable,
2867+
.WSAEACCES => unreachable,
2868+
.WSAENOBUFS => return error.SystemResources,
2869+
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
2870+
else => |err| return windows.unexpectedWSAError(err),
2871+
}
2872+
return;
2873+
}
2874+
28242875
while (true) {
28252876
switch (errno(system.connect(sockfd, sock_addr, len))) {
28262877
0 => return,

lib/std/os/bits/windows.zig

+11
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ pub const sockaddr_un = ws2_32.sockaddr_un;
177177
pub const in6_addr = [16]u8;
178178
pub const in_addr = u32;
179179

180+
pub const addrinfo = ws2_32.addrinfo;
181+
180182
pub const AF_UNSPEC = ws2_32.AF_UNSPEC;
181183
pub const AF_UNIX = ws2_32.AF_UNIX;
182184
pub const AF_INET = ws2_32.AF_INET;
@@ -219,6 +221,15 @@ pub const SOCK_RAW = ws2_32.SOCK_RAW;
219221
pub const SOCK_RDM = ws2_32.SOCK_RDM;
220222
pub const SOCK_SEQPACKET = ws2_32.SOCK_SEQPACKET;
221223

224+
/// WARNING: this flag is not supported by windows socket functions directly,
225+
/// it is only supported by std.os.socket. Be sure that this value does
226+
/// not share any bits with any of the SOCK_* values.
227+
pub const SOCK_CLOEXEC = 0x10000;
228+
/// WARNING: this flag is not supported by windows socket functions directly,
229+
/// it is only supported by std.os.socket. Be sure that this value does
230+
/// not share any bits with any of the SOCK_* values.
231+
pub const SOCK_NONBLOCK = 0x20000;
232+
222233
pub const IPPROTO_ICMP = ws2_32.IPPROTO_ICMP;
223234
pub const IPPROTO_IGMP = ws2_32.IPPROTO_IGMP;
224235
pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM;

lib/std/os/windows/ws2_32.zig

+38
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,35 @@ pub const IPPROTO_UDP = 17;
163163
pub const IPPROTO_ICMPV6 = 58;
164164
pub const IPPROTO_RM = 113;
165165

166+
pub const AI_PASSIVE = 0x00001;
167+
pub const AI_CANONNAME = 0x00002;
168+
pub const AI_NUMERICHOST = 0x00004;
169+
pub const AI_NUMERICSERV = 0x00008;
170+
pub const AI_ADDRCONFIG = 0x00400;
171+
pub const AI_V4MAPPED = 0x00800;
172+
pub const AI_NON_AUTHORITATIVE = 0x04000;
173+
pub const AI_SECURE = 0x08000;
174+
pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
175+
pub const AI_DISABLE_IDN_ENCODING = 0x80000;
176+
177+
pub const FIONBIO = -2147195266;
178+
166179
pub const sockaddr = extern struct {
167180
family: ADDRESS_FAMILY,
168181
data: [14]u8,
169182
};
170183

184+
pub const addrinfo = extern struct {
185+
flags: i32,
186+
family: i32,
187+
socktype: i32,
188+
protocol: i32,
189+
addrlen: usize,
190+
canonname: ?[*:0]u8,
191+
addr: ?*sockaddr,
192+
next: ?*addrinfo,
193+
};
194+
171195
/// IPv4 socket address
172196
pub const sockaddr_in = extern struct {
173197
family: ADDRESS_FAMILY = AF_INET,
@@ -752,3 +776,17 @@ pub extern "ws2_32" fn WSASendTo(
752776
lpOverlapped: ?*WSAOVERLAPPED,
753777
lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
754778
) callconv(.Stdcall) c_int;
779+
pub extern "ws2_32" fn getaddrinfo(
780+
pNodeName: [*:0]const u8,
781+
pServiceName: [*:0]const u8,
782+
pHints: *const addrinfo,
783+
ppResult: **addrinfo,
784+
) callconv(.Stdcall) i32;
785+
pub extern "ws2_32" fn freeaddrinfo(
786+
pAddrInfo: *addrinfo,
787+
) callconv(.Stdcall) void;
788+
pub extern "ws2_32" fn ioctlsocket(
789+
s: SOCKET,
790+
cmd: c_long,
791+
argp: *c_ulong,
792+
) callconv(.Stdcall) c_int;

0 commit comments

Comments
 (0)