| ... | ... | @@ -132,9 +132,58 @@ pub const HostName = struct { |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult { |
| 135 | | _ = options; |
| 136 | | _ = result; |
| 137 | | @panic("TODO"); |
| 135 | const addresses = options.addresses_buffer[0..result.addresses_len]; |
| 136 | // No further processing is needed if there are fewer than 2 results or |
| 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 | 189 | fn lookupDns(io: Io, options: LookupOptions) !LookupResult { |
| ... | ... | @@ -406,6 +455,14 @@ pub const Ip6Address = struct { |
| 406 | 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 | 466 | pub fn localhost(port: u16) Ip6Address { |
| 410 | 467 | return .{ |
| 411 | 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 | 654 | pub fn eql(a: Ip6Address, b: Ip6Address) bool { |
| 598 | 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 | 751 | pub const Stream = struct { |