| ... | @@ -132,9 +132,58 @@ pub const HostName = struct { | ... | @@ -132,9 +132,58 @@ pub const HostName = struct { |
| 132 | } | 132 | } |
| 133 | | 133 | |
| 134 | fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult { | 134 | fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult { |
| 135 | _ = options; | 135 | const addresses = options.addresses_buffer[0..result.addresses_len]; |
| 136 | _ = result; | 136 | // No further processing is needed if there are fewer than 2 results or |
| 137 | @panic("TODO"); | 137 | // if there are only IPv4 results. |
| | 138 | if (addresses.len < 2) return result; |
| | 139 | const all_ip4 = for (addresses) |a| switch (a) { |
| | 140 | .ip4 => continue, |
| | 141 | .ip6 => break false, |
| | 142 | } else true; |
| | 143 | if (all_ip4) return result; |
| | 144 | |
| | 145 | // RFC 3484/6724 describes how destination address selection is |
| | 146 | // supposed to work. However, to implement it requires making a bunch |
| | 147 | // of networking syscalls, which is unnecessarily high latency, |
| | 148 | // especially if implemented serially. Furthermore, rules 3, 4, and 7 |
| | 149 | // have excessive runtime and code size cost and dubious benefit. |
| | 150 | // |
| | 151 | // Therefore, this logic sorts only using values available without |
| | 152 | // doing any syscalls, relying on the calling code to have a |
| | 153 | // meta-strategy such as attempting connection to multiple results at |
| | 154 | // once and keeping the fastest response while canceling the others. |
| | 155 | |
| | 156 | const S = struct { |
| | 157 | pub fn lessThan(s: @This(), lhs: IpAddress, rhs: IpAddress) bool { |
| | 158 | return sortKey(s, lhs) < sortKey(s, rhs); |
| | 159 | } |
| | 160 | |
| | 161 | fn sortKey(s: @This(), a: IpAddress) i32 { |
| | 162 | _ = s; |
| | 163 | var da6: Ip6Address = .{ |
| | 164 | .port = 65535, |
| | 165 | .bytes = undefined, |
| | 166 | }; |
| | 167 | switch (a) { |
| | 168 | .ip6 => |ip6| { |
| | 169 | da6.bytes = ip6.bytes; |
| | 170 | da6.scope_id = ip6.scope_id; |
| | 171 | }, |
| | 172 | .ip4 => |ip4| { |
| | 173 | da6.bytes[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*; |
| | 174 | da6.bytes[12..].* = ip4.bytes; |
| | 175 | }, |
| | 176 | } |
| | 177 | const da6_scope: i32 = da6.scope(); |
| | 178 | const da6_prec: i32 = da6.policy().prec; |
| | 179 | var key: i32 = 0; |
| | 180 | key |= da6_prec << 20; |
| | 181 | key |= (15 - da6_scope) << 16; |
| | 182 | return key; |
| | 183 | } |
| | 184 | }; |
| | 185 | std.mem.sort(IpAddress, addresses, @as(S, .{}), S.lessThan); |
| | 186 | return result; |
| 138 | } | 187 | } |
| 139 | | 188 | |
| 140 | fn lookupDns(io: Io, options: LookupOptions) !LookupResult { | 189 | fn lookupDns(io: Io, options: LookupOptions) !LookupResult { |
| ... | @@ -406,6 +455,14 @@ pub const Ip6Address = struct { | ... | @@ -406,6 +455,14 @@ pub const Ip6Address = struct { |
| 406 | Incomplete, | 455 | Incomplete, |
| 407 | }; | 456 | }; |
| 408 | | 457 | |
| | 458 | pub const Policy = struct { |
| | 459 | addr: [16]u8, |
| | 460 | len: u8, |
| | 461 | mask: u8, |
| | 462 | prec: u8, |
| | 463 | label: u8, |
| | 464 | }; |
| | 465 | |
| 409 | pub fn localhost(port: u16) Ip6Address { | 466 | pub fn localhost(port: u16) Ip6Address { |
| 410 | return .{ | 467 | return .{ |
| 411 | .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, | 468 | .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, |
| ... | @@ -597,6 +654,98 @@ pub const Ip6Address = struct { | ... | @@ -597,6 +654,98 @@ pub const Ip6Address = struct { |
| 597 | pub fn eql(a: Ip6Address, b: Ip6Address) bool { | 654 | pub fn eql(a: Ip6Address, b: Ip6Address) bool { |
| 598 | return a.port == b.port and std.mem.eql(u8, &a.bytes, &b.bytes); | 655 | return a.port == b.port and std.mem.eql(u8, &a.bytes, &b.bytes); |
| 599 | } | 656 | } |
| | 657 | |
| | 658 | pub fn isMultiCast(a: Ip6Address) bool { |
| | 659 | return a.bytes[0] == 0xff; |
| | 660 | } |
| | 661 | |
| | 662 | pub fn isLinkLocal(a: Ip6Address) bool { |
| | 663 | const b = &a.bytes; |
| | 664 | return b[0] == 0xfe and (b[1] & 0xc0) == 0x80; |
| | 665 | } |
| | 666 | |
| | 667 | pub fn isLoopBack(a: Ip6Address) bool { |
| | 668 | const b = &a.bytes; |
| | 669 | return b[0] == 0 and b[1] == 0 and |
| | 670 | b[2] == 0 and |
| | 671 | b[12] == 0 and b[13] == 0 and |
| | 672 | b[14] == 0 and b[15] == 1; |
| | 673 | } |
| | 674 | |
| | 675 | pub fn isSiteLocal(a: Ip6Address) bool { |
| | 676 | const b = &a.bytes; |
| | 677 | return b[0] == 0xfe and (b[1] & 0xc0) == 0xc0; |
| | 678 | } |
| | 679 | |
| | 680 | pub fn policy(a: Ip6Address) *const Policy { |
| | 681 | const b = &a.bytes; |
| | 682 | for (&defined_policies) |*p| { |
| | 683 | if (!std.mem.eql(u8, b[0..p.len], p.addr[0..p.len])) continue; |
| | 684 | if ((b[p.len] & p.mask) != p.addr[p.len]) continue; |
| | 685 | return p; |
| | 686 | } |
| | 687 | unreachable; |
| | 688 | } |
| | 689 | |
| | 690 | pub fn scope(a: Ip6Address) u8 { |
| | 691 | if (isMultiCast(a)) return a.bytes[1] & 15; |
| | 692 | if (isLinkLocal(a)) return 2; |
| | 693 | if (isLoopBack(a)) return 2; |
| | 694 | if (isSiteLocal(a)) return 5; |
| | 695 | return 14; |
| | 696 | } |
| | 697 | |
| | 698 | const defined_policies = [_]Policy{ |
| | 699 | .{ |
| | 700 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".*, |
| | 701 | .len = 15, |
| | 702 | .mask = 0xff, |
| | 703 | .prec = 50, |
| | 704 | .label = 0, |
| | 705 | }, |
| | 706 | .{ |
| | 707 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00".*, |
| | 708 | .len = 11, |
| | 709 | .mask = 0xff, |
| | 710 | .prec = 35, |
| | 711 | .label = 4, |
| | 712 | }, |
| | 713 | .{ |
| | 714 | .addr = "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, |
| | 715 | .len = 1, |
| | 716 | .mask = 0xff, |
| | 717 | .prec = 30, |
| | 718 | .label = 2, |
| | 719 | }, |
| | 720 | .{ |
| | 721 | .addr = "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, |
| | 722 | .len = 3, |
| | 723 | .mask = 0xff, |
| | 724 | .prec = 5, |
| | 725 | .label = 5, |
| | 726 | }, |
| | 727 | .{ |
| | 728 | .addr = "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, |
| | 729 | .len = 0, |
| | 730 | .mask = 0xfe, |
| | 731 | .prec = 3, |
| | 732 | .label = 13, |
| | 733 | }, |
| | 734 | // These are deprecated and/or returned to the address |
| | 735 | // pool, so despite the RFC, treating them as special |
| | 736 | // is probably wrong. |
| | 737 | // { "", 11, 0xff, 1, 3 }, |
| | 738 | // { "\xfe\xc0", 1, 0xc0, 1, 11 }, |
| | 739 | // { "\x3f\xfe", 1, 0xff, 1, 12 }, |
| | 740 | // Last rule must match all addresses to stop loop. |
| | 741 | .{ |
| | 742 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, |
| | 743 | .len = 0, |
| | 744 | .mask = 0, |
| | 745 | .prec = 40, |
| | 746 | .label = 1, |
| | 747 | }, |
| | 748 | }; |
| 600 | }; | 749 | }; |
| 601 | | 750 | |
| 602 | pub const Stream = struct { | 751 | pub const Stream = struct { |