package fetchguard import "net/netip" // privateV4 is a literal port of ManualCoverUrlFetcher.php PRIVATE_V4_CIDRS. var privateV4 = []netip.Prefix{ netip.MustParsePrefix("127.0.0.0/8"), netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("172.16.0.0/12"), netip.MustParsePrefix("192.168.0.0/16"), netip.MustParsePrefix("169.254.0.0/16"), netip.MustParsePrefix("100.64.0.0/10"), netip.MustParsePrefix("0.0.0.0/8"), } // privateV6 is a literal port of PRIVATE_V6_PREFIXES. PHP lists bare "::1" // as a prefix-less loopback literal; it is expressed here as ::1/128 so // Prefix.Contains works uniformly with the CIDR entries. var privateV6 = []netip.Prefix{ netip.MustParsePrefix("::1/128"), netip.MustParsePrefix("fe80::/10"), netip.MustParsePrefix("fc00::/7"), } var ( nat64WellKnownPrefix = netip.MustParsePrefix("64:ff9b::/96") nat64LocalUsePrefix = netip.MustParsePrefix("64:ff9b:1::/48") sixToFourPrefix = netip.MustParsePrefix("2002::/16") ) // isReservedOrPrivate classifies addr against the PHP private/loopback/ // reserved/CGNAT table, including IPv4 embedded in supported IPv6 transition // formats. func isReservedOrPrivate(addr netip.Addr) bool { if !addr.IsValid() { return true } addr = addr.Unmap() if addr.IsMulticast() || addr.IsUnspecified() { return true } if embedded, ok := embeddedTransitionIPv4(addr); ok { return isReservedOrPrivate(embedded) } table := privateV4 if !addr.Is4() { table = privateV6 } for _, prefix := range table { if prefix.Contains(addr) { return true } } return false } // embeddedTransitionIPv4 extracts IPv4 from the transition formats supported // by fetchguard. A recognized but malformed RFC 6052 /48 address returns an // invalid address with ok=true so the classifier fails closed. func embeddedTransitionIPv4(addr netip.Addr) (netip.Addr, bool) { if !addr.Is6() { return netip.Addr{}, false } b := addr.As16() switch { case nat64WellKnownPrefix.Contains(addr): return netip.AddrFrom4([4]byte{b[12], b[13], b[14], b[15]}), true case nat64LocalUsePrefix.Contains(addr): if b[8] != 0 { return netip.Addr{}, true } return netip.AddrFrom4([4]byte{b[6], b[7], b[9], b[10]}), true case sixToFourPrefix.Contains(addr): return netip.AddrFrom4([4]byte{b[2], b[3], b[4], b[5]}), true default: return netip.Addr{}, false } }