authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-01 15:33:05-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-06-01 15:33:05-04:00
logc6764fd254170a32f34a7c08341b90b926552389
treee1138d3a1deeded1bb23ace3e614103561087877
parent8f4bc772604e708a150b279ffbaff0076c4b212d
parent0d32f4acf715bfe053890a86de1acba47cdb1ddb
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5475 from marler8997/windowsDns

support name resolution on windows

6 files changed, 140 insertions(+), 10 deletions(-)

lib/std/c.zig+14
...@@ -194,6 +194,20 @@ pub usingnamespace switch (builtin.os.tag) {...@@ -194,6 +194,20 @@ pub usingnamespace switch (builtin.os.tag) {
194 pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;194 pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
195 pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;195 pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
196 },196 },
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 },
197 else => struct {211 else => struct {
198 pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;212 pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;
199 pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;213 pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;
lib/std/net.zig+20-7
...@@ -406,7 +406,8 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)...@@ -406,7 +406,8 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
406406
407pub fn tcpConnectToAddress(address: Address) !fs.File {407pub fn tcpConnectToAddress(address: Address) !fs.File {
408 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;408 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);
410 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);411 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
411 errdefer os.close(sockfd);412 errdefer os.close(sockfd);
412 try os.connect(sockfd, &address.any, address.getOsSockLen());413 try os.connect(sockfd, &address.any, address.getOsSockLen());
...@@ -431,16 +432,16 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -431,16 +432,16 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
431 const arena = &result.arena.allocator;432 const arena = &result.arena.allocator;
432 errdefer result.arena.deinit();433 errdefer result.arena.deinit();
433434
434 if (builtin.link_libc) {435 if (builtin.os.tag == .windows or builtin.link_libc) {
435 const c = std.c;
436 const name_c = try std.cstr.addNullByte(allocator, name);436 const name_c = try std.cstr.addNullByte(allocator, name);
437 defer allocator.free(name_c);437 defer allocator.free(name_c);
438438
439 const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port});439 const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port});
440 defer allocator.free(port_c);440 defer allocator.free(port_c);
441441
442 const sys = if (builtin.os.tag == .windows) os.windows.ws2_32 else os.system;
442 const hints = os.addrinfo{443 const hints = os.addrinfo{
443 .flags = c.AI_NUMERICSERV,444 .flags = sys.AI_NUMERICSERV,
444 .family = os.AF_UNSPEC,445 .family = os.AF_UNSPEC,
445 .socktype = os.SOCK_STREAM,446 .socktype = os.SOCK_STREAM,
446 .protocol = os.IPPROTO_TCP,447 .protocol = os.IPPROTO_TCP,
...@@ -450,8 +451,20 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -450,8 +451,20 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
450 .next = null,451 .next = null,
451 };452 };
452 var res: *os.addrinfo = undefined;453 var res: *os.addrinfo = undefined;
453 switch (os.system.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res)) {454 const rc = sys.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res);
454 @intToEnum(os.system.EAI, 0) => {},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) => {},
455 .ADDRFAMILY => return error.HostLacksNetworkAddresses,468 .ADDRFAMILY => return error.HostLacksNetworkAddresses,
456 .AGAIN => return error.TemporaryNameServerFailure,469 .AGAIN => return error.TemporaryNameServerFailure,
457 .BADFLAGS => unreachable, // Invalid hints470 .BADFLAGS => unreachable, // Invalid hints
...@@ -467,7 +480,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -467,7 +480,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
467 },480 },
468 else => unreachable,481 else => unreachable,
469 }482 }
470 defer os.system.freeaddrinfo(res);483 defer sys.freeaddrinfo(res);
471484
472 const addr_count = blk: {485 const addr_count = blk: {
473 var count: usize = 0;486 var count: usize = 0;
lib/std/net/test.zig+4-1
...@@ -68,7 +68,10 @@ test "parse and render IPv4 addresses" {...@@ -68,7 +68,10 @@ test "parse and render IPv4 addresses" {
68}68}
6969
70test "resolve DNS" {70test "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) {
72 // DNS resolution not implemented on Windows yet.75 // DNS resolution not implemented on Windows yet.
73 return error.SkipZigTest;76 return error.SkipZigTest;
74 }77 }
lib/std/os.zig+53-2
...@@ -69,6 +69,8 @@ else switch (builtin.os.tag) {...@@ -69,6 +69,8 @@ else switch (builtin.os.tag) {
6969
70pub usingnamespace @import("os/bits.zig");70pub usingnamespace @import("os/bits.zig");
7171
72pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
73
72/// See also `getenv`. Populated by startup code before main().74/// See also `getenv`. Populated by startup code before main().
73/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.75/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
74/// https://github.com/ziglang/zig/issues/452476/// https://github.com/ziglang/zig/issues/4524
...@@ -2445,7 +2447,33 @@ pub const SocketError = error{...@@ -2445,7 +2447,33 @@ pub const SocketError = error{
2445 SocketTypeNotSupported,2447 SocketTypeNotSupported,
2446} || UnexpectedError;2448} || UnexpectedError;
24472449
2448pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {2450pub 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
2449 const have_sock_flags = comptime !std.Target.current.isDarwin();2477 const have_sock_flags = comptime !std.Target.current.isDarwin();
2450 const filtered_sock_type = if (!have_sock_flags)2478 const filtered_sock_type = if (!have_sock_flags)
2451 socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC)2479 socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC)
...@@ -2820,7 +2848,30 @@ pub const ConnectError = error{...@@ -2820,7 +2848,30 @@ pub const ConnectError = error{
2820} || UnexpectedError;2848} || UnexpectedError;
28212849
2822/// Initiate a connection on a socket.2850/// Initiate a connection on a socket.
2823pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {2851pub 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
2824 while (true) {2875 while (true) {
2825 switch (errno(system.connect(sockfd, sock_addr, len))) {2876 switch (errno(system.connect(sockfd, sock_addr, len))) {
2826 0 => return,2877 0 => return,
lib/std/os/bits/windows.zig+11
...@@ -177,6 +177,8 @@ pub const sockaddr_un = ws2_32.sockaddr_un;...@@ -177,6 +177,8 @@ pub const sockaddr_un = ws2_32.sockaddr_un;
177pub const in6_addr = [16]u8;177pub const in6_addr = [16]u8;
178pub const in_addr = u32;178pub const in_addr = u32;
179179
180pub const addrinfo = ws2_32.addrinfo;
181
180pub const AF_UNSPEC = ws2_32.AF_UNSPEC;182pub const AF_UNSPEC = ws2_32.AF_UNSPEC;
181pub const AF_UNIX = ws2_32.AF_UNIX;183pub const AF_UNIX = ws2_32.AF_UNIX;
182pub const AF_INET = ws2_32.AF_INET;184pub const AF_INET = ws2_32.AF_INET;
...@@ -219,6 +221,15 @@ pub const SOCK_RAW = ws2_32.SOCK_RAW;...@@ -219,6 +221,15 @@ pub const SOCK_RAW = ws2_32.SOCK_RAW;
219pub const SOCK_RDM = ws2_32.SOCK_RDM;221pub const SOCK_RDM = ws2_32.SOCK_RDM;
220pub const SOCK_SEQPACKET = ws2_32.SOCK_SEQPACKET;222pub 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.
227pub 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.
231pub const SOCK_NONBLOCK = 0x20000;
232
222pub const IPPROTO_ICMP = ws2_32.IPPROTO_ICMP;233pub const IPPROTO_ICMP = ws2_32.IPPROTO_ICMP;
223pub const IPPROTO_IGMP = ws2_32.IPPROTO_IGMP;234pub const IPPROTO_IGMP = ws2_32.IPPROTO_IGMP;
224pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM;235pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM;
lib/std/os/windows/ws2_32.zig+38
...@@ -163,11 +163,35 @@ pub const IPPROTO_UDP = 17;...@@ -163,11 +163,35 @@ pub const IPPROTO_UDP = 17;
163pub const IPPROTO_ICMPV6 = 58;163pub const IPPROTO_ICMPV6 = 58;
164pub const IPPROTO_RM = 113;164pub const IPPROTO_RM = 113;
165165
166pub const AI_PASSIVE = 0x00001;
167pub const AI_CANONNAME = 0x00002;
168pub const AI_NUMERICHOST = 0x00004;
169pub const AI_NUMERICSERV = 0x00008;
170pub const AI_ADDRCONFIG = 0x00400;
171pub const AI_V4MAPPED = 0x00800;
172pub const AI_NON_AUTHORITATIVE = 0x04000;
173pub const AI_SECURE = 0x08000;
174pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
175pub const AI_DISABLE_IDN_ENCODING = 0x80000;
176
177pub const FIONBIO = -2147195266;
178
166pub const sockaddr = extern struct {179pub const sockaddr = extern struct {
167 family: ADDRESS_FAMILY,180 family: ADDRESS_FAMILY,
168 data: [14]u8,181 data: [14]u8,
169};182};
170183
184pub 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
171/// IPv4 socket address195/// IPv4 socket address
172pub const sockaddr_in = extern struct {196pub const sockaddr_in = extern struct {
173 family: ADDRESS_FAMILY = AF_INET,197 family: ADDRESS_FAMILY = AF_INET,
...@@ -752,3 +776,17 @@ pub extern "ws2_32" fn WSASendTo(...@@ -752,3 +776,17 @@ pub extern "ws2_32" fn WSASendTo(
752 lpOverlapped: ?*WSAOVERLAPPED,776 lpOverlapped: ?*WSAOVERLAPPED,
753 lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,777 lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
754) callconv(.Stdcall) c_int;778) callconv(.Stdcall) c_int;
779pub 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;
785pub extern "ws2_32" fn freeaddrinfo(
786 pAddrInfo: *addrinfo,
787) callconv(.Stdcall) void;
788pub extern "ws2_32" fn ioctlsocket(
789 s: SOCKET,
790 cmd: c_long,
791 argp: *c_ulong,
792) callconv(.Stdcall) c_int;