diff --git a/ip/iputil.go b/ip/iputil.go index 52f92a5..3e09814 100644 --- a/ip/iputil.go +++ b/ip/iputil.go @@ -14,6 +14,62 @@ import ( "go.uber.org/multierr" ) +var ( + // ipv4InternalRanges contains the IP ranges internal in IPv4 range. + ipv4InternalRanges = []string{ + "0.0.0.0/8", // Current network (only valid as source address) + "10.0.0.0/8", // Private network + "100.64.0.0/10", // Shared Address Space + "127.0.0.0/8", // Loopback + "169.254.0.0/16", // Link-local (Also many cloud providers Metadata endpoint) + "172.16.0.0/12", // Private network + "192.0.0.0/24", // IETF Protocol Assignments + "192.0.2.0/24", // TEST-NET-1, documentation and examples + "192.88.99.0/24", // IPv6 to IPv4 relay (includes 2002::/16) + "192.168.0.0/16", // Private network + "198.18.0.0/15", // Network benchmark tests + "198.51.100.0/24", // TEST-NET-2, documentation and examples + "203.0.113.0/24", // TEST-NET-3, documentation and examples + "224.0.0.0/4", // IP multicast (former Class D network) + "240.0.0.0/4", // Reserved (former Class E network) + } + + // ipv6InternalRanges contains the IP ranges internal in IPv6 range. + ipv6InternalRanges = []string{ + "::1/128", // Loopback + "64:ff9b::/96", // IPv4/IPv6 translation (RFC 6052) + "100::/64", // Discard prefix (RFC 6666) + "2001::/32", // Teredo tunneling + "2001:10::/28", // Deprecated (previously ORCHID) + "2001:20::/28", // ORCHIDv2 + "2001:db8::/32", // Addresses used in documentation and example source code + "2002::/16", // 6to4 + "fc00::/7", // Unique local address + "fe80::/10", // Link-local address + "ff00::/8", // Multicast + } + + ipv4, ipv6 []*net.IPNet +) + +func init() { + for _, cidr := range ipv4InternalRanges { + _, rangeNet, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + ipv4 = append(ipv4, rangeNet) + } + + for _, cidr := range ipv6InternalRanges { + _, rangeNet, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + ipv6 = append(ipv6, rangeNet) + } +} + // IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP` func IsIP(str string) bool { return net.ParseIP(str) != nil @@ -48,6 +104,47 @@ func IsIPv4(ips ...interface{}) bool { return true } +// Check if an IP address is part of the list of internal IPs we have declared +// checks for all ipv4 and ipv6 list +func IsInternal(str string) bool { + if !IsIP(str) { + return false + + } + IP := net.ParseIP(str) + for _, net := range ipv4 { + if net.Contains(IP) { + return true + } + } + for _, net := range ipv6 { + if net.Contains(IP) { + return true + } + } + return false +} + +// Check if an IP address is part of the list of internal IPs we have declared +func IsInIpv4List(str string) bool { + for _, ip := range ipv4InternalRanges { + if strings.Contains(ip, str) { + return true + } + } + return false +} + +// Check if an IP address is part of the list of internal IPs we have declared +func IsInIpv6List(str string) bool { + for _, ip := range ipv6InternalRanges { + if strings.Contains(ip, str) { + return true + } + } + return false +} + // IsIPv6 checks if the string is an IP version 6. func IsIPv6(ips ...interface{}) bool { for _, ip := range ips { diff --git a/ip/iputil_test.go b/ip/iputil_test.go index 1a2eccb..ff3ba22 100644 --- a/ip/iputil_test.go +++ b/ip/iputil_test.go @@ -12,6 +12,15 @@ func TestIsIP(t *testing.T) { require.False(t, IsIP("test"), "string recognized as ip") } +func TestIsInternalIPv4(t *testing.T) { + // Test this ipv4 + require.False(t, IsInternal("153.12.14.1"), "internal ipv4 address recognized as not valid") + require.True(t, IsInternal("172.16.0.0"), "internal ipv4 address recognized as valid") + // Test with ipv6 + require.False(t, IsInternal("684D:1111:222:3333:4444:5555:6:77"), "internal ipv4 address recognized as not valid") + require.True(t, IsInternal("fc00:7e5b:cfa9::"), "internal ipv4 address recognized as valid") +} + func TestIsPort(t *testing.T) { require.False(t, IsPort("0"), "invalid port 0") require.False(t, IsPort("-1"), "negative port")