authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 02:19:22-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 02:19:22-04:00
log67058b9b7089446e16eee3c03ab3f8f9a5d13529
tree6c70752e5ad29ce53088f30fa431833c58f36b04
parentd5865f5319305b6d06502b64772f8207bae2d6a5
signaturelock-open Commit is signed but in an unrecognized format.

basic DNS address resolution for linux without libc


9 files changed, 805 insertions(+), 142 deletions(-)

lib/std/buffer.zig+8-8
...@@ -72,11 +72,11 @@ pub const Buffer = struct {...@@ -72,11 +72,11 @@ pub const Buffer = struct {
72 self.list.deinit();72 self.list.deinit();
73 }73 }
7474
75 pub fn toSlice(self: *const Buffer) []u8 {75 pub fn toSlice(self: Buffer) []u8 {
76 return self.list.toSlice()[0..self.len()];76 return self.list.toSlice()[0..self.len()];
77 }77 }
7878
79 pub fn toSliceConst(self: *const Buffer) []const u8 {79 pub fn toSliceConst(self: Buffer) []const u8 {
80 return self.list.toSliceConst()[0..self.len()];80 return self.list.toSliceConst()[0..self.len()];
81 }81 }
8282
...@@ -91,11 +91,11 @@ pub const Buffer = struct {...@@ -91,11 +91,11 @@ pub const Buffer = struct {
91 self.list.items[self.len()] = 0;91 self.list.items[self.len()] = 0;
92 }92 }
9393
94 pub fn isNull(self: *const Buffer) bool {94 pub fn isNull(self: Buffer) bool {
95 return self.list.len == 0;95 return self.list.len == 0;
96 }96 }
9797
98 pub fn len(self: *const Buffer) usize {98 pub fn len(self: Buffer) usize {
99 return self.list.len - 1;99 return self.list.len - 1;
100 }100 }
101101
...@@ -111,16 +111,16 @@ pub const Buffer = struct {...@@ -111,16 +111,16 @@ pub const Buffer = struct {
111 self.list.toSlice()[old_len] = byte;111 self.list.toSlice()[old_len] = byte;
112 }112 }
113113
114 pub fn eql(self: *const Buffer, m: []const u8) bool {114 pub fn eql(self: Buffer, m: []const u8) bool {
115 return mem.eql(u8, self.toSliceConst(), m);115 return mem.eql(u8, self.toSliceConst(), m);
116 }116 }
117117
118 pub fn startsWith(self: *const Buffer, m: []const u8) bool {118 pub fn startsWith(self: Buffer, m: []const u8) bool {
119 if (self.len() < m.len) return false;119 if (self.len() < m.len) return false;
120 return mem.eql(u8, self.list.items[0..m.len], m);120 return mem.eql(u8, self.list.items[0..m.len], m);
121 }121 }
122122
123 pub fn endsWith(self: *const Buffer, m: []const u8) bool {123 pub fn endsWith(self: Buffer, m: []const u8) bool {
124 const l = self.len();124 const l = self.len();
125 if (l < m.len) return false;125 if (l < m.len) return false;
126 const start = l - m.len;126 const start = l - m.len;
...@@ -133,7 +133,7 @@ pub const Buffer = struct {...@@ -133,7 +133,7 @@ pub const Buffer = struct {
133 }133 }
134134
135 /// For passing to C functions.135 /// For passing to C functions.
136 pub fn ptr(self: *const Buffer) [*]u8 {136 pub fn ptr(self: Buffer) [*]u8 {
137 return self.list.items.ptr;137 return self.list.items.ptr;
138 }138 }
139};139};
lib/std/c.zig+22
...@@ -116,6 +116,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add...@@ -116,6 +116,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add
116pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;116pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
117pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;117pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
118pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;118pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
119pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
120pub extern "c" fn sendto(
121 sockfd: fd_t,
122 buf: *const c_void,
123 len: usize,
124 flags: u32,
125 dest_addr: *const sockaddr,
126 addrlen: socklen_t,
127) isize;
128
129pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
130pub extern fn recvfrom(
131 sockfd: fd_t,
132 noalias buf: *c_void,
133 len: usize,
134 flags: u32,
135 noalias src_addr: ?*sockaddr,
136 noalias addrlen: ?*socklen_t,
137) isize;
138
119pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;139pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
120pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;140pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
121pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;141pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
...@@ -169,3 +189,5 @@ pub extern "c" fn getnameinfo(...@@ -169,3 +189,5 @@ pub extern "c" fn getnameinfo(
169) c_int;189) c_int;
170190
171pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8;191pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8;
192
193pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int;
lib/std/event/loop.zig+4
...@@ -466,6 +466,10 @@ pub const Loop = struct {...@@ -466,6 +466,10 @@ pub const Loop = struct {
466 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);466 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
467 }467 }
468468
469 pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) !void {
470 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
471 }
472
469 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {473 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {
470 var resume_node = ResumeNode.Basic{474 var resume_node = ResumeNode.Basic{
471 .base = ResumeNode{475 .base = ResumeNode{
lib/std/fs.zig+1-1
...@@ -704,7 +704,7 @@ pub const Dir = struct {...@@ -704,7 +704,7 @@ pub const Dir = struct {
704704
705 /// Call `File.close` on the result when done.705 /// Call `File.close` on the result when done.
706 pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {706 pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {
707 const flags = os.O_LARGEFILE | os.O_RDONLY;707 const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
708 const fd = try os.openatC(self.fd, sub_path, flags, 0);708 const fd = try os.openatC(self.fd, sub_path, flags, 0);
709 return File.openHandle(fd);709 return File.openHandle(fd);
710 }710 }
lib/std/fs/file.zig+1-1
...@@ -41,7 +41,7 @@ pub const File = struct {...@@ -41,7 +41,7 @@ pub const File = struct {
41 const path_w = try windows.cStrToPrefixedFileW(path);41 const path_w = try windows.cStrToPrefixedFileW(path);
42 return openReadW(&path_w);42 return openReadW(&path_w);
43 }43 }
44 const flags = os.O_LARGEFILE | os.O_RDONLY;44 const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
45 const fd = try os.openC(path, flags, 0);45 const fd = try os.openC(path, flags, 0);
46 return openHandle(fd);46 return openHandle(fd);
47 }47 }
lib/std/net.zig+476-124
...@@ -4,6 +4,7 @@ const assert = std.debug.assert;...@@ -4,6 +4,7 @@ const assert = std.debug.assert;
4const net = @This();4const net = @This();
5const mem = std.mem;5const mem = std.mem;
6const os = std.os;6const os = std.os;
7const fs = std.fs;
78
8pub const TmpWinAddr = struct {9pub const TmpWinAddr = struct {
9 family: u8,10 family: u8,
...@@ -285,7 +286,7 @@ test "std.net.parseIp6" {...@@ -285,7 +286,7 @@ test "std.net.parseIp6" {
285 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));286 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
286}287}
287288
288pub fn connectUnixSocket(path: []const u8) !std.fs.File {289pub fn connectUnixSocket(path: []const u8) !fs.File {
289 const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;290 const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
290 const sockfd = try os.socket(291 const sockfd = try os.socket(
291 os.AF_UNIX,292 os.AF_UNIX,
...@@ -312,7 +313,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File {...@@ -312,7 +313,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File {
312 try os.connect(sockfd, &sock_addr, size);313 try os.connect(sockfd, &sock_addr, size);
313 }314 }
314315
315 return std.fs.File.openHandle(sockfd);316 return fs.File.openHandle(sockfd);
316}317}
317318
318pub const AddressList = struct {319pub const AddressList = struct {
...@@ -356,7 +357,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -356,7 +357,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
356357
357 const hints = os.addrinfo{358 const hints = os.addrinfo{
358 .flags = c.AI_NUMERICSERV,359 .flags = c.AI_NUMERICSERV,
359 .family = os.AF_UNSPEC,360 .family = os.AF_INET, // TODO os.AF_UNSPEC,
360 .socktype = os.SOCK_STREAM,361 .socktype = os.SOCK_STREAM,
361 .protocol = os.IPPROTO_TCP,362 .protocol = os.IPPROTO_TCP,
362 .canonname = null,363 .canonname = null,
...@@ -413,40 +414,41 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -413,40 +414,41 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
413 return result;414 return result;
414 }415 }
415 if (builtin.os == .linux) {416 if (builtin.os == .linux) {
416 const flags = os.AI_NUMERICSERV;417 const flags = std.c.AI_NUMERICSERV;
417 const family = os.AF_INET; //TODO os.AF_UNSPEC;418 const family = os.AF_INET; //TODO os.AF_UNSPEC;
418 // The limit of 48 results is a non-sharp bound on the number of addresses419 // The limit of 48 results is a non-sharp bound on the number of addresses
419 // that can fit in one 512-byte DNS packet full of v4 results and a second420 // that can fit in one 512-byte DNS packet full of v4 results and a second
420 // packet full of v6 results. Due to headers, the actual limit is lower.421 // packet full of v6 results. Due to headers, the actual limit is lower.
421 var buf: [48]LookupAddr = undefined;422 var addrs = std.ArrayList(LookupAddr).init(allocator);
422 var canon_buf: [256]u8 = undefined;423 defer addrs.deinit();
423 var canon_len: usize = 0;
424 const cnt = try linuxLookupName(buf[0..], &canon_buf, &canon_len, name, family, flags);
425424
426 result.addrs = try arena.alloc(Address, cnt);425 var canon = std.Buffer.initNull(allocator);
426 defer canon.deinit();
427427
428 if (canon_len != 0) {428 try linuxLookupName(&addrs, &canon, name, family, flags);
429 result.canon_name = try mem.dupe(arena, u8, canon_buf[0..canon_len]);429
430 result.addrs = try arena.alloc(Address, addrs.len);
431 if (!canon.isNull()) {
432 result.canon_name = canon.toOwnedSlice();
430 }433 }
431434
432 var i: usize = 0;435 for (addrs.toSliceConst()) |addr, i| {
433 while (i < cnt) : (i += 1) {436 const os_addr = if (addr.family == os.AF_INET6)
434 const os_addr = if (buf[i].family == os.AF_INET6)
435 os.sockaddr{437 os.sockaddr{
436 .in6 = os.sockaddr_in6{438 .in6 = os.sockaddr_in6{
437 .family = buf[i].family,439 .family = addr.family,
438 .port = mem.nativeToBig(u16, port),440 .port = mem.nativeToBig(u16, port),
439 .flowinfo = 0,441 .flowinfo = 0,
440 .addr = buf[i].addr,442 .addr = addr.addr,
441 .scope_id = buf[i].scope_id,443 .scope_id = addr.scope_id,
442 },444 },
443 }445 }
444 else446 else
445 os.sockaddr{447 os.sockaddr{
446 .in = os.sockaddr_in{448 .in = os.sockaddr_in{
447 .family = buf[i].family,449 .family = addr.family,
448 .port = mem.nativeToBig(u16, port),450 .port = mem.nativeToBig(u16, port),
449 .addr = @ptrCast(*align(1) u32, &buf[i].addr).*,451 .addr = @ptrCast(*align(1) const u32, &addr.addr).*,
450 .zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },452 .zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
451 },453 },
452 };454 };
...@@ -466,51 +468,52 @@ const LookupAddr = struct {...@@ -466,51 +468,52 @@ const LookupAddr = struct {
466};468};
467469
468fn linuxLookupName(470fn linuxLookupName(
469 buf: []LookupAddr,471 addrs: *std.ArrayList(LookupAddr),
470 canon_buf: []u8,472 canon: *std.Buffer,
471 canon_len: *usize,
472 opt_name: ?[]const u8,473 opt_name: ?[]const u8,
473 family: i32,474 family: i32,
474 flags: u32,475 flags: u32,
475) !usize {476) !void {
476 var cnt: usize = 0;
477 if (opt_name) |name| {477 if (opt_name) |name| {
478 // reject empty name and check len so it fits into temp bufs478 // reject empty name and check len so it fits into temp bufs
479 if (name.len >= 254) return error.UnknownName;479 try canon.replaceContents(name);
480 mem.copy(u8, canon_buf, name);480 try linuxLookupNameFromNumeric(addrs, name, family);
481 canon_len.* = name.len;481 if (addrs.len == 0 and (flags & std.c.AI_NUMERICHOST) == 0) {
482482 try linuxLookupNameFromHosts(addrs, canon, name, family);
483 cnt = (linuxLookupNameFromNumeric(buf, name, family) catch |err| switch (err) {483 if (addrs.len == 0) {
484 error.ExpectedIPv6ButFoundIPv4 => unreachable,484 try linuxLookupNameFromDnsSearch(addrs, canon, name, family);
485 error.ExpectedIPv4ButFoundIPv6 => unreachable,
486 });
487 if (cnt == 0 and (flags & os.AI_NUMERICHOST) == 0) {
488 cnt = try linuxLookupNameFromHosts(buf, canon_buf, canon_len, name, family);
489 if (cnt == 0) {
490 cnt = try linuxLookupNameFromDnsSearch(buf, canon_buf, canon_len, name, family);
491 }485 }
492 }486 }
493 } else {487 } else {
494 canon_len.* = 0;488 try canon.resize(0);
495 cnt = linuxLookupNameFromNull(buf, family, flags);489 try linuxLookupNameFromNull(addrs, family, flags);
496 }490 }
497 if (cnt == 0) return error.UnknownName;491 if (addrs.len == 0) return error.UnknownName;
498492
499 // No further processing is needed if there are fewer than 2493 // No further processing is needed if there are fewer than 2
500 // results or if there are only IPv4 results.494 // results or if there are only IPv4 results.
501 if (cnt == 1 or family == os.AF_INET) return cnt;495 if (addrs.len == 1 or family == os.AF_INET) return;
502496
503 @panic("port the RFC 3484/6724 destination address selection from musl libc");497 @panic("port the RFC 3484/6724 destination address selection from musl libc");
504}498}
505499
506fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32) !usize {500fn linuxLookupNameFromNumericUnspec(addrs: *std.ArrayList(LookupAddr), name: []const u8) !void {
501 return linuxLookupNameFromNumeric(addrs, name, os.AF_UNSPEC) catch |err| switch (err) {
502 error.ExpectedIPv6ButFoundIPv4 => unreachable,
503 error.ExpectedIPv4ButFoundIPv6 => unreachable,
504 else => |e| return e,
505 };
506}
507
508fn linuxLookupNameFromNumeric(addrs: *std.ArrayList(LookupAddr), name: []const u8, family: i32) !void {
507 if (parseIp4(name)) |ip4| {509 if (parseIp4(name)) |ip4| {
508 if (family == os.AF_INET6) return error.ExpectedIPv6ButFoundIPv4;510 if (family == os.AF_INET6) return error.ExpectedIPv6ButFoundIPv4;
511 const item = try addrs.addOne();
509 // TODO [0..4] should return *[4]u8, making this pointer cast unnecessary512 // TODO [0..4] should return *[4]u8, making this pointer cast unnecessary
510 mem.writeIntNative(u32, @ptrCast(*[4]u8, &buf[0].addr), ip4);513 mem.writeIntNative(u32, @ptrCast(*[4]u8, &item.addr), ip4);
511 buf[0].family = os.AF_INET;514 item.family = os.AF_INET;
512 buf[0].scope_id = 0;515 item.scope_id = 0;
513 return 1;516 return;
514 } else |err| switch (err) {517 } else |err| switch (err) {
515 error.Overflow,518 error.Overflow,
516 error.InvalidEnd,519 error.InvalidEnd,
...@@ -521,10 +524,11 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)...@@ -521,10 +524,11 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)
521524
522 if (parseIp6(name)) |ip6| {525 if (parseIp6(name)) |ip6| {
523 if (family == os.AF_INET) return error.ExpectedIPv4ButFoundIPv6;526 if (family == os.AF_INET) return error.ExpectedIPv4ButFoundIPv6;
524 @memcpy(&buf[0].addr, &ip6.addr, 16);527 const item = try addrs.addOne();
525 buf[0].family = os.AF_INET6;528 @memcpy(&item.addr, &ip6.addr, 16);
526 buf[0].scope_id = ip6.scope_id;529 item.family = os.AF_INET6;
527 return 1;530 item.scope_id = ip6.scope_id;
531 return;
528 } else |err| switch (err) {532 } else |err| switch (err) {
529 error.Overflow,533 error.Overflow,
530 error.InvalidEnd,534 error.InvalidEnd,
...@@ -532,64 +536,54 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)...@@ -532,64 +536,54 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)
532 error.Incomplete,536 error.Incomplete,
533 => {},537 => {},
534 }538 }
535
536 return 0;
537}539}
538540
539fn linuxLookupNameFromNull(buf: []LookupAddr, family: i32, flags: u32) usize {541fn linuxLookupNameFromNull(addrs: *std.ArrayList(LookupAddr), family: i32, flags: u32) !void {
540 var cnt: usize = 0;542 if ((flags & std.c.AI_PASSIVE) != 0) {
541 if ((flags & os.AI_PASSIVE) != 0) {
542 if (family != os.AF_INET6) {543 if (family != os.AF_INET6) {
543 buf[cnt] = LookupAddr{544 (try addrs.addOne()).* = LookupAddr{
544 .family = os.AF_INET,545 .family = os.AF_INET,
545 .addr = [1]u8{0} ** 16,546 .addr = [1]u8{0} ** 16,
546 };547 };
547 cnt += 1;
548 }548 }
549 if (family != os.AF_INET) {549 if (family != os.AF_INET) {
550 buf[cnt] = LookupAddr{550 (try addrs.addOne()).* = LookupAddr{
551 .family = os.AF_INET6,551 .family = os.AF_INET6,
552 .addr = [1]u8{0} ** 16,552 .addr = [1]u8{0} ** 16,
553 };553 };
554 cnt += 1;
555 }554 }
556 } else {555 } else {
557 if (family != os.AF_INET6) {556 if (family != os.AF_INET6) {
558 buf[cnt] = LookupAddr{557 (try addrs.addOne()).* = LookupAddr{
559 .family = os.AF_INET,558 .family = os.AF_INET,
560 .addr = [4]u8{ 127, 0, 0, 1 } ++ ([1]u8{0} ** 12),559 .addr = [4]u8{ 127, 0, 0, 1 } ++ ([1]u8{0} ** 12),
561 };560 };
562 cnt += 1;
563 }561 }
564 if (family != os.AF_INET) {562 if (family != os.AF_INET) {
565 buf[cnt] = LookupAddr{563 (try addrs.addOne()).* = LookupAddr{
566 .family = os.AF_INET6,564 .family = os.AF_INET6,
567 .addr = ([1]u8{0} ** 15) ++ [1]u8{1},565 .addr = ([1]u8{0} ** 15) ++ [1]u8{1},
568 };566 };
569 cnt += 1;
570 }567 }
571 }568 }
572 return cnt;
573}569}
574570
575fn linuxLookupNameFromHosts(571fn linuxLookupNameFromHosts(
576 buf: []LookupAddr,572 addrs: *std.ArrayList(LookupAddr),
577 canon_buf: []u8,573 canon: *std.Buffer,
578 canon_len: *usize,
579 name: []const u8,574 name: []const u8,
580 family: i32,575 family: i32,
581) !usize {576) !void {
582 const file = std.fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) {577 const file = fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) {
583 error.FileNotFound,578 error.FileNotFound,
584 error.NotDir,579 error.NotDir,
585 error.AccessDenied,580 error.AccessDenied,
586 => return 0,581 => return,
587 else => |e| return e,582 else => |e| return e,
588 };583 };
589 defer file.close();584 defer file.close();
590585
591 var cnt: usize = 0;586 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
592 const stream = &std.io.BufferedInStream(std.fs.File.ReadError).init(&file.inStream().stream).stream;
593 var line_buf: [512]u8 = undefined;587 var line_buf: [512]u8 = undefined;
594 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {588 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
595 error.StreamTooLong => blk: {589 error.StreamTooLong => blk: {
...@@ -612,26 +606,20 @@ fn linuxLookupNameFromHosts(...@@ -612,26 +606,20 @@ fn linuxLookupNameFromHosts(
612 }606 }
613 } else continue;607 } else continue;
614608
615 switch (linuxLookupNameFromNumeric(buf[cnt..], ip_text, family) catch |err| switch (err) {609 const prev_len = addrs.len;
610 linuxLookupNameFromNumeric(addrs, ip_text, family) catch |err| switch (err) {
616 error.ExpectedIPv6ButFoundIPv4 => continue,611 error.ExpectedIPv6ButFoundIPv4 => continue,
617 error.ExpectedIPv4ButFoundIPv6 => continue,612 error.ExpectedIPv4ButFoundIPv6 => continue,
618 }) {613 error.OutOfMemory => |e| return e,
619 0 => continue,614 };
620 1 => {615 if (addrs.len > prev_len) {
621 // first name is canonical name616 // first name is canonical name
622 const name_text = first_name_text.?;617 const name_text = first_name_text.?;
623 if (isValidHostName(name_text)) {618 if (isValidHostName(name_text)) {
624 mem.copy(u8, canon_buf, name_text);619 try canon.replaceContents(name_text);
625 canon_len.* = name_text.len;620 }
626 }
627
628 cnt += 1;
629 if (cnt == buf.len) break;
630 },
631 else => unreachable,
632 }621 }
633 }622 }
634 return cnt;
635}623}
636624
637pub fn isValidHostName(hostname: []const u8) bool {625pub fn isValidHostName(hostname: []const u8) bool {
...@@ -647,50 +635,414 @@ pub fn isValidHostName(hostname: []const u8) bool {...@@ -647,50 +635,414 @@ pub fn isValidHostName(hostname: []const u8) bool {
647}635}
648636
649fn linuxLookupNameFromDnsSearch(637fn linuxLookupNameFromDnsSearch(
650 buf: []LookupAddr,638 addrs: *std.ArrayList(LookupAddr),
651 canon_buf: []u8,639 canon: *std.Buffer,
652 canon_len: *usize,
653 name: []const u8,640 name: []const u8,
654 family: i32,641 family: i32,
655) !usize {642) !void {
656 var search: [256]u8 = undefined;643 var rc: ResolvConf = undefined;
657 const resolv_conf = try getResolvConf(&search);644 try getResolvConf(addrs.allocator, &rc);
645 defer rc.deinit();
658646
659 // Count dots, suppress search when >=ndots or name ends in647 // Count dots, suppress search when >=ndots or name ends in
660 // a dot, which is an explicit request for global scope.648 // a dot, which is an explicit request for global scope.
661 //var dots: usize = 0;649 var dots: usize = 0;
662 //for (name) |byte| {650 for (name) |byte| {
663 // if (byte == '.') dots += 1;651 if (byte == '.') dots += 1;
664 //}652 }
653
654 const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))
655 [_]u8{}
656 else
657 rc.search.toSliceConst();
658
659 var canon_name = name;
660
661 // Strip final dot for canon, fail if multiple trailing dots.
662 if (mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1;
663 if (mem.endsWith(u8, canon_name, ".")) return error.UnknownName;
664
665 // Name with search domain appended is setup in canon[]. This both
666 // provides the desired default canonical name (if the requested
667 // name is not a CNAME record) and serves as a buffer for passing
668 // the full requested name to name_from_dns.
669 try canon.resize(canon_name.len);
670 mem.copy(u8, canon.toSlice(), canon_name);
671 try canon.appendByte('.');
672
673 var tok_it = mem.tokenize(search, " \t");
674 while (tok_it.next()) |tok| {
675 canon.shrink(canon_name.len + 1);
676 try canon.append(tok);
677 try linuxLookupNameFromDns(addrs, canon, canon.toSliceConst(), family, rc);
678 if (addrs.len != 0) return;
679 }
680
681 canon.shrink(canon_name.len);
682 return linuxLookupNameFromDns(addrs, canon, name, family, rc);
683}
684
685const dpc_ctx = struct {
686 addrs: *std.ArrayList(LookupAddr),
687 canon: *std.Buffer,
688};
689
690fn linuxLookupNameFromDns(
691 addrs: *std.ArrayList(LookupAddr),
692 canon: *std.Buffer,
693 name: []const u8,
694 family: i32,
695 rc: ResolvConf,
696) !void {
697 var ctx = dpc_ctx{
698 .addrs = addrs,
699 .canon = canon,
700 };
701 const AfRr = struct {
702 af: i32,
703 rr: u8,
704 };
705 const afrrs = [_]AfRr{
706 AfRr{ .af = os.AF_INET6, .rr = os.RR_A },
707 AfRr{ .af = os.AF_INET, .rr = os.RR_AAAA },
708 };
709 var qbuf: [2][280]u8 = undefined;
710 var abuf: [2][512]u8 = undefined;
711 var qp: [2][]const u8 = undefined;
712 const apbuf = [2][]u8{ &abuf[0], &abuf[1] };
713 var nq: usize = 0;
714
715 for (afrrs) |afrr| {
716 if (family != afrr.af) {
717 const len = os.res_mkquery(0, name, 1, afrr.rr, [_]u8{}, null, &qbuf[nq]);
718 qp[nq] = qbuf[nq][0..len];
719 nq += 1;
720 }
721 }
722
723 var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] };
724 try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
725
726 var i: usize = 0;
727 while (i < nq) : (i += 1) {
728 dnsParse(ap[i], ctx, dnsParseCallback) catch {};
729 }
730
731 if (addrs.len != 0) return;
732 if (ap[0].len < 4 or (ap[0][3] & 15) == 2) return error.TemporaryNameServerFailure;
733 if ((ap[0][3] & 15) == 0) return error.UnknownName;
734 if ((ap[0][3] & 15) == 3) return;
735 return error.NameServerFailure;
736}
737
738const ResolvConf = struct {
739 attempts: u32,
740 ndots: u32,
741 timeout: u32,
742 search: std.Buffer,
743 ns: std.ArrayList(LookupAddr),
744
745 fn deinit(rc: *ResolvConf) void {
746 rc.ns.deinit();
747 rc.search.deinit();
748 rc.* = undefined;
749 }
750};
751
752/// Ignores lines longer than 512 bytes.
753/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
754fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
755 rc.* = ResolvConf{
756 .ns = std.ArrayList(LookupAddr).init(allocator),
757 .search = std.Buffer.initNull(allocator),
758 .ndots = 1,
759 .timeout = 5,
760 .attempts = 2,
761 };
762 errdefer rc.deinit();
763
764 const file = fs.File.openReadC(c"/etc/resolv.conf") catch |err| switch (err) {
765 error.FileNotFound,
766 error.NotDir,
767 error.AccessDenied,
768 => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1"),
769 else => |e| return e,
770 };
771 defer file.close();
772
773 var cnt: usize = 0;
774 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
775 var line_buf: [512]u8 = undefined;
776 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
777 error.StreamTooLong => blk: {
778 // Skip to the delimiter in the stream, to fix parsing
779 try stream.skipUntilDelimiterOrEof('\n');
780 // Give an empty line to the while loop, which will be skipped.
781 break :blk line_buf[0..0];
782 },
783 else => |e| return e,
784 }) |line| {
785 const no_comment_line = mem.separate(line, "#").next().?;
786 var line_it = mem.tokenize(no_comment_line, " \t");
787
788 const token = line_it.next() orelse continue;
789 if (mem.eql(u8, token, "options")) {
790 while (line_it.next()) |sub_tok| {
791 var colon_it = mem.separate(sub_tok, ":");
792 const name = colon_it.next().?;
793 const value_txt = colon_it.next() orelse continue;
794 const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
795 error.Overflow => 255,
796 error.InvalidCharacter => continue,
797 };
798 if (mem.eql(u8, name, "ndots")) {
799 rc.ndots = std.math.min(value, 15);
800 } else if (mem.eql(u8, name, "attempts")) {
801 rc.attempts = std.math.min(value, 10);
802 } else if (mem.eql(u8, name, "timeout")) {
803 rc.timeout = std.math.min(value, 60);
804 }
805 }
806 } else if (mem.eql(u8, token, "nameserver")) {
807 const ip_txt = line_it.next() orelse continue;
808 try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt);
809 } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
810 try rc.search.replaceContents(line_it.rest());
811 }
812 }
813
814 if (rc.ns.len == 0) {
815 return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1");
816 }
817}
818
819fn eqlSockAddr(a: *const os.sockaddr, b: *const os.sockaddr, len: usize) bool {
820 const a_bytes = @ptrCast([*]const u8, a)[0..len];
821 const b_bytes = @ptrCast([*]const u8, b)[0..len];
822 return mem.eql(u8, a_bytes, b_bytes);
823}
824
825fn resMSendRc(
826 queries: []const []const u8,
827 answers: [][]u8,
828 answer_bufs: []const []u8,
829 rc: ResolvConf,
830) !void {
831 const timeout = 1000 * rc.timeout;
832 const attempts = rc.attempts;
833
834 var sl: os.socklen_t = @sizeOf(os.sockaddr_in);
835 var family: os.sa_family_t = os.AF_INET;
836
837 var ns_list = std.ArrayList(os.sockaddr).init(rc.ns.allocator);
838 defer ns_list.deinit();
665839
666 //if (dots >= conf.ndots || name[l-1]=='.') *search = 0;840 try ns_list.resize(rc.ns.len);
667841 const ns = ns_list.toSlice();
668 //// Strip final dot for canon, fail if multiple trailing dots.842
669 //if (name[l-1]=='.') l--;843 for (rc.ns.toSliceConst()) |iplit, i| {
670 //if (!l || name[l-1]=='.') return EAI_NONAME;844 if (iplit.family == os.AF_INET) {
671845 ns[i] = os.sockaddr{
672 //// This can never happen; the caller already checked length.846 .in = os.sockaddr_in{
673 //if (l >= 256) return EAI_NONAME;847 .family = os.AF_INET,
674848 .port = mem.nativeToBig(u16, 53),
675 //// Name with search domain appended is setup in canon[]. This both849 .addr = mem.readIntNative(u32, @ptrCast(*const [4]u8, &iplit.addr)),
676 //// provides the desired default canonical name (if the requested850 .zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
677 //// name is not a CNAME record) and serves as a buffer for passing851 },
678 //// the full requested name to name_from_dns.852 };
679 //memcpy(canon, name, l);853 } else {
680 //canon[l] = '.';854 ns[i] = os.sockaddr{
681855 .in6 = os.sockaddr_in6{
682 //for (p=search; *p; p=z) {856 .family = os.AF_INET6,
683 // for (; isspace(*p); p++);857 .port = mem.nativeToBig(u16, 53),
684 // for (z=p; *z && !isspace(*z); z++);858 .flowinfo = 0,
685 // if (z==p) break;859 .addr = iplit.addr,
686 // if (z-p < 256 - l - 1) {860 .scope_id = iplit.scope_id,
687 // memcpy(canon+l+1, p, z-p);861 },
688 // canon[z-p+1+l] = 0;862 };
689 // int cnt = name_from_dns(buf, canon, canon, family, &conf);863 sl = @sizeOf(os.sockaddr_in6);
690 // if (cnt) return cnt;864 family = os.AF_INET6;
865 }
866 }
867
868 // Get local address and open/bind a socket
869 var sa: os.sockaddr = undefined;
870 @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(os.sockaddr));
871 sa.in.family = family;
872 const flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK;
873 const fd = os.socket(family, flags, 0) catch |err| switch (err) {
874 error.AddressFamilyNotSupported => blk: {
875 // Handle case where system lacks IPv6 support
876 if (family == os.AF_INET6) {
877 family = os.AF_INET;
878 break :blk try os.socket(os.AF_INET, flags, 0);
879 }
880 return err;
881 },
882 else => |e| return e,
883 };
884 defer os.close(fd);
885 try os.bind(fd, &sa, sl);
886
887 // Past this point, there are no errors. Each individual query will
888 // yield either no reply (indicated by zero length) or an answer
889 // packet which is up to the caller to interpret.
890
891 // Convert any IPv4 addresses in a mixed environment to v4-mapped
892 // TODO
893 //if (family == AF_INET6) {
894 // setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
895 // for (i=0; i<nns; i++) {
896 // if (ns[i].sin.sin_family != AF_INET) continue;
897 // memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
898 // &ns[i].sin.sin_addr, 4);
899 // memcpy(ns[i].sin6.sin6_addr.s6_addr,
900 // "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
901 // ns[i].sin6.sin6_family = AF_INET6;
902 // ns[i].sin6.sin6_flowinfo = 0;
903 // ns[i].sin6.sin6_scope_id = 0;
691 // }904 // }
692 //}905 //}
693906
694 //canon[l] = 0;907 var pfd = [1]os.pollfd{os.pollfd{
695 //return name_from_dns(buf, canon, name, family, &conf);908 .fd = fd,
909 .events = os.POLLIN,
910 .revents = undefined,
911 }};
912 const retry_interval = timeout / attempts;
913 var next: u32 = 0;
914 var t2: usize = std.time.milliTimestamp();
915 var t0 = t2;
916 var t1 = t2 - retry_interval;
917
918 var servfail_retry: usize = undefined;
919
920 outer: while (t2 - t0 < timeout) : (t2 = std.time.milliTimestamp()) {
921 if (t2 - t1 >= retry_interval) {
922 // Query all configured nameservers in parallel
923 var i: usize = 0;
924 while (i < queries.len) : (i += 1) {
925 if (answers[i].len == 0) {
926 var j: usize = 0;
927 while (j < ns.len) : (j += 1) {
928 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j], sl) catch undefined;
929 }
930 }
931 }
932 t1 = t2;
933 servfail_retry = 2 * queries.len;
934 }
935
936 // Wait for a response, or until time to retry
937 const clamped_timeout = std.math.min(u31(std.math.maxInt(u31)), t1 + retry_interval - t2);
938 const nevents = os.poll(&pfd, clamped_timeout) catch 0;
939 if (nevents == 0) continue;
940
941 while (true) {
942 var sl_copy = sl;
943 const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa, &sl_copy) catch break;
944
945 // Ignore non-identifiable packets
946 if (rlen < 4) continue;
947
948 // Ignore replies from addresses we didn't send to
949 var j: usize = 0;
950 while (j < ns.len and !eqlSockAddr(&ns[j], &sa, sl)) : (j += 1) {}
951 if (j == ns.len) continue;
952
953 // Find which query this answer goes with, if any
954 var i: usize = next;
955 while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
956 answer_bufs[next][1] != queries[i][1])) : (i += 1)
957 {}
958
959 if (i == queries.len) continue;
960 if (answers[i].len != 0) continue;
961
962 // Only accept positive or negative responses;
963 // retry immediately on server failure, and ignore
964 // all other codes such as refusal.
965 switch (answer_bufs[next][3] & 15) {
966 0, 3 => {},
967 2 => if (servfail_retry != 0) {
968 servfail_retry -= 1;
969 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j], sl) catch undefined;
970 },
971 else => continue,
972 }
973
974 // Store answer in the right slot, or update next
975 // available temp slot if it's already in place.
976 answers[i].len = rlen;
977 if (i == next) {
978 while (next < queries.len and answers[next].len != 0) : (next += 1) {}
979 } else {
980 mem.copy(u8, answer_bufs[i], answer_bufs[next][0..rlen]);
981 }
982
983 if (next == queries.len) break :outer;
984 }
985 }
986}
987
988fn dnsParse(
989 r: []const u8,
990 ctx: var,
991 comptime callback: var,
992) !void {
993 if (r.len < 12) return error.InvalidDnsPacket;
994 if ((r[3] & 15) != 0) return;
995 var p = r.ptr + 12;
996 var qdcount = r[4] * usize(256) + r[5];
997 var ancount = r[6] * usize(256) + r[7];
998 if (qdcount + ancount > 64) return error.InvalidDnsPacket;
999 while (qdcount != 0) {
1000 qdcount -= 1;
1001 while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1;
1002 if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6)
1003 return error.InvalidDnsPacket;
1004 p += usize(5) + @boolToInt(p[0] != 0);
1005 }
1006 while (ancount != 0) {
1007 ancount -= 1;
1008 while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1;
1009 if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6)
1010 return error.InvalidDnsPacket;
1011 p += usize(1) + @boolToInt(p[0] != 0);
1012 const len = p[8] * usize(256) + p[9];
1013 if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;
1014 try callback(ctx, p[1], p[10 .. 10 + len], r);
1015 p += 10 + len;
1016 }
1017}
1018
1019fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
1020 var tmp: [256]u8 = undefined;
1021 switch (rr) {
1022 os.RR_A => {
1023 if (data.len != 4) return error.InvalidDnsARecord;
1024 const new_addr = try ctx.addrs.addOne();
1025 new_addr.* = LookupAddr{
1026 .family = os.AF_INET,
1027 .addr = undefined,
1028 };
1029 mem.copy(u8, &new_addr.addr, data);
1030 },
1031 os.RR_AAAA => {
1032 if (data.len != 16) return error.InvalidDnsAAAARecord;
1033 const new_addr = try ctx.addrs.addOne();
1034 new_addr.* = LookupAddr{
1035 .family = os.AF_INET6,
1036 .addr = undefined,
1037 };
1038 mem.copy(u8, &new_addr.addr, data);
1039 },
1040 os.RR_CNAME => {
1041 @panic("TODO dn_expand");
1042 //if (__dn_expand(packet, (const unsigned char *)packet + 512,
1043 // data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp))
1044 // strcpy(ctx->canon, tmp);
1045 },
1046 else => return,
1047 }
696}1048}
lib/std/os.zig+251-8
...@@ -1508,16 +1508,17 @@ pub const SocketError = error{...@@ -1508,16 +1508,17 @@ pub const SocketError = error{
1508 ProtocolNotSupported,1508 ProtocolNotSupported,
1509} || UnexpectedError;1509} || UnexpectedError;
15101510
1511pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {1511pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
1512 const rc = system.socket(domain, socket_type, protocol);1512 const rc = system.socket(domain, socket_type, protocol);
1513 switch (errno(rc)) {1513 switch (errno(rc)) {
1514 0 => return @intCast(i32, rc),1514 0 => return @intCast(fd_t, rc),
1515 EACCES => return error.PermissionDenied,1515 EACCES => return error.PermissionDenied,
1516 EAFNOSUPPORT => return error.AddressFamilyNotSupported,1516 EAFNOSUPPORT => return error.AddressFamilyNotSupported,
1517 EINVAL => return error.ProtocolFamilyNotAvailable,1517 EINVAL => return error.ProtocolFamilyNotAvailable,
1518 EMFILE => return error.ProcessFdQuotaExceeded,1518 EMFILE => return error.ProcessFdQuotaExceeded,
1519 ENFILE => return error.SystemFdQuotaExceeded,1519 ENFILE => return error.SystemFdQuotaExceeded,
1520 ENOBUFS, ENOMEM => return error.SystemResources,1520 ENOBUFS => return error.SystemResources,
1521 ENOMEM => return error.SystemResources,
1521 EPROTONOSUPPORT => return error.ProtocolNotSupported,1522 EPROTONOSUPPORT => return error.ProtocolNotSupported,
1522 else => |err| return unexpectedErrno(err),1523 else => |err| return unexpectedErrno(err),
1523 }1524 }
...@@ -1559,17 +1560,17 @@ pub const BindError = error{...@@ -1559,17 +1560,17 @@ pub const BindError = error{
1559} || UnexpectedError;1560} || UnexpectedError;
15601561
1561/// addr is `*const T` where T is one of the sockaddr1562/// addr is `*const T` where T is one of the sockaddr
1562pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {1563pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
1563 const rc = system.bind(fd, addr, @sizeOf(sockaddr));1564 const rc = system.bind(sockfd, addr, len);
1564 switch (errno(rc)) {1565 switch (errno(rc)) {
1565 0 => return,1566 0 => return,
1566 EACCES => return error.AccessDenied,1567 EACCES => return error.AccessDenied,
1567 EADDRINUSE => return error.AddressInUse,1568 EADDRINUSE => return error.AddressInUse,
1568 EBADF => unreachable, // always a race condition if this error is returned1569 EBADF => unreachable, // always a race condition if this error is returned
1569 EINVAL => unreachable,1570 EINVAL => unreachable, // invalid parameters
1570 ENOTSOCK => unreachable,1571 ENOTSOCK => unreachable, // invalid `sockfd`
1571 EADDRNOTAVAIL => return error.AddressNotAvailable,1572 EADDRNOTAVAIL => return error.AddressNotAvailable,
1572 EFAULT => unreachable,1573 EFAULT => unreachable, // invalid `addr` pointer
1573 ELOOP => return error.SymLinkLoop,1574 ELOOP => return error.SymLinkLoop,
1574 ENAMETOOLONG => return error.NameTooLong,1575 ENAMETOOLONG => return error.NameTooLong,
1575 ENOENT => return error.FileNotFound,1576 ENOENT => return error.FileNotFound,
...@@ -2833,3 +2834,245 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {...@@ -2833,3 +2834,245 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
28332834
2834 @compileError("TODO implement gethostname for this OS");2835 @compileError("TODO implement gethostname for this OS");
2835}2836}
2837
2838pub fn res_mkquery(
2839 op: u4,
2840 dname: []const u8,
2841 class: u8,
2842 ty: u8,
2843 data: []const u8,
2844 newrr: ?[*]const u8,
2845 buf: []u8,
2846) usize {
2847 var name = dname;
2848 if (mem.endsWith(u8, name, ".")) name.len -= 1;
2849 assert(name.len <= 253);
2850 const n = 17 + name.len + @boolToInt(name.len != 0);
2851
2852 // Construct query template - ID will be filled later
2853 var q: [280]u8 = undefined;
2854 @memset(&q, 0, n);
2855 q[2] = u8(op) * 8 + 1;
2856 q[5] = 1;
2857 mem.copy(u8, q[13..], name);
2858 var i: usize = 13;
2859 var j: usize = undefined;
2860 while (q[i] != 0) : (i = j + 1) {
2861 j = i;
2862 while (q[j] != 0 and q[j] != '.') : (j += 1) {}
2863 // TODO determine the circumstances for this and whether or
2864 // not this should be an error.
2865 if (j - i - 1 > 62) unreachable;
2866 q[i - 1] = @intCast(u8, j - i);
2867 }
2868 q[i + 1] = ty;
2869 q[i + 3] = class;
2870
2871 // Make a reasonably unpredictable id
2872 var ts: timespec = undefined;
2873 clock_gettime(CLOCK_REALTIME, &ts) catch {};
2874 const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count);
2875 const unsec = @bitCast(UInt, ts.tv_nsec);
2876 const id = @truncate(u32, unsec + unsec / 65536);
2877 q[0] = @truncate(u8, id / 256);
2878 q[1] = @truncate(u8, id);
2879
2880 mem.copy(u8, buf, q[0..n]);
2881 return n;
2882}
2883
2884pub const SendError = error{
2885 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
2886 /// on the destination socket file, or search permission is denied for one of the
2887 /// directories the path prefix. (See path_resolution(7).)
2888 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
2889 /// it was a unicast address.
2890 AccessDenied,
2891
2892 /// The socket is marked nonblocking and the requested operation would block, and
2893 /// there is no global event loop configured.
2894 /// It's also possible to get this error under the following condition:
2895 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
2896 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
2897 /// determined that all port numbers in the ephemeral port range are currently in use. See
2898 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
2899 WouldBlock,
2900
2901 /// Another Fast Open is already in progress.
2902 FastOpenAlreadyInProgress,
2903
2904 /// Connection reset by peer.
2905 ConnectionResetByPeer,
2906
2907 /// The socket type requires that message be sent atomically, and the size of the message
2908 /// to be sent made this impossible. The message is not transmitted.
2909 ///
2910 MessageTooBig,
2911
2912 /// The output queue for a network interface was full. This generally indicates that the
2913 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
2914 /// this does not occur in Linux. Packets are just silently dropped when a device queue
2915 /// overflows.)
2916 /// This is also caused when there is not enough kernel memory available.
2917 SystemResources,
2918
2919 /// The local end has been shut down on a connection oriented socket. In this case, the
2920 /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
2921 BrokenPipe,
2922} || UnexpectedError;
2923
2924/// Transmit a message to another socket.
2925///
2926/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
2927/// recipient is known). The following call
2928///
2929/// send(sockfd, buf, len, flags);
2930///
2931/// is equivalent to
2932///
2933/// sendto(sockfd, buf, len, flags, NULL, 0);
2934///
2935/// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
2936/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
2937/// that the socket was actually connected.
2938/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
2939///
2940/// If the message is too long to pass atomically through the underlying protocol,
2941/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
2942///
2943/// There is no indication of failure to deliver.
2944///
2945/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
2946/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
2947/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
2948/// possible to send more data.
2949pub fn sendto(
2950 /// The file descriptor of the sending socket.
2951 sockfd: fd_t,
2952 /// Message to send.
2953 buf: []const u8,
2954 flags: u32,
2955 dest_addr: ?*const sockaddr,
2956 addrlen: socklen_t,
2957) SendError!usize {
2958 while (true) {
2959 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
2960 switch (errno(rc)) {
2961 0 => return rc,
2962
2963 EACCES => return error.AccessDenied,
2964 EAGAIN => if (std.event.Loop.instance) |loop| {
2965 loop.waitUntilFdWritable(sockfd) catch return error.WouldBlock;
2966 continue;
2967 } else {
2968 return error.WouldBlock;
2969 },
2970 EALREADY => return error.FastOpenAlreadyInProgress,
2971 EBADF => unreachable, // always a race condition
2972 ECONNRESET => return error.ConnectionResetByPeer,
2973 EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
2974 EFAULT => unreachable, // An invalid user space address was specified for an argument.
2975 EINTR => continue,
2976 EINVAL => unreachable, // Invalid argument passed.
2977 EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
2978 EMSGSIZE => return error.MessageTooBig,
2979 ENOBUFS => return error.SystemResources,
2980 ENOMEM => return error.SystemResources,
2981 ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
2982 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2983 EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
2984 EPIPE => return error.BrokenPipe,
2985 else => |err| return unexpectedErrno(err),
2986 }
2987 }
2988}
2989
2990/// Transmit a message to another socket.
2991///
2992/// The `send` call may be used only when the socket is in a connected state (so that the intended
2993/// recipient is known). The only difference between `send` and `write` is the presence of
2994/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
2995/// call
2996///
2997/// send(sockfd, buf, len, flags);
2998///
2999/// is equivalent to
3000///
3001/// sendto(sockfd, buf, len, flags, NULL, 0);
3002///
3003/// There is no indication of failure to deliver.
3004///
3005/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
3006/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
3007/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
3008/// possible to send more data.
3009pub fn send(
3010 /// The file descriptor of the sending socket.
3011 sockfd: fd_t,
3012 buf: []const u8,
3013 flags: u32,
3014) SendError!usize {
3015 return sendto(sockfd, buf, flags, null, 0);
3016}
3017
3018pub const PollError = error{
3019 /// The kernel had no space to allocate file descriptor tables.
3020 SystemResources,
3021} || UnexpectedError;
3022
3023pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
3024 while (true) {
3025 const rc = system.poll(fds.ptr, fds.len, timeout);
3026 switch (errno(rc)) {
3027 0 => return rc,
3028 EFAULT => unreachable,
3029 EINTR => continue,
3030 EINVAL => unreachable,
3031 ENOMEM => return error.SystemResources,
3032 else => |err| return unexpectedErrno(err),
3033 }
3034 }
3035}
3036
3037pub const RecvFromError = error{
3038 /// The socket is marked nonblocking and the requested operation would block, and
3039 /// there is no global event loop configured.
3040 WouldBlock,
3041
3042 /// A remote host refused to allow the network connection, typically because it is not
3043 /// running the requested service.
3044 ConnectionRefused,
3045
3046 /// Could not allocate kernel memory.
3047 SystemResources,
3048} || UnexpectedError;
3049
3050pub fn recvfrom(
3051 sockfd: fd_t,
3052 buf: []u8,
3053 flags: u32,
3054 src_addr: ?*sockaddr,
3055 addrlen: ?*socklen_t,
3056) RecvFromError!usize {
3057 while (true) {
3058 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
3059 switch (errno(rc)) {
3060 0 => return rc,
3061 EBADF => unreachable, // always a race condition
3062 EFAULT => unreachable,
3063 EINVAL => unreachable,
3064 ENOTCONN => unreachable,
3065 ENOTSOCK => unreachable,
3066 EINTR => continue,
3067 EAGAIN => if (std.event.Loop.instance) |loop| {
3068 loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
3069 continue;
3070 } else {
3071 return error.WouldBlock;
3072 },
3073 ENOMEM => return error.SystemResources,
3074 ECONNREFUSED => return error.ConnectionRefused,
3075 else => |err| return unexpectedErrno(err),
3076 }
3077 }
3078}
lib/std/os/bits/linux.zig+20
...@@ -1431,3 +1431,23 @@ pub const IPPROTO_UDPLITE = 136;...@@ -1431,3 +1431,23 @@ pub const IPPROTO_UDPLITE = 136;
1431pub const IPPROTO_MPLS = 137;1431pub const IPPROTO_MPLS = 137;
1432pub const IPPROTO_RAW = 255;1432pub const IPPROTO_RAW = 255;
1433pub const IPPROTO_MAX = 256;1433pub const IPPROTO_MAX = 256;
1434
1435pub const RR_A = 1;
1436pub const RR_CNAME = 5;
1437pub const RR_AAAA = 28;
1438
1439pub const nfds_t = usize;
1440pub const pollfd = extern struct {
1441 fd: fd_t,
1442 events: i16,
1443 revents: i16,
1444};
1445
1446pub const POLLIN = 0x001;
1447pub const POLLPRI = 0x002;
1448pub const POLLOUT = 0x004;
1449pub const POLLERR = 0x008;
1450pub const POLLHUP = 0x010;
1451pub const POLLNVAL = 0x020;
1452pub const POLLRDNORM = 0x040;
1453pub const POLLRDBAND = 0x080;
lib/std/os/linux.zig+22
...@@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize {...@@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize {
226 return syscall2(SYS_munmap, @ptrToInt(address), length);226 return syscall2(SYS_munmap, @ptrToInt(address), length);
227}227}
228228
229pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize {
230 if (@hasDecl(@This(), "SYS_poll")) {
231 return syscall3(SYS_poll, @ptrToInt(fds), n, @bitCast(u32, timeout));
232 } else {
233 return syscall6(
234 SYS_ppoll,
235 @ptrToInt(fds),
236 n,
237 @ptrToInt(if (timeout >= 0)
238 &timespec{
239 .tv_sec = timeout / 1000,
240 .tv_nsec = (timeout % 1000) * 1000000,
241 }
242 else
243 null),
244 0,
245 0,
246 NSIG / 8,
247 );
248 }
249}
250
229pub fn read(fd: i32, buf: [*]u8, count: usize) usize {251pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
230 return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);252 return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
231}253}