github.com/letsencrypt/boulder@v0.20251208.0/iana/ip_test.go (about)

     1  package iana
     2  
     3  import (
     4  	"net/netip"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func TestIsReservedAddr(t *testing.T) {
    10  	t.Parallel()
    11  
    12  	cases := []struct {
    13  		ip   string
    14  		want string
    15  	}{
    16  		{"127.0.0.1", "Loopback"},          // second-lowest IP in a reserved /8, common mistaken request
    17  		{"128.0.0.1", ""},                  // second-lowest IP just above a reserved /8
    18  		{"192.168.254.254", "Private-Use"}, // highest IP in a reserved /16
    19  		{"192.169.255.255", ""},            // highest IP in the /16 above a reserved /16
    20  
    21  		{"::", "Unspecified Address"}, // lowest possible IPv6 address, reserved, possible parsing edge case
    22  		{"::1", "Loopback Address"},   // reserved, common mistaken request
    23  		{"::2", ""},                   // surprisingly unreserved
    24  
    25  		{"fe80::1", "Link-Local Unicast"},                                 // second-lowest IP in a reserved /10
    26  		{"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "Link-Local Unicast"}, // highest IP in a reserved /10
    27  		{"fec0::1", ""}, // second-lowest IP just above a reserved /10
    28  
    29  		{"fe80::1%eth0", "Link-Local Unicast"}, // IPv6 link-local with zone
    30  		{"::1%lo", "Loopback Address"},         // IPv6 loopback with zone
    31  
    32  		{"192.0.0.170", "NAT64/DNS64 Discovery"},            // first of two reserved IPs that are comma-split in IANA's CSV; also a more-specific of a larger reserved block that comes first
    33  		{"192.0.0.171", "NAT64/DNS64 Discovery"},            // second of two reserved IPs that are comma-split in IANA's CSV; also a more-specific of a larger reserved block that comes first
    34  		{"2001:1::1", "Port Control Protocol Anycast"},      // reserved IP that comes after a line with a line break in IANA's CSV; also a more-specific of a larger reserved block that comes first
    35  		{"2002::", "6to4"},                                  // lowest IP in a reserved /16 that has a footnote in IANA's CSV
    36  		{"2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "6to4"}, // highest IP in a reserved /16 that has a footnote in IANA's CSV
    37  
    38  		{"0100::", "Discard-Only Address Block"},                         // part of a reserved block in a non-canonical IPv6 format
    39  		{"0100::0000:ffff:ffff:ffff:ffff", "Discard-Only Address Block"}, // part of a reserved block in a non-canonical IPv6 format
    40  		{"0100::0002:0000:0000:0000:0000", ""},                           // non-reserved but in a non-canonical IPv6 format
    41  	}
    42  
    43  	for _, tc := range cases {
    44  		t.Run(tc.ip, func(t *testing.T) {
    45  			t.Parallel()
    46  			err := IsReservedAddr(netip.MustParseAddr(tc.ip))
    47  			if err == nil && tc.want != "" {
    48  				t.Errorf("Got success, wanted error for %#v", tc.ip)
    49  			}
    50  			if err != nil && !strings.Contains(err.Error(), tc.want) {
    51  				t.Errorf("%#v: got %q, want %q", tc.ip, err.Error(), tc.want)
    52  			}
    53  		})
    54  	}
    55  }
    56  
    57  func TestIsReservedPrefix(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	cases := []struct {
    61  		cidr string
    62  		want bool
    63  	}{
    64  		{"172.16.0.0/12", true},
    65  		{"172.16.0.0/32", true},
    66  		{"172.16.0.1/32", true},
    67  		{"172.31.255.0/24", true},
    68  		{"172.31.255.255/24", true},
    69  		{"172.31.255.255/32", true},
    70  		{"172.32.0.0/24", false},
    71  		{"172.32.0.1/32", false},
    72  
    73  		{"100::/64", true},
    74  		{"100::/128", true},
    75  		{"100::1/128", true},
    76  		{"100::1:ffff:ffff:ffff:ffff/128", true},
    77  		{"100:0:0:2::/64", false},
    78  		{"100:0:0:2::1/128", false},
    79  	}
    80  
    81  	for _, tc := range cases {
    82  		t.Run(tc.cidr, func(t *testing.T) {
    83  			t.Parallel()
    84  			err := IsReservedPrefix(netip.MustParsePrefix(tc.cidr))
    85  			if err != nil && !tc.want {
    86  				t.Error(err)
    87  			}
    88  			if err == nil && tc.want {
    89  				t.Errorf("Wanted error for %#v, got success", tc.cidr)
    90  			}
    91  		})
    92  	}
    93  }