1// Copyright 2020 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package netip 6 7import ( 8 "bytes" 9 "encoding" 10 "encoding/json" 11 "strings" 12 "testing" 13) 14 15var ( 16 mustPrefix = MustParsePrefix 17 mustIP = MustParseAddr 18) 19 20func TestPrefixValid(t *testing.T) { 21 v4 := MustParseAddr("1.2.3.4") 22 v6 := MustParseAddr("::1") 23 tests := []struct { 24 ipp Prefix 25 want bool 26 }{ 27 {Prefix{v4, -2}, false}, 28 {Prefix{v4, -1}, false}, 29 {Prefix{v4, 0}, true}, 30 {Prefix{v4, 32}, true}, 31 {Prefix{v4, 33}, false}, 32 33 {Prefix{v6, -2}, false}, 34 {Prefix{v6, -1}, false}, 35 {Prefix{v6, 0}, true}, 36 {Prefix{v6, 32}, true}, 37 {Prefix{v6, 128}, true}, 38 {Prefix{v6, 129}, false}, 39 40 {Prefix{Addr{}, -2}, false}, 41 {Prefix{Addr{}, -1}, false}, 42 {Prefix{Addr{}, 0}, false}, 43 {Prefix{Addr{}, 32}, false}, 44 {Prefix{Addr{}, 128}, false}, 45 } 46 for _, tt := range tests { 47 got := tt.ipp.IsValid() 48 if got != tt.want { 49 t.Errorf("(%v).IsValid() = %v want %v", tt.ipp, got, tt.want) 50 } 51 } 52} 53 54var nextPrevTests = []struct { 55 ip Addr 56 next Addr 57 prev Addr 58}{ 59 {mustIP("10.0.0.1"), mustIP("10.0.0.2"), mustIP("10.0.0.0")}, 60 {mustIP("10.0.0.255"), mustIP("10.0.1.0"), mustIP("10.0.0.254")}, 61 {mustIP("127.0.0.1"), mustIP("127.0.0.2"), mustIP("127.0.0.0")}, 62 {mustIP("254.255.255.255"), mustIP("255.0.0.0"), mustIP("254.255.255.254")}, 63 {mustIP("255.255.255.255"), Addr{}, mustIP("255.255.255.254")}, 64 {mustIP("0.0.0.0"), mustIP("0.0.0.1"), Addr{}}, 65 {mustIP("::"), mustIP("::1"), Addr{}}, 66 {mustIP("::%x"), mustIP("::1%x"), Addr{}}, 67 {mustIP("::1"), mustIP("::2"), mustIP("::")}, 68 {mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), Addr{}, mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")}, 69} 70 71func TestIPNextPrev(t *testing.T) { 72 doNextPrev(t) 73 74 for _, ip := range []Addr{ 75 mustIP("0.0.0.0"), 76 mustIP("::"), 77 } { 78 got := ip.Prev() 79 if !got.isZero() { 80 t.Errorf("IP(%v).Prev = %v; want zero", ip, got) 81 } 82 } 83 84 var allFF [16]byte 85 for i := range allFF { 86 allFF[i] = 0xff 87 } 88 89 for _, ip := range []Addr{ 90 mustIP("255.255.255.255"), 91 AddrFrom16(allFF), 92 } { 93 got := ip.Next() 94 if !got.isZero() { 95 t.Errorf("IP(%v).Next = %v; want zero", ip, got) 96 } 97 } 98} 99 100func BenchmarkIPNextPrev(b *testing.B) { 101 for i := 0; i < b.N; i++ { 102 doNextPrev(b) 103 } 104} 105 106func doNextPrev(t testing.TB) { 107 for _, tt := range nextPrevTests { 108 gnext, gprev := tt.ip.Next(), tt.ip.Prev() 109 if gnext != tt.next { 110 t.Errorf("IP(%v).Next = %v; want %v", tt.ip, gnext, tt.next) 111 } 112 if gprev != tt.prev { 113 t.Errorf("IP(%v).Prev = %v; want %v", tt.ip, gprev, tt.prev) 114 } 115 if !tt.ip.Next().isZero() && tt.ip.Next().Prev() != tt.ip { 116 t.Errorf("IP(%v).Next.Prev = %v; want %v", tt.ip, tt.ip.Next().Prev(), tt.ip) 117 } 118 if !tt.ip.Prev().isZero() && tt.ip.Prev().Next() != tt.ip { 119 t.Errorf("IP(%v).Prev.Next = %v; want %v", tt.ip, tt.ip.Prev().Next(), tt.ip) 120 } 121 } 122} 123 124func TestIPBitLen(t *testing.T) { 125 tests := []struct { 126 ip Addr 127 want int 128 }{ 129 {Addr{}, 0}, 130 {mustIP("0.0.0.0"), 32}, 131 {mustIP("10.0.0.1"), 32}, 132 {mustIP("::"), 128}, 133 {mustIP("fed0::1"), 128}, 134 {mustIP("::ffff:10.0.0.1"), 128}, 135 } 136 for _, tt := range tests { 137 got := tt.ip.BitLen() 138 if got != tt.want { 139 t.Errorf("BitLen(%v) = %d; want %d", tt.ip, got, tt.want) 140 } 141 } 142} 143 144func TestPrefixContains(t *testing.T) { 145 tests := []struct { 146 ipp Prefix 147 ip Addr 148 want bool 149 }{ 150 {mustPrefix("9.8.7.6/0"), mustIP("9.8.7.6"), true}, 151 {mustPrefix("9.8.7.6/16"), mustIP("9.8.7.6"), true}, 152 {mustPrefix("9.8.7.6/16"), mustIP("9.8.6.4"), true}, 153 {mustPrefix("9.8.7.6/16"), mustIP("9.9.7.6"), false}, 154 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.6"), true}, 155 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, 156 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, 157 {mustPrefix("::1/0"), mustIP("::1"), true}, 158 {mustPrefix("::1/0"), mustIP("::2"), true}, 159 {mustPrefix("::1/127"), mustIP("::1"), true}, 160 {mustPrefix("::1/127"), mustIP("::2"), false}, 161 {mustPrefix("::1/128"), mustIP("::1"), true}, 162 {mustPrefix("::1/127"), mustIP("::2"), false}, 163 // zones support 164 {mustPrefix("::1%a/128"), mustIP("::1"), true}, // prefix zones are stripped... 165 {mustPrefix("::1%a/128"), mustIP("::1%a"), false}, // but ip zones are not 166 // invalid IP 167 {mustPrefix("::1/0"), Addr{}, false}, 168 {mustPrefix("1.2.3.4/0"), Addr{}, false}, 169 // invalid Prefix 170 {Prefix{mustIP("::1"), 129}, mustIP("::1"), false}, 171 {Prefix{mustIP("1.2.3.4"), 33}, mustIP("1.2.3.4"), false}, 172 {Prefix{Addr{}, 0}, mustIP("1.2.3.4"), false}, 173 {Prefix{Addr{}, 32}, mustIP("1.2.3.4"), false}, 174 {Prefix{Addr{}, 128}, mustIP("::1"), false}, 175 // wrong IP family 176 {mustPrefix("::1/0"), mustIP("1.2.3.4"), false}, 177 {mustPrefix("1.2.3.4/0"), mustIP("::1"), false}, 178 } 179 for _, tt := range tests { 180 got := tt.ipp.Contains(tt.ip) 181 if got != tt.want { 182 t.Errorf("(%v).Contains(%v) = %v want %v", tt.ipp, tt.ip, got, tt.want) 183 } 184 } 185} 186 187func TestParseIPError(t *testing.T) { 188 tests := []struct { 189 ip string 190 errstr string 191 }{ 192 { 193 ip: "localhost", 194 }, 195 { 196 ip: "500.0.0.1", 197 errstr: "field has value >255", 198 }, 199 { 200 ip: "::gggg%eth0", 201 errstr: "must have at least one digit", 202 }, 203 { 204 ip: "fe80::1cc0:3e8c:119f:c2e1%", 205 errstr: "zone must be a non-empty string", 206 }, 207 { 208 ip: "%eth0", 209 errstr: "missing IPv6 address", 210 }, 211 } 212 for _, test := range tests { 213 t.Run(test.ip, func(t *testing.T) { 214 _, err := ParseAddr(test.ip) 215 if err == nil { 216 t.Fatal("no error") 217 } 218 if _, ok := err.(parseAddrError); !ok { 219 t.Errorf("error type is %T, want parseIPError", err) 220 } 221 if test.errstr == "" { 222 test.errstr = "unable to parse IP" 223 } 224 if got := err.Error(); !strings.Contains(got, test.errstr) { 225 t.Errorf("error is missing substring %q: %s", test.errstr, got) 226 } 227 }) 228 } 229} 230 231func TestParseAddrPort(t *testing.T) { 232 tests := []struct { 233 in string 234 want AddrPort 235 wantErr bool 236 }{ 237 {in: "1.2.3.4:1234", want: AddrPort{mustIP("1.2.3.4"), 1234}}, 238 {in: "1.1.1.1:123456", wantErr: true}, 239 {in: "1.1.1.1:-123", wantErr: true}, 240 {in: "[::1]:1234", want: AddrPort{mustIP("::1"), 1234}}, 241 {in: "[1.2.3.4]:1234", wantErr: true}, 242 {in: "fe80::1:1234", wantErr: true}, 243 {in: ":0", wantErr: true}, // if we need to parse this form, there should be a separate function that explicitly allows it 244 } 245 for _, test := range tests { 246 t.Run(test.in, func(t *testing.T) { 247 got, err := ParseAddrPort(test.in) 248 if err != nil { 249 if test.wantErr { 250 return 251 } 252 t.Fatal(err) 253 } 254 if got != test.want { 255 t.Errorf("got %v; want %v", got, test.want) 256 } 257 if got.String() != test.in { 258 t.Errorf("String = %q; want %q", got.String(), test.in) 259 } 260 }) 261 262 t.Run(test.in+"/AppendTo", func(t *testing.T) { 263 got, err := ParseAddrPort(test.in) 264 if err == nil { 265 testAppendToMarshal(t, got) 266 } 267 }) 268 269 // TextMarshal and TextUnmarshal mostly behave like 270 // ParseAddrPort and String. Divergent behavior are handled in 271 // TestAddrPortMarshalUnmarshal. 272 t.Run(test.in+"/Marshal", func(t *testing.T) { 273 var got AddrPort 274 jsin := `"` + test.in + `"` 275 err := json.Unmarshal([]byte(jsin), &got) 276 if err != nil { 277 if test.wantErr { 278 return 279 } 280 t.Fatal(err) 281 } 282 if got != test.want { 283 t.Errorf("got %v; want %v", got, test.want) 284 } 285 gotb, err := json.Marshal(got) 286 if err != nil { 287 t.Fatal(err) 288 } 289 if string(gotb) != jsin { 290 t.Errorf("Marshal = %q; want %q", string(gotb), jsin) 291 } 292 }) 293 } 294} 295 296func TestAddrPortMarshalUnmarshal(t *testing.T) { 297 tests := []struct { 298 in string 299 want AddrPort 300 }{ 301 {"", AddrPort{}}, 302 } 303 304 for _, test := range tests { 305 t.Run(test.in, func(t *testing.T) { 306 orig := `"` + test.in + `"` 307 308 var ipp AddrPort 309 if err := json.Unmarshal([]byte(orig), &ipp); err != nil { 310 t.Fatalf("failed to unmarshal: %v", err) 311 } 312 313 ippb, err := json.Marshal(ipp) 314 if err != nil { 315 t.Fatalf("failed to marshal: %v", err) 316 } 317 318 back := string(ippb) 319 if orig != back { 320 t.Errorf("Marshal = %q; want %q", back, orig) 321 } 322 323 testAppendToMarshal(t, ipp) 324 }) 325 } 326} 327 328type appendMarshaler interface { 329 encoding.TextMarshaler 330 AppendTo([]byte) []byte 331} 332 333// testAppendToMarshal tests that x's AppendTo and MarshalText methods yield the same results. 334// x's MarshalText method must not return an error. 335func testAppendToMarshal(t *testing.T, x appendMarshaler) { 336 t.Helper() 337 m, err := x.MarshalText() 338 if err != nil { 339 t.Fatalf("(%v).MarshalText: %v", x, err) 340 } 341 a := make([]byte, 0, len(m)) 342 a = x.AppendTo(a) 343 if !bytes.Equal(m, a) { 344 t.Errorf("(%v).MarshalText = %q, (%v).AppendTo = %q", x, m, x, a) 345 } 346} 347 348func TestIPv6Accessor(t *testing.T) { 349 var a [16]byte 350 for i := range a { 351 a[i] = uint8(i) + 1 352 } 353 ip := AddrFrom16(a) 354 for i := range a { 355 if got, want := ip.v6(uint8(i)), uint8(i)+1; got != want { 356 t.Errorf("v6(%v) = %v; want %v", i, got, want) 357 } 358 } 359} 360