diff --git a/fetchguard/ip.go b/fetchguard/ip.go new file mode 100644 index 0000000..06b77ab --- /dev/null +++ b/fetchguard/ip.go @@ -0,0 +1,45 @@ +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"), +} + +// isReservedOrPrivate classifies addr against the PHP private/loopback/ +// reserved/CGNAT table. The caller must pass an already-Unmap()-ed address +// (fetch.go's dial hook); this function does not Unmap. +func isReservedOrPrivate(addr netip.Addr) bool { + if !addr.IsValid() { + return true + } + if addr.IsMulticast() || addr.IsUnspecified() { + return true + } + table := privateV4 + if !addr.Is4() { + table = privateV6 + } + for _, prefix := range table { + if prefix.Contains(addr) { + return true + } + } + return false +}