authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 16:10:14-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 16:10:14-04:00
log8d3b7689ad9c2dd14d0f5cadf2b711ff1ab70054
tree3f3b6a6bfb2bb750db66d0bfe373952ee05dcc4c
parent9ade31faaf74f5fc4291df17749ade508e53f734
signaturelock-open Commit is signed but in an unrecognized format.

std.net: port the RFC 3484/6724 destination...

...address selection from musl libc

2 files changed, 214 insertions(+), 13 deletions(-)

lib/std/net.zig+207-8
......@@ -357,7 +357,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
357357
358358 const hints = os.addrinfo{
359359 .flags = c.AI_NUMERICSERV,
360 .family = os.AF_INET, // TODO os.AF_UNSPEC,
360 .family = os.AF_UNSPEC,
361361 .socktype = os.SOCK_STREAM,
362362 .protocol = os.IPPROTO_TCP,
363363 .canonname = null,
......@@ -415,14 +415,11 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
415415 }
416416 if (builtin.os == .linux) {
417417 const flags = std.c.AI_NUMERICSERV;
418 const family = os.AF_INET; //TODO os.AF_UNSPEC;
419 // The limit of 48 results is a non-sharp bound on the number of addresses
420 // that can fit in one 512-byte DNS packet full of v4 results and a second
421 // packet full of v6 results. Due to headers, the actual limit is lower.
418 const family = os.AF_UNSPEC;
422419 var addrs = std.ArrayList(LookupAddr).init(allocator);
423420 defer addrs.deinit();
424421
425 var canon = std.Buffer.initNull(allocator);
422 var canon = std.Buffer.initNull(arena);
426423 defer canon.deinit();
427424
428425 try linuxLookupName(&addrs, &canon, name, family, flags);
......@@ -467,6 +464,14 @@ const LookupAddr = struct {
467464 sortkey: i32 = 0,
468465};
469466
467const DAS_USABLE = 0x40000000;
468const DAS_MATCHINGSCOPE = 0x20000000;
469const DAS_MATCHINGLABEL = 0x10000000;
470const DAS_PREC_SHIFT = 20;
471const DAS_SCOPE_SHIFT = 16;
472const DAS_PREFIX_SHIFT = 8;
473const DAS_ORDER_SHIFT = 0;
474
470475fn linuxLookupName(
471476 addrs: *std.ArrayList(LookupAddr),
472477 canon: *std.Buffer,
......@@ -493,8 +498,201 @@ fn linuxLookupName(
493498 // No further processing is needed if there are fewer than 2
494499 // results or if there are only IPv4 results.
495500 if (addrs.len == 1 or family == os.AF_INET) return;
501 const all_ip4 = for (addrs.toSliceConst()) |addr| {
502 if (addr.family != os.AF_INET) break false;
503 } else true;
504 if (all_ip4) return;
505
506 // The following implements a subset of RFC 3484/6724 destination
507 // address selection by generating a single 31-bit sort key for
508 // each address. Rules 3, 4, and 7 are omitted for having
509 // excessive runtime and code size cost and dubious benefit.
510 // So far the label/precedence table cannot be customized.
511 // This implementation is ported from musl libc.
512 // A more idiomatic "ziggy" implementation would be welcome.
513 for (addrs.toSlice()) |*addr, i| {
514 var key: i32 = 0;
515 var sa6: os.sockaddr_in6 = undefined;
516 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));
517 var da6 = os.sockaddr_in6{
518 .family = os.AF_INET6,
519 .scope_id = addr.scope_id,
520 .port = 65535,
521 .flowinfo = 0,
522 .addr = [1]u8{0} ** 16,
523 };
524 var sa4: os.sockaddr_in = undefined;
525 @memset(@ptrCast([*]u8, &sa4), 0, @sizeOf(os.sockaddr_in));
526 var da4 = os.sockaddr_in{
527 .family = os.AF_INET,
528 .port = 65535,
529 .addr = 0,
530 .zero = [1]u8{0} ** 8,
531 };
532 var sa: *os.sockaddr = undefined;
533 var da: *os.sockaddr = undefined;
534 var salen: os.socklen_t = undefined;
535 var dalen: os.socklen_t = undefined;
536 if (addr.family == os.AF_INET6) {
537 mem.copy(u8, &da6.addr, &addr.addr);
538 da = @ptrCast(*os.sockaddr, &da6);
539 dalen = @sizeOf(os.sockaddr_in6);
540 sa = @ptrCast(*os.sockaddr, &sa6);
541 salen = @sizeOf(os.sockaddr_in6);
542 } else {
543 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
544 mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
545 mem.copy(u8, da6.addr[12..], addr.addr[0..4]);
546 da4.addr = mem.readIntNative(u32, @ptrCast(*const [4]u8, &addr.addr));
547 da = @ptrCast(*os.sockaddr, &da4);
548 dalen = @sizeOf(os.sockaddr_in);
549 sa = @ptrCast(*os.sockaddr, &sa4);
550 salen = @sizeOf(os.sockaddr_in);
551 }
552 const dpolicy = policyOf(da6.addr);
553 const dscope: i32 = scopeOf(da6.addr);
554 const dlabel = dpolicy.label;
555 const dprec: i32 = dpolicy.prec;
556 const MAXADDRS = 3;
557 var prefixlen: i32 = 0;
558 if (os.socket(addr.family, os.SOCK_DGRAM | os.SOCK_CLOEXEC, os.IPPROTO_UDP)) |fd| syscalls: {
559 defer os.close(fd);
560 os.connect(fd, da, dalen) catch break :syscalls;
561 key |= DAS_USABLE;
562 os.getsockname(fd, sa, &salen) catch break :syscalls;
563 if (addr.family == os.AF_INET) {
564 // TODO sa6.addr[12..16] should return *[4]u8, making this cast unnecessary.
565 mem.writeIntNative(u32, @ptrCast(*[4]u8, &sa6.addr[12]), sa4.addr);
566 }
567 if (dscope == i32(scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE;
568 if (dlabel == labelOf(sa6.addr)) key |= DAS_MATCHINGLABEL;
569 prefixlen = prefixMatch(sa6.addr, da6.addr);
570 } else |_| {}
571 key |= dprec << DAS_PREC_SHIFT;
572 key |= (15 - dscope) << DAS_SCOPE_SHIFT;
573 key |= prefixlen << DAS_PREFIX_SHIFT;
574 key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT;
575 addr.sortkey = key;
576 }
577 std.sort.sort(LookupAddr, addrs.toSlice(), addrCmpLessThan);
578}
579
580const Policy = struct {
581 addr: [16]u8,
582 len: u8,
583 mask: u8,
584 prec: u8,
585 label: u8,
586};
587
588const defined_policies = [_]Policy{
589 Policy{
590 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
591 .len = 15,
592 .mask = 0xff,
593 .prec = 50,
594 .label = 0,
595 },
596 Policy{
597 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00",
598 .len = 11,
599 .mask = 0xff,
600 .prec = 35,
601 .label = 4,
602 },
603 Policy{
604 .addr = "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
605 .len = 1,
606 .mask = 0xff,
607 .prec = 30,
608 .label = 2,
609 },
610 Policy{
611 .addr = "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
612 .len = 3,
613 .mask = 0xff,
614 .prec = 5,
615 .label = 5,
616 },
617 Policy{
618 .addr = "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
619 .len = 0,
620 .mask = 0xfe,
621 .prec = 3,
622 .label = 13,
623 },
624 // These are deprecated and/or returned to the address
625 // pool, so despite the RFC, treating them as special
626 // is probably wrong.
627 // { "", 11, 0xff, 1, 3 },
628 // { "\xfe\xc0", 1, 0xc0, 1, 11 },
629 // { "\x3f\xfe", 1, 0xff, 1, 12 },
630 // Last rule must match all addresses to stop loop.
631 Policy{
632 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
633 .len = 0,
634 .mask = 0,
635 .prec = 40,
636 .label = 1,
637 },
638};
639
640fn policyOf(a: [16]u8) *const Policy {
641 for (defined_policies) |*policy| {
642 if (!mem.eql(u8, a[0..policy.len], policy.addr[0..policy.len])) continue;
643 if ((a[policy.len] & policy.mask) != policy.addr[policy.len]) continue;
644 return policy;
645 }
646 unreachable;
647}
648
649fn scopeOf(a: [16]u8) u8 {
650 if (IN6_IS_ADDR_MULTICAST(a)) return a[1] & 15;
651 if (IN6_IS_ADDR_LINKLOCAL(a)) return 2;
652 if (IN6_IS_ADDR_LOOPBACK(a)) return 2;
653 if (IN6_IS_ADDR_SITELOCAL(a)) return 5;
654 return 14;
655}
656
657fn prefixMatch(s: [16]u8, d: [16]u8) u8 {
658 // TODO: This FIXME inherited from porting from musl libc.
659 // I don't want this to go into zig std lib 1.0.0.
660
661 // FIXME: The common prefix length should be limited to no greater
662 // than the nominal length of the prefix portion of the source
663 // address. However the definition of the source prefix length is
664 // not clear and thus this limiting is not yet implemented.
665 var i: u8 = 0;
666 while (i < 128 and ((s[i / 8] ^ d[i / 8]) & (u8(128) >> @intCast(u3, i % 8))) == 0) : (i += 1) {}
667 return i;
668}
669
670fn labelOf(a: [16]u8) u8 {
671 return policyOf(a).label;
672}
673
674fn IN6_IS_ADDR_MULTICAST(a: [16]u8) bool {
675 return a[0] == 0xff;
676}
677
678fn IN6_IS_ADDR_LINKLOCAL(a: [16]u8) bool {
679 return a[0] == 0xfe and (a[1] & 0xc0) == 0x80;
680}
681
682fn IN6_IS_ADDR_LOOPBACK(a: [16]u8) bool {
683 return a[0] == 0 and a[1] == 0 and
684 a[2] == 0 and
685 a[12] == 0 and a[13] == 0 and
686 a[14] == 0 and a[15] == 1;
687}
688
689fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {
690 return a[0] == 0xfe and (a[1] & 0xc0) == 0xc0;
691}
496692
497 @panic("port the RFC 3484/6724 destination address selection from musl libc");
693// Parameters `b` and `a` swapped to make this descending.
694fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool {
695 return a.sortkey < b.sortkey;
498696}
499697
500698fn linuxLookupNameFromNumericUnspec(addrs: *std.ArrayList(LookupAddr), name: []const u8) !void {
......@@ -770,7 +968,6 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
770968 };
771969 defer file.close();
772970
773 var cnt: usize = 0;
774971 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
775972 var line_buf: [512]u8 = undefined;
776973 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
......@@ -990,6 +1187,8 @@ fn dnsParse(
9901187 ctx: var,
9911188 comptime callback: var,
9921189) !void {
1190 // This implementation is ported from musl libc.
1191 // A more idiomatic "ziggy" implementation would be welcome.
9931192 if (r.len < 12) return error.InvalidDnsPacket;
9941193 if ((r[3] & 15) != 0) return;
9951194 var p = r.ptr + 12;
lib/std/os.zig+7-5
......@@ -1808,11 +1808,9 @@ pub const GetSockNameError = error{
18081808 SystemResources,
18091809} || UnexpectedError;
18101810
1811pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr {
1812 var addr: sockaddr = undefined;
1813 var addrlen: socklen_t = @sizeOf(sockaddr);
1814 switch (errno(system.getsockname(sockfd, &addr, &addrlen))) {
1815 0 => return addr,
1811pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
1812 switch (errno(system.getsockname(sockfd, addr, addrlen))) {
1813 0 => return,
18161814 else => |err| return unexpectedErrno(err),
18171815
18181816 EBADF => unreachable, // always a race condition
......@@ -2844,6 +2842,8 @@ pub fn res_mkquery(
28442842 newrr: ?[*]const u8,
28452843 buf: []u8,
28462844) usize {
2845 // This implementation is ported from musl libc.
2846 // A more idiomatic "ziggy" implementation would be welcome.
28472847 var name = dname;
28482848 if (mem.endsWith(u8, name, ".")) name.len -= 1;
28492849 assert(name.len <= 253);
......@@ -3084,6 +3084,8 @@ pub fn dn_expand(
30843084 comp_dn: []const u8,
30853085 exp_dn: []u8,
30863086) DnExpandError!usize {
3087 // This implementation is ported from musl libc.
3088 // A more idiomatic "ziggy" implementation would be welcome.
30873089 var p = comp_dn.ptr;
30883090 var len: usize = std.math.maxInt(usize);
30893091 const end = msg.ptr + msg.len;