You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
3.7 KiB
104 lines
3.7 KiB
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
|
|
}
|