package locator import ( "fmt" "net/netip" ) // The prefixes of NAT64 (RFC 6052): the well-known prefix, which every // reader accepts, and the block where the prefix of a network may also be, // besides a public IPv6 one (spec v0.13, §44.1). var ( nat64WellKnown = netip.MustParsePrefix("64:ff9b::/96") nat64Block = netip.MustParsePrefix("64:ff9b::/16") ) // CheckResolvedIP reports why a reader must not connect to ip, the address // that the name of an https address resolved to (spec v0.13, §44.1). A // reader checks it on every connection, redirections included; this package // downloads nothing. // // ip must be public, as an IP address written in a locator must be. On an // IPv6-only network with DNS64 and NAT64, a name that has only IPv4 // addresses resolves to an IPv6 address that holds one (RFC 6052): one of // the well-known prefix 64:ff9b::/96 is public when the IPv4 address in its // last 32 bits is. nat64 is the NAT64 prefix of the network, which the // reader discovers with RFC 7050 or its system gives, or the zero Prefix for // none: an address in it is public only when the IPv4 address it holds, at // the positions of RFC 6052, is, even when the prefix is a public one. nat64 // must have one of the lengths of RFC 6052 and lie in 64:ff9b::/16 or be a // public IPv6 prefix. // // An IPv4-mapped address is checked as the IPv6 address it is, and is not // public: a reader passes an IPv4 address as its 4 bytes. func CheckResolvedIP(ip netip.Addr, nat64 netip.Prefix) error { if !ip.IsValid() { return fmt.Errorf("locator: no IP address") } if nat64.IsValid() { if err := checkNAT64Prefix(nat64); err != nil { return err } } // The prefixes of NAT64 decide first: the prefix of a network may be a // public one, and an address in it reaches the IPv4 address it holds, // which may be private. for _, p := range []netip.Prefix{nat64WellKnown, nat64} { if !p.IsValid() || !p.Contains(ip) { continue } v4, ok := nat64IPv4(ip, p.Bits()) switch { case !ok: return fmt.Errorf("locator: an https address whose name resolves to %s, an address of the NAT64 prefix %s whose bits 64 to 71 are not zero", ip, p) case !publicIP(v4): return fmt.Errorf("locator: an https address whose name resolves to %s, an address of NAT64 that holds %s, an IP address that is not public", ip, v4) } return nil } if publicIP(ip) { return nil } return fmt.Errorf("locator: an https address whose name resolves to %s, an IP address that is not public", ip) } // checkNAT64Prefix reports why p cannot be the NAT64 prefix of a network // (RFC 6052, spec v0.13, §44.1). func checkNAT64Prefix(p netip.Prefix) error { switch { case !p.Addr().Is6() || p.Addr().Is4In6(): return fmt.Errorf("locator: the NAT64 prefix %s is not an IPv6 prefix", p) case p.Masked() != p: return fmt.Errorf("locator: the NAT64 prefix %s has bits set after its length", p) } switch p.Bits() { case 32, 40, 48, 56, 64, 96: default: return fmt.Errorf("locator: the NAT64 prefix %s is not of 32, 40, 48, 56, 64 or 96 bits (RFC 6052)", p) } if !nat64Block.Contains(p.Addr()) && !publicIP(p.Addr()) { return fmt.Errorf("locator: the NAT64 prefix %s is neither in 64:ff9b::/16 nor a public IPv6 prefix", p) } return nil } // nat64IPv4 extracts the IPv4 address that ip, an address of a NAT64 prefix // of bits bits, holds, at the positions of RFC 6052, section 2.2: the 32 // bits after the prefix, skipping bits 64 to 71, which must be zero. func nat64IPv4(ip netip.Addr, bits int) (netip.Addr, bool) { b := ip.As16() if bits < 96 && b[8] != 0 { return netip.Addr{}, false } var v4 [4]byte n := 0 for i := bits / 8; n < 4; i++ { if i == 8 { continue } v4[n] = b[i] n++ } return netip.AddrFrom4(v4), true }