Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IsInternal function #20

Merged
merged 10 commits into from
Nov 14, 2022
132 changes: 132 additions & 0 deletions ip/iputil.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,53 @@ import (
"go.uber.org/multierr"
)

var ipv4InternalRanges = []string{}
xm1k3 marked this conversation as resolved.
Show resolved Hide resolved
var ipv6InternalRanges = []string{}

// internalRangeChecker contains a list of internal IP ranges.
type internalRangeChecker struct {
ipv4 []*net.IPNet
ipv6 []*net.IPNet
}

var rangeChecker = internalRangeChecker{}

func init() {
// 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
}
}

// 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
Expand Down Expand Up @@ -48,6 +95,52 @@ 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 {
_, err := newInternalRangeChecker()
xm1k3 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return false
}

if !IsIP(str) {
return false

}
IP := net.ParseIP(str)
for _, net := range rangeChecker.ipv4 {
if net.Contains(IP) {
return true
}
}
for _, net := range rangeChecker.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 IsInternalIpv4(str string) bool {
xm1k3 marked this conversation as resolved.
Show resolved Hide resolved
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 IsInternalIpv6(str string) bool {
xm1k3 marked this conversation as resolved.
Show resolved Hide resolved
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 {
Expand Down Expand Up @@ -218,3 +311,42 @@ func GetBindableAddress(port int, ips ...string) (string, error) {

return "", errs
}

// newInternalRangeChecker creates a structure for checking if a host is from
// a internal IP range whether its ipv4 or ipv6.
func newInternalRangeChecker() (*internalRangeChecker, error) {
err := rangeChecker.appendIPv4Ranges(ipv4InternalRanges)
if err != nil {
return nil, err
}

err = rangeChecker.appendIPv6Ranges(ipv6InternalRanges)
if err != nil {
return nil, err
}
return &rangeChecker, nil
}

// appendIPv4Ranges adds a list of IPv4 Ranges to the list.
func (r *internalRangeChecker) appendIPv4Ranges(ranges []string) error {
for _, ip := range ranges {
_, rangeNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
r.ipv4 = append(r.ipv4, rangeNet)
}
return nil
}

// appendIPv6Ranges adds a list of IPv6 Ranges to the list.
func (r *internalRangeChecker) appendIPv6Ranges(ranges []string) error {
for _, ip := range ranges {
_, rangeNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
r.ipv6 = append(r.ipv6, rangeNet)
}
return nil
}
9 changes: 9 additions & 0 deletions ip/iputil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down