- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
179 lines
6.2 KiB
Go
179 lines
6.2 KiB
Go
package fetchguard
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
)
|
|
|
|
func TestIsReservedOrPrivate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
ip string
|
|
want bool
|
|
}{
|
|
{name: "loopback v4", ip: "127.0.0.1", want: true},
|
|
{name: "rfc1918 10/8", ip: "10.1.2.3", want: true},
|
|
{name: "rfc1918 172.16/12 start", ip: "172.16.0.1", want: true},
|
|
{name: "rfc1918 172.16/12 end", ip: "172.31.255.255", want: true},
|
|
{name: "rfc1918 192.168/16", ip: "192.168.1.1", want: true},
|
|
{name: "cloud metadata link-local", ip: "169.254.169.254", want: true},
|
|
{name: "cgnat 100.64/10", ip: "100.64.0.1", want: true},
|
|
{name: "this-network 0.0.0.0/8", ip: "0.0.0.1", want: true},
|
|
{name: "public 8.8.8.8", ip: "8.8.8.8", want: false},
|
|
{name: "public 1.1.1.1", ip: "1.1.1.1", want: false},
|
|
{name: "public 93.184.216.34", ip: "93.184.216.34", want: false},
|
|
{name: "loopback v6", ip: "::1", want: true},
|
|
{name: "link-local v6", ip: "fe80::1", want: true},
|
|
{name: "unique-local v6", ip: "fc00::1", want: true},
|
|
{name: "public v6", ip: "2606:4700:4700::1111", want: false},
|
|
{name: "v4-mapped metadata", ip: "::ffff:169.254.169.254", want: true},
|
|
{name: "multicast v4", ip: "224.0.0.1", want: true},
|
|
{name: "unspecified v4", ip: "0.0.0.0", want: true},
|
|
{name: "just below 172.16.0.0/12", ip: "172.15.255.255", want: false},
|
|
{name: "just above 172.16.0.0/12", ip: "172.32.0.0", want: false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
addr, err := netip.ParseAddr(tt.ip)
|
|
if err != nil {
|
|
t.Fatalf("ParseAddr(%q): %v", tt.ip, err)
|
|
}
|
|
got := isReservedOrPrivate(addr)
|
|
if got != tt.want {
|
|
t.Fatalf("isReservedOrPrivate(%s) = %v, want %v", addr, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsReservedOrPrivateIPv6Transitions(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
ip string
|
|
want bool
|
|
}{
|
|
{name: "nat64 well-known loopback", ip: "64:ff9b::7f00:1", want: true},
|
|
{name: "nat64 well-known rfc1918", ip: "64:ff9b::a00:1", want: true},
|
|
{name: "nat64 well-known metadata", ip: "64:ff9b::a9fe:a9fe", want: true},
|
|
{name: "nat64 well-known public", ip: "64:ff9b::808:808", want: false},
|
|
{name: "nat64 local-use loopback", ip: "64:ff9b:1:7f00:0:100::", want: true},
|
|
{name: "nat64 local-use rfc1918", ip: "64:ff9b:1:a00:0:100::", want: true},
|
|
{name: "nat64 local-use metadata", ip: "64:ff9b:1:a9fe:a9:fe00::", want: true},
|
|
{name: "nat64 local-use public", ip: "64:ff9b:1:808:8:800::", want: false},
|
|
{name: "nat64 local-use non-zero u octet", ip: "64:ff9b:1:7f00:100:100::", want: true},
|
|
{name: "6to4 loopback", ip: "2002:7f00:1::", want: true},
|
|
{name: "6to4 rfc1918", ip: "2002:a00:1::", want: true},
|
|
{name: "6to4 metadata", ip: "2002:a9fe:a9fe::", want: true},
|
|
{name: "6to4 public", ip: "2002:808:808::", want: false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
addr := netip.MustParseAddr(tt.ip)
|
|
if got := isReservedOrPrivate(addr); got != tt.want {
|
|
t.Fatalf("isReservedOrPrivate(%s) = %v, want %v", addr, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsReservedOrPrivateSpecialUseSmoke(t *testing.T) {
|
|
for _, s := range []string{"198.18.0.1", "192.0.0.1", "240.0.0.1", "255.255.255.255", "2001:db8::1", "fec0::1", "fe80::1%eth0"} {
|
|
if !isReservedOrPrivate(netip.MustParseAddr(s)) {
|
|
t.Errorf("%s should be non-public", s)
|
|
}
|
|
}
|
|
for _, s := range []string{"8.8.8.8", "1.1.1.1", "2606:4700:4700::1111"} {
|
|
if isReservedOrPrivate(netip.MustParseAddr(s)) {
|
|
t.Errorf("%s should be public", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func prefixBounds(p netip.Prefix) (first, last netip.Addr) {
|
|
first = p.Masked().Addr()
|
|
b := first.AsSlice()
|
|
for bit := p.Bits(); bit < len(b)*8; bit++ {
|
|
b[bit/8] |= 1 << (7 - bit%8)
|
|
}
|
|
last, _ = netip.AddrFromSlice(b)
|
|
return first, last
|
|
}
|
|
|
|
func TestIsReservedOrPrivateIANABoundaries(t *testing.T) {
|
|
var all []netip.Prefix
|
|
all = append(all, privateV4...)
|
|
all = append(all, privateV6...)
|
|
inAny := func(a netip.Addr) bool {
|
|
for _, p := range all {
|
|
if p.Contains(a) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
for _, p := range all {
|
|
t.Run(p.String(), func(t *testing.T) {
|
|
first, last := prefixBounds(p)
|
|
interior := first.Next()
|
|
for _, a := range []netip.Addr{first, last, interior} {
|
|
if !p.Contains(a) {
|
|
t.Fatalf("test bug: %s not in %s", a, p)
|
|
}
|
|
if !isReservedOrPrivate(a) {
|
|
t.Errorf("%s (in %s) classified public", a, p)
|
|
}
|
|
}
|
|
for _, a := range []netip.Addr{first.Prev(), last.Next()} {
|
|
if !a.IsValid() || inAny(a) || a.IsMulticast() || a.IsUnspecified() {
|
|
continue
|
|
}
|
|
if _, ok := embeddedTransitionIPv4(a); ok {
|
|
continue
|
|
}
|
|
if isReservedOrPrivate(a) {
|
|
t.Errorf("neighbour %s of %s classified non-public", a, p)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
explicit := []struct {
|
|
ip string
|
|
want bool
|
|
}{
|
|
{"198.17.255.255", false}, {"198.18.0.0", true}, {"198.19.255.255", true}, {"198.20.0.0", false},
|
|
{"191.255.255.255", false}, {"192.0.0.0", true}, {"192.0.1.0", false},
|
|
{"239.255.255.255", true}, {"240.0.0.0", true}, {"255.255.255.255", true},
|
|
{"100.63.255.255", false}, {"100.64.0.0", true}, {"100.127.255.255", true}, {"100.128.0.0", false},
|
|
{"172.15.255.255", false}, {"172.32.0.0", false},
|
|
{"2001:200::1", false}, {"2001:db8::", true}, {"2001:db9::", false},
|
|
{"3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", true}, {"3fff:1000::", false},
|
|
{"8.8.8.8", false}, {"1.1.1.1", false}, {"2606:4700:4700::1111", false},
|
|
{"::ffff:127.0.0.1", true}, {"::ffff:10.0.0.1", true}, {"::ffff:198.18.0.1", true}, {"::ffff:8.8.8.8", false},
|
|
}
|
|
for _, tc := range explicit {
|
|
if got := isReservedOrPrivate(netip.MustParseAddr(tc.ip)); got != tc.want {
|
|
t.Errorf("isReservedOrPrivate(%s) = %v, want %v", tc.ip, got, tc.want)
|
|
}
|
|
}
|
|
if !isReservedOrPrivate(netip.Addr{}) {
|
|
t.Error("invalid addr must fail closed")
|
|
}
|
|
}
|
|
|
|
func TestIsReservedOrPrivateIgnoresZone(t *testing.T) {
|
|
for _, s := range []string{"fe80::1%eth0", "fe80::1%1", "fe80::1", "fc00::1%eth0"} {
|
|
if !isReservedOrPrivate(netip.MustParseAddr(s)) {
|
|
t.Errorf("%s should be non-public", s)
|
|
}
|
|
}
|
|
for _, s := range []string{"2606:4700:4700::1111%eth0", "2606:4700:4700::1111%2"} {
|
|
a := netip.MustParseAddr(s)
|
|
if isReservedOrPrivate(a) != isReservedOrPrivate(a.WithZone("")) || isReservedOrPrivate(a) {
|
|
t.Errorf("%s should classify as its unzoned public form", s)
|
|
}
|
|
}
|
|
}
|