]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/netip/netip_test.go
net/netip: allow only valid prefix digits in ParsePrefix
[gostls13.git] / src / net / netip / netip_test.go
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
5 package netip_test
6
7 import (
8         "bytes"
9         "encoding/json"
10         "flag"
11         "fmt"
12         "internal/intern"
13         "internal/testenv"
14         "net"
15         . "net/netip"
16         "reflect"
17         "slices"
18         "sort"
19         "strings"
20         "testing"
21 )
22
23 var long = flag.Bool("long", false, "run long tests")
24
25 type uint128 = Uint128
26
27 var (
28         mustPrefix = MustParsePrefix
29         mustIP     = MustParseAddr
30         mustIPPort = MustParseAddrPort
31 )
32
33 func TestParseAddr(t *testing.T) {
34         var validIPs = []struct {
35                 in      string
36                 ip      Addr   // output of ParseAddr()
37                 str     string // output of String(). If "", use in.
38                 wantErr string
39         }{
40                 // Basic zero IPv4 address.
41                 {
42                         in: "0.0.0.0",
43                         ip: MkAddr(Mk128(0, 0xffff00000000), Z4),
44                 },
45                 // Basic non-zero IPv4 address.
46                 {
47                         in: "192.168.140.255",
48                         ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4),
49                 },
50                 // IPv4 address in windows-style "print all the digits" form.
51                 {
52                         in:      "010.000.015.001",
53                         wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`,
54                 },
55                 // IPv4 address with a silly amount of leading zeros.
56                 {
57                         in:      "000001.00000002.00000003.000000004",
58                         wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`,
59                 },
60                 // 4-in-6 with octet with leading zero
61                 {
62                         in:      "::ffff:1.2.03.4",
63                         wantErr: `ParseAddr("::ffff:1.2.03.4"): ParseAddr("1.2.03.4"): IPv4 field has octet with leading zero (at "1.2.03.4")`,
64                 },
65                 // Basic zero IPv6 address.
66                 {
67                         in: "::",
68                         ip: MkAddr(Mk128(0, 0), Z6noz),
69                 },
70                 // Localhost IPv6.
71                 {
72                         in: "::1",
73                         ip: MkAddr(Mk128(0, 1), Z6noz),
74                 },
75                 // Fully expanded IPv6 address.
76                 {
77                         in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
78                         ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz),
79                 },
80                 // IPv6 with elided fields in the middle.
81                 {
82                         in: "fd7a:115c::626b:430b",
83                         ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz),
84                 },
85                 // IPv6 with elided fields at the end.
86                 {
87                         in: "fd7a:115c:a1e0:ab12:4843:cd96::",
88                         ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz),
89                 },
90                 // IPv6 with single elided field at the end.
91                 {
92                         in:  "fd7a:115c:a1e0:ab12:4843:cd96:626b::",
93                         ip:  MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz),
94                         str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
95                 },
96                 // IPv6 with single elided field in the middle.
97                 {
98                         in:  "fd7a:115c:a1e0::4843:cd96:626b:430b",
99                         ip:  MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz),
100                         str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b",
101                 },
102                 // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6)
103                 {
104                         in:  "::ffff:192.168.140.255",
105                         ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz),
106                         str: "::ffff:192.168.140.255",
107                 },
108                 // IPv6 with a zone specifier.
109                 {
110                         in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
111                         ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), intern.Get("eth0")),
112                 },
113                 // IPv6 with dotted decimal and zone specifier.
114                 {
115                         in:  "1:2::ffff:192.168.140.255%eth1",
116                         ip:  MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), intern.Get("eth1")),
117                         str: "1:2::ffff:c0a8:8cff%eth1",
118                 },
119                 // 4-in-6 with zone
120                 {
121                         in:  "::ffff:192.168.140.255%eth1",
122                         ip:  MkAddr(Mk128(0, 0x0000ffffc0a88cff), intern.Get("eth1")),
123                         str: "::ffff:192.168.140.255%eth1",
124                 },
125                 // IPv6 with capital letters.
126                 {
127                         in:  "FD9E:1A04:F01D::1",
128                         ip:  MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz),
129                         str: "fd9e:1a04:f01d::1",
130                 },
131         }
132
133         for _, test := range validIPs {
134                 t.Run(test.in, func(t *testing.T) {
135                         got, err := ParseAddr(test.in)
136                         if err != nil {
137                                 if err.Error() == test.wantErr {
138                                         return
139                                 }
140                                 t.Fatal(err)
141                         }
142                         if test.wantErr != "" {
143                                 t.Fatalf("wanted error %q; got none", test.wantErr)
144                         }
145                         if got != test.ip {
146                                 t.Errorf("got %#v, want %#v", got, test.ip)
147                         }
148
149                         // Check that ParseAddr is a pure function.
150                         got2, err := ParseAddr(test.in)
151                         if err != nil {
152                                 t.Fatal(err)
153                         }
154                         if got != got2 {
155                                 t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2)
156                         }
157
158                         // Check that ParseAddr(ip.String()) is the identity function.
159                         s := got.String()
160                         got3, err := ParseAddr(s)
161                         if err != nil {
162                                 t.Fatal(err)
163                         }
164                         if got != got3 {
165                                 t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got)
166                         }
167
168                         // Check that the slow-but-readable parser produces the same result.
169                         slow, err := parseIPSlow(test.in)
170                         if err != nil {
171                                 t.Fatal(err)
172                         }
173                         if got != slow {
174                                 t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow)
175                         }
176
177                         // Check that the parsed IP formats as expected.
178                         s = got.String()
179                         wants := test.str
180                         if wants == "" {
181                                 wants = test.in
182                         }
183                         if s != wants {
184                                 t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants)
185                         }
186
187                         // Check that AppendTo matches MarshalText.
188                         TestAppendToMarshal(t, got)
189
190                         // Check that MarshalText/UnmarshalText work similarly to
191                         // ParseAddr/String (see TestIPMarshalUnmarshal for
192                         // marshal-specific behavior that's not common with
193                         // ParseAddr/String).
194                         js := `"` + test.in + `"`
195                         var jsgot Addr
196                         if err := json.Unmarshal([]byte(js), &jsgot); err != nil {
197                                 t.Fatal(err)
198                         }
199                         if jsgot != got {
200                                 t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got)
201                         }
202                         jsb, err := json.Marshal(jsgot)
203                         if err != nil {
204                                 t.Fatal(err)
205                         }
206                         jswant := `"` + wants + `"`
207                         jsback := string(jsb)
208                         if jsback != jswant {
209                                 t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant)
210                         }
211                 })
212         }
213
214         var invalidIPs = []string{
215                 // Empty string
216                 "",
217                 // Garbage non-IP
218                 "bad",
219                 // Single number. Some parsers accept this as an IPv4 address in
220                 // big-endian uint32 form, but we don't.
221                 "1234",
222                 // IPv4 with a zone specifier
223                 "1.2.3.4%eth0",
224                 // IPv4 field must have at least one digit
225                 ".1.2.3",
226                 "1.2.3.",
227                 "1..2.3",
228                 // IPv4 address too long
229                 "1.2.3.4.5",
230                 // IPv4 in dotted octal form
231                 "0300.0250.0214.0377",
232                 // IPv4 in dotted hex form
233                 "0xc0.0xa8.0x8c.0xff",
234                 // IPv4 in class B form
235                 "192.168.12345",
236                 // IPv4 in class B form, with a small enough number to be
237                 // parseable as a regular dotted decimal field.
238                 "127.0.1",
239                 // IPv4 in class A form
240                 "192.1234567",
241                 // IPv4 in class A form, with a small enough number to be
242                 // parseable as a regular dotted decimal field.
243                 "127.1",
244                 // IPv4 field has value >255
245                 "192.168.300.1",
246                 // IPv4 with too many fields
247                 "192.168.0.1.5.6",
248                 // IPv6 with not enough fields
249                 "1:2:3:4:5:6:7",
250                 // IPv6 with too many fields
251                 "1:2:3:4:5:6:7:8:9",
252                 // IPv6 with 8 fields and a :: expander
253                 "1:2:3:4::5:6:7:8",
254                 // IPv6 with a field bigger than 2b
255                 "fe801::1",
256                 // IPv6 with non-hex values in field
257                 "fe80:tail:scal:e::",
258                 // IPv6 with a zone delimiter but no zone.
259                 "fe80::1%",
260                 // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4.
261                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
262                 // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4.
263                 "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
264                 // IPv6 with invalid embedded IPv4.
265                 "::ffff:192.168.140.bad",
266                 // IPv6 with multiple ellipsis ::.
267                 "fe80::1::1",
268                 // IPv6 with invalid non hex/colon character.
269                 "fe80:1?:1",
270                 // IPv6 with truncated bytes after single colon.
271                 "fe80:",
272         }
273
274         for _, s := range invalidIPs {
275                 t.Run(s, func(t *testing.T) {
276                         got, err := ParseAddr(s)
277                         if err == nil {
278                                 t.Errorf("ParseAddr(%q) = %#v, want error", s, got)
279                         }
280
281                         slow, err := parseIPSlow(s)
282                         if err == nil {
283                                 t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow)
284                         }
285
286                         std := net.ParseIP(s)
287                         if std != nil {
288                                 t.Errorf("net.ParseIP(%q) = %#v, want error", s, std)
289                         }
290
291                         if s == "" {
292                                 // Don't test unmarshaling of "" here, do it in
293                                 // IPMarshalUnmarshal.
294                                 return
295                         }
296                         var jsgot Addr
297                         js := []byte(`"` + s + `"`)
298                         if err := json.Unmarshal(js, &jsgot); err == nil {
299                                 t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot)
300                         }
301                 })
302         }
303 }
304
305 func TestAddrFromSlice(t *testing.T) {
306         tests := []struct {
307                 ip       []byte
308                 wantAddr Addr
309                 wantOK   bool
310         }{
311                 {
312                         ip:       []byte{10, 0, 0, 1},
313                         wantAddr: mustIP("10.0.0.1"),
314                         wantOK:   true,
315                 },
316                 {
317                         ip:       []byte{0xfe, 0x80, 15: 0x01},
318                         wantAddr: mustIP("fe80::01"),
319                         wantOK:   true,
320                 },
321                 {
322                         ip:       []byte{0, 1, 2},
323                         wantAddr: Addr{},
324                         wantOK:   false,
325                 },
326                 {
327                         ip:       nil,
328                         wantAddr: Addr{},
329                         wantOK:   false,
330                 },
331         }
332         for _, tt := range tests {
333                 addr, ok := AddrFromSlice(tt.ip)
334                 if ok != tt.wantOK || addr != tt.wantAddr {
335                         t.Errorf("AddrFromSlice(%#v) = %#v, %v, want %#v, %v", tt.ip, addr, ok, tt.wantAddr, tt.wantOK)
336                 }
337         }
338 }
339
340 func TestIPv4Constructors(t *testing.T) {
341         if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") {
342                 t.Errorf("don't match")
343         }
344 }
345
346 func TestAddrMarshalUnmarshalBinary(t *testing.T) {
347         tests := []struct {
348                 ip       string
349                 wantSize int
350         }{
351                 {"", 0}, // zero IP
352                 {"1.2.3.4", 4},
353                 {"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16},
354                 {"::ffff:c000:0280", 16},
355                 {"::ffff:c000:0280%eth0", 20},
356         }
357         for _, tc := range tests {
358                 var ip Addr
359                 if len(tc.ip) > 0 {
360                         ip = mustIP(tc.ip)
361                 }
362                 b, err := ip.MarshalBinary()
363                 if err != nil {
364                         t.Fatal(err)
365                 }
366                 if len(b) != tc.wantSize {
367                         t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize)
368                 }
369                 var ip2 Addr
370                 if err := ip2.UnmarshalBinary(b); err != nil {
371                         t.Fatal(err)
372                 }
373                 if ip != ip2 {
374                         t.Fatalf("got %v; want %v", ip2, ip)
375                 }
376         }
377
378         // Cannot unmarshal from unexpected IP length.
379         for _, n := range []int{3, 5} {
380                 var ip2 Addr
381                 if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
382                         t.Fatalf("unmarshaled from unexpected IP length %d", n)
383                 }
384         }
385 }
386
387 func TestAddrPortMarshalTextString(t *testing.T) {
388         tests := []struct {
389                 in   AddrPort
390                 want string
391         }{
392                 {mustIPPort("1.2.3.4:80"), "1.2.3.4:80"},
393                 {mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"},
394                 {mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"},
395                 {mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"},
396                 {mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"},
397         }
398         for i, tt := range tests {
399                 if got := tt.in.String(); got != tt.want {
400                         t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want)
401                 }
402                 mt, err := tt.in.MarshalText()
403                 if err != nil {
404                         t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
405                         continue
406                 }
407                 if string(mt) != tt.want {
408                         t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want)
409                 }
410         }
411 }
412
413 func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
414         tests := []struct {
415                 ipport   string
416                 wantSize int
417         }{
418                 {"1.2.3.4:51820", 4 + 2},
419                 {"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
420                 {"[::ffff:c000:0280]:65535", 16 + 2},
421                 {"[::ffff:c000:0280%eth0]:1", 20 + 2},
422         }
423         for _, tc := range tests {
424                 var ipport AddrPort
425                 if len(tc.ipport) > 0 {
426                         ipport = mustIPPort(tc.ipport)
427                 }
428                 b, err := ipport.MarshalBinary()
429                 if err != nil {
430                         t.Fatal(err)
431                 }
432                 if len(b) != tc.wantSize {
433                         t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
434                 }
435                 var ipport2 AddrPort
436                 if err := ipport2.UnmarshalBinary(b); err != nil {
437                         t.Fatal(err)
438                 }
439                 if ipport != ipport2 {
440                         t.Fatalf("got %v; want %v", ipport2, ipport)
441                 }
442         }
443
444         // Cannot unmarshal from unexpected lengths.
445         for _, n := range []int{3, 7} {
446                 var ipport2 AddrPort
447                 if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
448                         t.Fatalf("unmarshaled from unexpected length %d", n)
449                 }
450         }
451 }
452
453 func TestPrefixMarshalTextString(t *testing.T) {
454         tests := []struct {
455                 in   Prefix
456                 want string
457         }{
458                 {mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
459                 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
460                 {mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
461                 {mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
462                 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped
463         }
464         for i, tt := range tests {
465                 if got := tt.in.String(); got != tt.want {
466                         t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want)
467                 }
468                 mt, err := tt.in.MarshalText()
469                 if err != nil {
470                         t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err)
471                         continue
472                 }
473                 if string(mt) != tt.want {
474                         t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want)
475                 }
476         }
477 }
478
479 func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
480         type testCase struct {
481                 prefix   Prefix
482                 wantSize int
483         }
484         tests := []testCase{
485                 {mustPrefix("1.2.3.4/24"), 4 + 1},
486                 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
487                 {mustPrefix("::ffff:c000:0280/96"), 16 + 1},
488                 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped
489         }
490         tests = append(tests,
491                 testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
492                 testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
493         for _, tc := range tests {
494                 prefix := tc.prefix
495                 b, err := prefix.MarshalBinary()
496                 if err != nil {
497                         t.Fatal(err)
498                 }
499                 if len(b) != tc.wantSize {
500                         t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
501                 }
502                 var prefix2 Prefix
503                 if err := prefix2.UnmarshalBinary(b); err != nil {
504                         t.Fatal(err)
505                 }
506                 if prefix != prefix2 {
507                         t.Fatalf("got %v; want %v", prefix2, prefix)
508                 }
509         }
510
511         // Cannot unmarshal from unexpected lengths.
512         for _, n := range []int{3, 6} {
513                 var prefix2 Prefix
514                 if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
515                         t.Fatalf("unmarshaled from unexpected length %d", n)
516                 }
517         }
518 }
519
520 func TestAddrMarshalUnmarshal(t *testing.T) {
521         // This only tests the cases where Marshal/Unmarshal diverges from
522         // the behavior of ParseAddr/String. For the rest of the test cases,
523         // see TestParseAddr above.
524         orig := `""`
525         var ip Addr
526         if err := json.Unmarshal([]byte(orig), &ip); err != nil {
527                 t.Fatalf("Unmarshal(%q) got error %v", orig, err)
528         }
529         if ip != (Addr{}) {
530                 t.Errorf("Unmarshal(%q) is not the zero Addr", orig)
531         }
532
533         jsb, err := json.Marshal(ip)
534         if err != nil {
535                 t.Fatalf("Marshal(%v) got error %v", ip, err)
536         }
537         back := string(jsb)
538         if back != orig {
539                 t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig)
540         }
541 }
542
543 func TestAddrFrom16(t *testing.T) {
544         tests := []struct {
545                 name string
546                 in   [16]byte
547                 want Addr
548         }{
549                 {
550                         name: "v6-raw",
551                         in:   [...]byte{15: 1},
552                         want: MkAddr(Mk128(0, 1), Z6noz),
553                 },
554                 {
555                         name: "v4-raw",
556                         in:   [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4},
557                         want: MkAddr(Mk128(0, 0xffff01020304), Z6noz),
558                 },
559         }
560         for _, tt := range tests {
561                 t.Run(tt.name, func(t *testing.T) {
562                         got := AddrFrom16(tt.in)
563                         if got != tt.want {
564                                 t.Errorf("got %#v; want %#v", got, tt.want)
565                         }
566                 })
567         }
568 }
569
570 func TestIPProperties(t *testing.T) {
571         var (
572                 nilIP Addr
573
574                 unicast4           = mustIP("192.0.2.1")
575                 unicast6           = mustIP("2001:db8::1")
576                 unicastZone6       = mustIP("2001:db8::1%eth0")
577                 unicast6Unassigned = mustIP("4000::1") // not in 2000::/3.
578
579                 multicast4     = mustIP("224.0.0.1")
580                 multicast6     = mustIP("ff02::1")
581                 multicastZone6 = mustIP("ff02::1%eth0")
582
583                 llu4     = mustIP("169.254.0.1")
584                 llu6     = mustIP("fe80::1")
585                 llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
586                 lluZone6 = mustIP("fe80::1%eth0")
587
588                 loopback4 = mustIP("127.0.0.1")
589
590                 ilm6     = mustIP("ff01::1")
591                 ilmZone6 = mustIP("ff01::1%eth0")
592
593                 private4a = mustIP("10.0.0.1")
594                 private4b = mustIP("172.16.0.1")
595                 private4c = mustIP("192.168.1.1")
596                 private6  = mustIP("fd00::1")
597         )
598
599         tests := []struct {
600                 name                    string
601                 ip                      Addr
602                 globalUnicast           bool
603                 interfaceLocalMulticast bool
604                 linkLocalMulticast      bool
605                 linkLocalUnicast        bool
606                 loopback                bool
607                 multicast               bool
608                 private                 bool
609                 unspecified             bool
610         }{
611                 {
612                         name: "nil",
613                         ip:   nilIP,
614                 },
615                 {
616                         name:          "unicast v4Addr",
617                         ip:            unicast4,
618                         globalUnicast: true,
619                 },
620                 {
621                         name:          "unicast v6Addr",
622                         ip:            unicast6,
623                         globalUnicast: true,
624                 },
625                 {
626                         name:          "unicast v6AddrZone",
627                         ip:            unicastZone6,
628                         globalUnicast: true,
629                 },
630                 {
631                         name:          "unicast v6Addr unassigned",
632                         ip:            unicast6Unassigned,
633                         globalUnicast: true,
634                 },
635                 {
636                         name:               "multicast v4Addr",
637                         ip:                 multicast4,
638                         linkLocalMulticast: true,
639                         multicast:          true,
640                 },
641                 {
642                         name:               "multicast v6Addr",
643                         ip:                 multicast6,
644                         linkLocalMulticast: true,
645                         multicast:          true,
646                 },
647                 {
648                         name:               "multicast v6AddrZone",
649                         ip:                 multicastZone6,
650                         linkLocalMulticast: true,
651                         multicast:          true,
652                 },
653                 {
654                         name:             "link-local unicast v4Addr",
655                         ip:               llu4,
656                         linkLocalUnicast: true,
657                 },
658                 {
659                         name:             "link-local unicast v6Addr",
660                         ip:               llu6,
661                         linkLocalUnicast: true,
662                 },
663                 {
664                         name:             "link-local unicast v6Addr upper bound",
665                         ip:               llu6Last,
666                         linkLocalUnicast: true,
667                 },
668                 {
669                         name:             "link-local unicast v6AddrZone",
670                         ip:               lluZone6,
671                         linkLocalUnicast: true,
672                 },
673                 {
674                         name:     "loopback v4Addr",
675                         ip:       loopback4,
676                         loopback: true,
677                 },
678                 {
679                         name:     "loopback v6Addr",
680                         ip:       IPv6Loopback(),
681                         loopback: true,
682                 },
683                 {
684                         name:                    "interface-local multicast v6Addr",
685                         ip:                      ilm6,
686                         interfaceLocalMulticast: true,
687                         multicast:               true,
688                 },
689                 {
690                         name:                    "interface-local multicast v6AddrZone",
691                         ip:                      ilmZone6,
692                         interfaceLocalMulticast: true,
693                         multicast:               true,
694                 },
695                 {
696                         name:          "private v4Addr 10/8",
697                         ip:            private4a,
698                         globalUnicast: true,
699                         private:       true,
700                 },
701                 {
702                         name:          "private v4Addr 172.16/12",
703                         ip:            private4b,
704                         globalUnicast: true,
705                         private:       true,
706                 },
707                 {
708                         name:          "private v4Addr 192.168/16",
709                         ip:            private4c,
710                         globalUnicast: true,
711                         private:       true,
712                 },
713                 {
714                         name:          "private v6Addr",
715                         ip:            private6,
716                         globalUnicast: true,
717                         private:       true,
718                 },
719                 {
720                         name:        "unspecified v4Addr",
721                         ip:          IPv4Unspecified(),
722                         unspecified: true,
723                 },
724                 {
725                         name:        "unspecified v6Addr",
726                         ip:          IPv6Unspecified(),
727                         unspecified: true,
728                 },
729         }
730
731         for _, tt := range tests {
732                 t.Run(tt.name, func(t *testing.T) {
733                         gu := tt.ip.IsGlobalUnicast()
734                         if gu != tt.globalUnicast {
735                                 t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast)
736                         }
737
738                         ilm := tt.ip.IsInterfaceLocalMulticast()
739                         if ilm != tt.interfaceLocalMulticast {
740                                 t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast)
741                         }
742
743                         llu := tt.ip.IsLinkLocalUnicast()
744                         if llu != tt.linkLocalUnicast {
745                                 t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast)
746                         }
747
748                         llm := tt.ip.IsLinkLocalMulticast()
749                         if llm != tt.linkLocalMulticast {
750                                 t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast)
751                         }
752
753                         lo := tt.ip.IsLoopback()
754                         if lo != tt.loopback {
755                                 t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback)
756                         }
757
758                         multicast := tt.ip.IsMulticast()
759                         if multicast != tt.multicast {
760                                 t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast)
761                         }
762
763                         private := tt.ip.IsPrivate()
764                         if private != tt.private {
765                                 t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private)
766                         }
767
768                         unspecified := tt.ip.IsUnspecified()
769                         if unspecified != tt.unspecified {
770                                 t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified)
771                         }
772                 })
773         }
774 }
775
776 func TestAddrWellKnown(t *testing.T) {
777         tests := []struct {
778                 name string
779                 ip   Addr
780                 std  net.IP
781         }{
782                 {
783                         name: "IPv6 link-local all nodes",
784                         ip:   IPv6LinkLocalAllNodes(),
785                         std:  net.IPv6linklocalallnodes,
786                 },
787                 {
788                         name: "IPv6 link-local all routers",
789                         ip:   IPv6LinkLocalAllRouters(),
790                         std:  net.IPv6linklocalallrouters,
791                 },
792                 {
793                         name: "IPv6 loopback",
794                         ip:   IPv6Loopback(),
795                         std:  net.IPv6loopback,
796                 },
797                 {
798                         name: "IPv6 unspecified",
799                         ip:   IPv6Unspecified(),
800                         std:  net.IPv6unspecified,
801                 },
802         }
803
804         for _, tt := range tests {
805                 t.Run(tt.name, func(t *testing.T) {
806                         want := tt.std.String()
807                         got := tt.ip.String()
808
809                         if got != want {
810                                 t.Fatalf("got %s, want %s", got, want)
811                         }
812                 })
813         }
814 }
815
816 func TestAddrLessCompare(t *testing.T) {
817         tests := []struct {
818                 a, b Addr
819                 want bool
820         }{
821                 {Addr{}, Addr{}, false},
822                 {Addr{}, mustIP("1.2.3.4"), true},
823                 {mustIP("1.2.3.4"), Addr{}, false},
824
825                 {mustIP("1.2.3.4"), mustIP("0102:0304::0"), true},
826                 {mustIP("0102:0304::0"), mustIP("1.2.3.4"), false},
827                 {mustIP("1.2.3.4"), mustIP("1.2.3.4"), false},
828
829                 {mustIP("::1"), mustIP("::2"), true},
830                 {mustIP("::1"), mustIP("::1%foo"), true},
831                 {mustIP("::1%foo"), mustIP("::2"), true},
832                 {mustIP("::2"), mustIP("::3"), true},
833
834                 {mustIP("::"), mustIP("0.0.0.0"), false},
835                 {mustIP("0.0.0.0"), mustIP("::"), true},
836
837                 {mustIP("::1%a"), mustIP("::1%b"), true},
838                 {mustIP("::1%a"), mustIP("::1%a"), false},
839                 {mustIP("::1%b"), mustIP("::1%a"), false},
840         }
841         for _, tt := range tests {
842                 got := tt.a.Less(tt.b)
843                 if got != tt.want {
844                         t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
845                 }
846                 cmp := tt.a.Compare(tt.b)
847                 if got && cmp != -1 {
848                         t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp)
849                 }
850                 if cmp < -1 || cmp > 1 {
851                         t.Errorf("bogus Compare return value %v", cmp)
852                 }
853                 if cmp == 0 && tt.a != tt.b {
854                         t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b)
855                 }
856                 if cmp == 1 && !tt.b.Less(tt.a) {
857                         t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b)
858                 }
859
860                 // Also check inverse.
861                 if got == tt.want && got {
862                         got2 := tt.b.Less(tt.a)
863                         if got2 {
864                                 t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
865                         }
866                 }
867         }
868
869         // And just sort.
870         values := []Addr{
871                 mustIP("::1"),
872                 mustIP("::2"),
873                 Addr{},
874                 mustIP("1.2.3.4"),
875                 mustIP("8.8.8.8"),
876                 mustIP("::1%foo"),
877         }
878         sort.Slice(values, func(i, j int) bool { return values[i].Less(values[j]) })
879         got := fmt.Sprintf("%s", values)
880         want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]`
881         if got != want {
882                 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
883         }
884 }
885
886 func TestAddrPortCompare(t *testing.T) {
887         tests := []struct {
888                 a, b AddrPort
889                 want int
890         }{
891                 {AddrPort{}, AddrPort{}, 0},
892                 {AddrPort{}, mustIPPort("1.2.3.4:80"), -1},
893
894                 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:80"), 0},
895                 {mustIPPort("[::1]:80"), mustIPPort("[::1]:80"), 0},
896
897                 {mustIPPort("1.2.3.4:80"), mustIPPort("2.3.4.5:22"), -1},
898                 {mustIPPort("[::1]:80"), mustIPPort("[::2]:22"), -1},
899
900                 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:443"), -1},
901                 {mustIPPort("[::1]:80"), mustIPPort("[::1]:443"), -1},
902
903                 {mustIPPort("1.2.3.4:80"), mustIPPort("[0102:0304::0]:80"), -1},
904         }
905         for _, tt := range tests {
906                 got := tt.a.Compare(tt.b)
907                 if got != tt.want {
908                         t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
909                 }
910
911                 // Also check inverse.
912                 if got == tt.want {
913                         got2 := tt.b.Compare(tt.a)
914                         if want2 := -1 * tt.want; got2 != want2 {
915                                 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
916                         }
917                 }
918         }
919
920         // And just sort.
921         values := []AddrPort{
922                 mustIPPort("[::1]:80"),
923                 mustIPPort("[::2]:80"),
924                 AddrPort{},
925                 mustIPPort("1.2.3.4:443"),
926                 mustIPPort("8.8.8.8:8080"),
927                 mustIPPort("[::1%foo]:1024"),
928         }
929         slices.SortFunc(values, func(a, b AddrPort) int { return a.Compare(b) })
930         got := fmt.Sprintf("%s", values)
931         want := `[invalid AddrPort 1.2.3.4:443 8.8.8.8:8080 [::1]:80 [::1%foo]:1024 [::2]:80]`
932         if got != want {
933                 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
934         }
935 }
936
937 func TestPrefixCompare(t *testing.T) {
938         tests := []struct {
939                 a, b Prefix
940                 want int
941         }{
942                 {Prefix{}, Prefix{}, 0},
943                 {Prefix{}, mustPrefix("1.2.3.0/24"), -1},
944
945                 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/24"), 0},
946                 {mustPrefix("fe80::/64"), mustPrefix("fe80::/64"), 0},
947
948                 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.4.0/24"), -1},
949                 {mustPrefix("fe80::/64"), mustPrefix("fe90::/64"), -1},
950
951                 {mustPrefix("1.2.0.0/16"), mustPrefix("1.2.0.0/24"), -1},
952                 {mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
953
954                 {mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
955         }
956         for _, tt := range tests {
957                 got := tt.a.Compare(tt.b)
958                 if got != tt.want {
959                         t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
960                 }
961
962                 // Also check inverse.
963                 if got == tt.want {
964                         got2 := tt.b.Compare(tt.a)
965                         if want2 := -1 * tt.want; got2 != want2 {
966                                 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
967                         }
968                 }
969         }
970
971         // And just sort.
972         values := []Prefix{
973                 mustPrefix("1.2.3.0/24"),
974                 mustPrefix("fe90::/64"),
975                 mustPrefix("fe80::/64"),
976                 mustPrefix("1.2.0.0/16"),
977                 Prefix{},
978                 mustPrefix("fe80::/48"),
979                 mustPrefix("1.2.0.0/24"),
980         }
981         slices.SortFunc(values, func(a, b Prefix) int { return a.Compare(b) })
982         got := fmt.Sprintf("%s", values)
983         want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
984         if got != want {
985                 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
986         }
987 }
988
989 func TestIPStringExpanded(t *testing.T) {
990         tests := []struct {
991                 ip Addr
992                 s  string
993         }{
994                 {
995                         ip: Addr{},
996                         s:  "invalid IP",
997                 },
998                 {
999                         ip: mustIP("192.0.2.1"),
1000                         s:  "192.0.2.1",
1001                 },
1002                 {
1003                         ip: mustIP("::ffff:192.0.2.1"),
1004                         s:  "0000:0000:0000:0000:0000:ffff:c000:0201",
1005                 },
1006                 {
1007                         ip: mustIP("2001:db8::1"),
1008                         s:  "2001:0db8:0000:0000:0000:0000:0000:0001",
1009                 },
1010                 {
1011                         ip: mustIP("2001:db8::1%eth0"),
1012                         s:  "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
1013                 },
1014         }
1015
1016         for _, tt := range tests {
1017                 t.Run(tt.ip.String(), func(t *testing.T) {
1018                         want := tt.s
1019                         got := tt.ip.StringExpanded()
1020
1021                         if got != want {
1022                                 t.Fatalf("got %s, want %s", got, want)
1023                         }
1024                 })
1025         }
1026 }
1027
1028 func TestPrefixMasking(t *testing.T) {
1029         type subtest struct {
1030                 ip   Addr
1031                 bits uint8
1032                 p    Prefix
1033                 ok   bool
1034         }
1035
1036         // makeIPv6 produces a set of IPv6 subtests with an optional zone identifier.
1037         makeIPv6 := func(zone string) []subtest {
1038                 if zone != "" {
1039                         zone = "%" + zone
1040                 }
1041
1042                 return []subtest{
1043                         {
1044                                 ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1045                                 bits: 255,
1046                         },
1047                         {
1048                                 ip:   mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1049                                 bits: 32,
1050                                 p:    mustPrefix("2001:db8::/32"),
1051                                 ok:   true,
1052                         },
1053                         {
1054                                 ip:   mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
1055                                 bits: 96,
1056                                 p:    mustPrefix("fe80::dead:beef:0:0/96"),
1057                                 ok:   true,
1058                         },
1059                         {
1060                                 ip:   mustIP(fmt.Sprintf("aaaa::%s", zone)),
1061                                 bits: 4,
1062                                 p:    mustPrefix("a000::/4"),
1063                                 ok:   true,
1064                         },
1065                         {
1066                                 ip:   mustIP(fmt.Sprintf("::%s", zone)),
1067                                 bits: 63,
1068                                 p:    mustPrefix("::/63"),
1069                                 ok:   true,
1070                         },
1071                 }
1072         }
1073
1074         tests := []struct {
1075                 family   string
1076                 subtests []subtest
1077         }{
1078                 {
1079                         family: "nil",
1080                         subtests: []subtest{
1081                                 {
1082                                         bits: 255,
1083                                         ok:   true,
1084                                 },
1085                                 {
1086                                         bits: 16,
1087                                         ok:   true,
1088                                 },
1089                         },
1090                 },
1091                 {
1092                         family: "IPv4",
1093                         subtests: []subtest{
1094                                 {
1095                                         ip:   mustIP("192.0.2.0"),
1096                                         bits: 255,
1097                                 },
1098                                 {
1099                                         ip:   mustIP("192.0.2.0"),
1100                                         bits: 16,
1101                                         p:    mustPrefix("192.0.0.0/16"),
1102                                         ok:   true,
1103                                 },
1104                                 {
1105                                         ip:   mustIP("255.255.255.255"),
1106                                         bits: 20,
1107                                         p:    mustPrefix("255.255.240.0/20"),
1108                                         ok:   true,
1109                                 },
1110                                 {
1111                                         // Partially masking one byte that contains both
1112                                         // 1s and 0s on either side of the mask limit.
1113                                         ip:   mustIP("100.98.156.66"),
1114                                         bits: 10,
1115                                         p:    mustPrefix("100.64.0.0/10"),
1116                                         ok:   true,
1117                                 },
1118                         },
1119                 },
1120                 {
1121                         family:   "IPv6",
1122                         subtests: makeIPv6(""),
1123                 },
1124                 {
1125                         family:   "IPv6 zone",
1126                         subtests: makeIPv6("eth0"),
1127                 },
1128         }
1129
1130         for _, tt := range tests {
1131                 t.Run(tt.family, func(t *testing.T) {
1132                         for _, st := range tt.subtests {
1133                                 t.Run(st.p.String(), func(t *testing.T) {
1134                                         // Ensure st.ip is not mutated.
1135                                         orig := st.ip.String()
1136
1137                                         p, err := st.ip.Prefix(int(st.bits))
1138                                         if st.ok && err != nil {
1139                                                 t.Fatalf("failed to produce prefix: %v", err)
1140                                         }
1141                                         if !st.ok && err == nil {
1142                                                 t.Fatal("expected an error, but none occurred")
1143                                         }
1144                                         if err != nil {
1145                                                 t.Logf("err: %v", err)
1146                                                 return
1147                                         }
1148
1149                                         if !reflect.DeepEqual(p, st.p) {
1150                                                 t.Errorf("prefix = %q, want %q", p, st.p)
1151                                         }
1152
1153                                         if got := st.ip.String(); got != orig {
1154                                                 t.Errorf("IP was mutated: %q, want %q", got, orig)
1155                                         }
1156                                 })
1157                         }
1158                 })
1159         }
1160 }
1161
1162 func TestPrefixMarshalUnmarshal(t *testing.T) {
1163         tests := []string{
1164                 "",
1165                 "1.2.3.4/32",
1166                 "0.0.0.0/0",
1167                 "::/0",
1168                 "::1/128",
1169                 "2001:db8::/32",
1170         }
1171
1172         for _, s := range tests {
1173                 t.Run(s, func(t *testing.T) {
1174                         // Ensure that JSON  (and by extension, text) marshaling is
1175                         // sane by entering quoted input.
1176                         orig := `"` + s + `"`
1177
1178                         var p Prefix
1179                         if err := json.Unmarshal([]byte(orig), &p); err != nil {
1180                                 t.Fatalf("failed to unmarshal: %v", err)
1181                         }
1182
1183                         pb, err := json.Marshal(p)
1184                         if err != nil {
1185                                 t.Fatalf("failed to marshal: %v", err)
1186                         }
1187
1188                         back := string(pb)
1189                         if orig != back {
1190                                 t.Errorf("Marshal = %q; want %q", back, orig)
1191                         }
1192                 })
1193         }
1194 }
1195
1196 func TestPrefixUnmarshalTextNonZero(t *testing.T) {
1197         ip := mustPrefix("fe80::/64")
1198         if err := ip.UnmarshalText([]byte("xxx")); err == nil {
1199                 t.Fatal("unmarshaled into non-empty Prefix")
1200         }
1201 }
1202
1203 func TestIs4AndIs6(t *testing.T) {
1204         tests := []struct {
1205                 ip  Addr
1206                 is4 bool
1207                 is6 bool
1208         }{
1209                 {Addr{}, false, false},
1210                 {mustIP("1.2.3.4"), true, false},
1211                 {mustIP("127.0.0.2"), true, false},
1212                 {mustIP("::1"), false, true},
1213                 {mustIP("::ffff:192.0.2.128"), false, true},
1214                 {mustIP("::fffe:c000:0280"), false, true},
1215                 {mustIP("::1%eth0"), false, true},
1216         }
1217         for _, tt := range tests {
1218                 got4 := tt.ip.Is4()
1219                 if got4 != tt.is4 {
1220                         t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
1221                 }
1222
1223                 got6 := tt.ip.Is6()
1224                 if got6 != tt.is6 {
1225                         t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
1226                 }
1227         }
1228 }
1229
1230 func TestIs4In6(t *testing.T) {
1231         tests := []struct {
1232                 ip        Addr
1233                 want      bool
1234                 wantUnmap Addr
1235         }{
1236                 {Addr{}, false, Addr{}},
1237                 {mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
1238                 {mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
1239                 {mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
1240                 {mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
1241                 {mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1242                 {mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
1243                 {mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1244                 {mustIP("0:0:0:0:000000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1245                 {mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1246                 {mustIP("::1"), false, mustIP("::1")},
1247                 {mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
1248         }
1249         for _, tt := range tests {
1250                 got := tt.ip.Is4In6()
1251                 if got != tt.want {
1252                         t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
1253                 }
1254                 u := tt.ip.Unmap()
1255                 if u != tt.wantUnmap {
1256                         t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
1257                 }
1258         }
1259 }
1260
1261 func TestPrefixMasked(t *testing.T) {
1262         tests := []struct {
1263                 prefix Prefix
1264                 masked Prefix
1265         }{
1266                 {
1267                         prefix: mustPrefix("192.168.0.255/24"),
1268                         masked: mustPrefix("192.168.0.0/24"),
1269                 },
1270                 {
1271                         prefix: mustPrefix("2100::/3"),
1272                         masked: mustPrefix("2000::/3"),
1273                 },
1274                 {
1275                         prefix: PrefixFrom(mustIP("2000::"), 129),
1276                         masked: Prefix{},
1277                 },
1278                 {
1279                         prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
1280                         masked: Prefix{},
1281                 },
1282         }
1283         for _, test := range tests {
1284                 t.Run(test.prefix.String(), func(t *testing.T) {
1285                         got := test.prefix.Masked()
1286                         if got != test.masked {
1287                                 t.Errorf("Masked=%s, want %s", got, test.masked)
1288                         }
1289                 })
1290         }
1291 }
1292
1293 func TestPrefix(t *testing.T) {
1294         tests := []struct {
1295                 prefix      string
1296                 ip          Addr
1297                 bits        int
1298                 str         string
1299                 contains    []Addr
1300                 notContains []Addr
1301         }{
1302                 {
1303                         prefix:      "192.168.0.0/24",
1304                         ip:          mustIP("192.168.0.0"),
1305                         bits:        24,
1306                         contains:    mustIPs("192.168.0.1", "192.168.0.55"),
1307                         notContains: mustIPs("192.168.1.1", "1.1.1.1"),
1308                 },
1309                 {
1310                         prefix:      "192.168.1.1/32",
1311                         ip:          mustIP("192.168.1.1"),
1312                         bits:        32,
1313                         contains:    mustIPs("192.168.1.1"),
1314                         notContains: mustIPs("192.168.1.2"),
1315                 },
1316                 {
1317                         prefix:      "100.64.0.0/10", // CGNAT range; prefix not multiple of 8
1318                         ip:          mustIP("100.64.0.0"),
1319                         bits:        10,
1320                         contains:    mustIPs("100.64.0.0", "100.64.0.1", "100.81.251.94", "100.100.100.100", "100.127.255.254", "100.127.255.255"),
1321                         notContains: mustIPs("100.63.255.255", "100.128.0.0"),
1322                 },
1323                 {
1324                         prefix:      "2001:db8::/96",
1325                         ip:          mustIP("2001:db8::"),
1326                         bits:        96,
1327                         contains:    mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
1328                         notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
1329                 },
1330                 {
1331                         prefix:      "0.0.0.0/0",
1332                         ip:          mustIP("0.0.0.0"),
1333                         bits:        0,
1334                         contains:    mustIPs("192.168.0.1", "1.1.1.1"),
1335                         notContains: append(mustIPs("2001:db8::1"), Addr{}),
1336                 },
1337                 {
1338                         prefix:      "::/0",
1339                         ip:          mustIP("::"),
1340                         bits:        0,
1341                         contains:    mustIPs("::1", "2001:db8::1"),
1342                         notContains: mustIPs("192.0.2.1"),
1343                 },
1344                 {
1345                         prefix:      "2000::/3",
1346                         ip:          mustIP("2000::"),
1347                         bits:        3,
1348                         contains:    mustIPs("2001:db8::1"),
1349                         notContains: mustIPs("fe80::1"),
1350                 },
1351         }
1352         for _, test := range tests {
1353                 t.Run(test.prefix, func(t *testing.T) {
1354                         prefix, err := ParsePrefix(test.prefix)
1355                         if err != nil {
1356                                 t.Fatal(err)
1357                         }
1358                         if prefix.Addr() != test.ip {
1359                                 t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
1360                         }
1361                         if prefix.Bits() != test.bits {
1362                                 t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
1363                         }
1364                         for _, ip := range test.contains {
1365                                 if !prefix.Contains(ip) {
1366                                         t.Errorf("does not contain %s", ip)
1367                                 }
1368                         }
1369                         for _, ip := range test.notContains {
1370                                 if prefix.Contains(ip) {
1371                                         t.Errorf("contains %s", ip)
1372                                 }
1373                         }
1374                         want := test.str
1375                         if want == "" {
1376                                 want = test.prefix
1377                         }
1378                         if got := prefix.String(); got != want {
1379                                 t.Errorf("prefix.String()=%q, want %q", got, want)
1380                         }
1381
1382                         TestAppendToMarshal(t, prefix)
1383                 })
1384         }
1385 }
1386
1387 func TestPrefixFromInvalidBits(t *testing.T) {
1388         v4 := MustParseAddr("1.2.3.4")
1389         v6 := MustParseAddr("66::66")
1390         tests := []struct {
1391                 ip       Addr
1392                 in, want int
1393         }{
1394                 {v4, 0, 0},
1395                 {v6, 0, 0},
1396                 {v4, 1, 1},
1397                 {v4, 33, -1},
1398                 {v6, 33, 33},
1399                 {v6, 127, 127},
1400                 {v6, 128, 128},
1401                 {v4, 254, -1},
1402                 {v4, 255, -1},
1403                 {v4, -1, -1},
1404                 {v6, -1, -1},
1405                 {v4, -5, -1},
1406                 {v6, -5, -1},
1407         }
1408         for _, tt := range tests {
1409                 p := PrefixFrom(tt.ip, tt.in)
1410                 if got := p.Bits(); got != tt.want {
1411                         t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
1412                 }
1413         }
1414 }
1415
1416 func TestParsePrefixAllocs(t *testing.T) {
1417         tests := []struct {
1418                 ip    string
1419                 slash string
1420         }{
1421                 {"192.168.1.0", "/24"},
1422                 {"aaaa:bbbb:cccc::", "/24"},
1423         }
1424         for _, test := range tests {
1425                 prefix := test.ip + test.slash
1426                 t.Run(prefix, func(t *testing.T) {
1427                         ipAllocs := int(testing.AllocsPerRun(5, func() {
1428                                 ParseAddr(test.ip)
1429                         }))
1430                         prefixAllocs := int(testing.AllocsPerRun(5, func() {
1431                                 ParsePrefix(prefix)
1432                         }))
1433                         if got := prefixAllocs - ipAllocs; got != 0 {
1434                                 t.Errorf("allocs=%d, want 0", got)
1435                         }
1436                 })
1437         }
1438 }
1439
1440 func TestParsePrefixError(t *testing.T) {
1441         tests := []struct {
1442                 prefix string
1443                 errstr string
1444         }{
1445                 {
1446                         prefix: "192.168.0.0",
1447                         errstr: "no '/'",
1448                 },
1449                 {
1450                         prefix: "1.257.1.1/24",
1451                         errstr: "value >255",
1452                 },
1453                 {
1454                         prefix: "1.1.1.0/q",
1455                         errstr: "bad bits",
1456                 },
1457                 {
1458                         prefix: "1.1.1.0/-1",
1459                         errstr: "bad bits",
1460                 },
1461                 {
1462                         prefix: "1.1.1.0/33",
1463                         errstr: "out of range",
1464                 },
1465                 {
1466                         prefix: "2001::/129",
1467                         errstr: "out of range",
1468                 },
1469                 // Zones are not allowed: https://go.dev/issue/51899
1470                 {
1471                         prefix: "1.1.1.0%a/24",
1472                         errstr: "unexpected character",
1473                 },
1474                 {
1475                         prefix: "2001:db8::%a/32",
1476                         errstr: "zones cannot be present",
1477                 },
1478                 {
1479                         prefix: "1.1.1.0/+32",
1480                         errstr: "bad bits",
1481                 },
1482                 {
1483                         prefix: "1.1.1.0/-32",
1484                         errstr: "bad bits",
1485                 },
1486                 {
1487                         prefix: "1.1.1.0/032",
1488                         errstr: "bad bits",
1489                 },
1490                 {
1491                         prefix: "1.1.1.0/0032",
1492                         errstr: "bad bits",
1493                 },
1494         }
1495         for _, test := range tests {
1496                 t.Run(test.prefix, func(t *testing.T) {
1497                         _, err := ParsePrefix(test.prefix)
1498                         if err == nil {
1499                                 t.Fatal("no error")
1500                         }
1501                         if got := err.Error(); !strings.Contains(got, test.errstr) {
1502                                 t.Errorf("error is missing substring %q: %s", test.errstr, got)
1503                         }
1504                 })
1505         }
1506 }
1507
1508 func TestPrefixIsSingleIP(t *testing.T) {
1509         tests := []struct {
1510                 ipp  Prefix
1511                 want bool
1512         }{
1513                 {ipp: mustPrefix("127.0.0.1/32"), want: true},
1514                 {ipp: mustPrefix("127.0.0.1/31"), want: false},
1515                 {ipp: mustPrefix("127.0.0.1/0"), want: false},
1516                 {ipp: mustPrefix("::1/128"), want: true},
1517                 {ipp: mustPrefix("::1/127"), want: false},
1518                 {ipp: mustPrefix("::1/0"), want: false},
1519                 {ipp: Prefix{}, want: false},
1520         }
1521         for _, tt := range tests {
1522                 got := tt.ipp.IsSingleIP()
1523                 if got != tt.want {
1524                         t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
1525                 }
1526         }
1527 }
1528
1529 func mustIPs(strs ...string) []Addr {
1530         var res []Addr
1531         for _, s := range strs {
1532                 res = append(res, mustIP(s))
1533         }
1534         return res
1535 }
1536
1537 func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
1538         b.ReportAllocs()
1539         tests := []struct {
1540                 name string
1541                 ip   string
1542         }{
1543                 {"ipv4", "1.2.3.4"},
1544                 {"ipv6", "2001:db8::1"},
1545                 {"ipv6+zone", "2001:db8::1%eth0"},
1546         }
1547         for _, tc := range tests {
1548                 b.Run(tc.name, func(b *testing.B) {
1549                         ip := mustIP(tc.ip)
1550                         for i := 0; i < b.N; i++ {
1551                                 bt, err := ip.MarshalBinary()
1552                                 if err != nil {
1553                                         b.Fatal(err)
1554                                 }
1555                                 var ip2 Addr
1556                                 if err := ip2.UnmarshalBinary(bt); err != nil {
1557                                         b.Fatal(err)
1558                                 }
1559                         }
1560                 })
1561         }
1562 }
1563
1564 func BenchmarkStdIPv4(b *testing.B) {
1565         b.ReportAllocs()
1566         ips := []net.IP{}
1567         for i := 0; i < b.N; i++ {
1568                 ip := net.IPv4(8, 8, 8, 8)
1569                 ips = ips[:0]
1570                 for i := 0; i < 100; i++ {
1571                         ips = append(ips, ip)
1572                 }
1573         }
1574 }
1575
1576 func BenchmarkIPv4(b *testing.B) {
1577         b.ReportAllocs()
1578         ips := []Addr{}
1579         for i := 0; i < b.N; i++ {
1580                 ip := IPv4(8, 8, 8, 8)
1581                 ips = ips[:0]
1582                 for i := 0; i < 100; i++ {
1583                         ips = append(ips, ip)
1584                 }
1585         }
1586 }
1587
1588 // ip4i was one of the possible representations of IP that came up in
1589 // discussions, inlining IPv4 addresses, but having an "overflow"
1590 // interface for IPv6 or IPv6 + zone. This is here for benchmarking.
1591 type ip4i struct {
1592         ip4    [4]byte
1593         flags1 byte
1594         flags2 byte
1595         flags3 byte
1596         flags4 byte
1597         ipv6   any
1598 }
1599
1600 func newip4i_v4(a, b, c, d byte) ip4i {
1601         return ip4i{ip4: [4]byte{a, b, c, d}}
1602 }
1603
1604 // BenchmarkIPv4_inline benchmarks the candidate representation, ip4i.
1605 func BenchmarkIPv4_inline(b *testing.B) {
1606         b.ReportAllocs()
1607         ips := []ip4i{}
1608         for i := 0; i < b.N; i++ {
1609                 ip := newip4i_v4(8, 8, 8, 8)
1610                 ips = ips[:0]
1611                 for i := 0; i < 100; i++ {
1612                         ips = append(ips, ip)
1613                 }
1614         }
1615 }
1616
1617 func BenchmarkStdIPv6(b *testing.B) {
1618         b.ReportAllocs()
1619         ips := []net.IP{}
1620         for i := 0; i < b.N; i++ {
1621                 ip := net.ParseIP("2001:db8::1")
1622                 ips = ips[:0]
1623                 for i := 0; i < 100; i++ {
1624                         ips = append(ips, ip)
1625                 }
1626         }
1627 }
1628
1629 func BenchmarkIPv6(b *testing.B) {
1630         b.ReportAllocs()
1631         ips := []Addr{}
1632         for i := 0; i < b.N; i++ {
1633                 ip := mustIP("2001:db8::1")
1634                 ips = ips[:0]
1635                 for i := 0; i < 100; i++ {
1636                         ips = append(ips, ip)
1637                 }
1638         }
1639 }
1640
1641 func BenchmarkIPv4Contains(b *testing.B) {
1642         b.ReportAllocs()
1643         prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
1644         ip := IPv4(192, 168, 1, 1)
1645         for i := 0; i < b.N; i++ {
1646                 prefix.Contains(ip)
1647         }
1648 }
1649
1650 func BenchmarkIPv6Contains(b *testing.B) {
1651         b.ReportAllocs()
1652         prefix := MustParsePrefix("::1/128")
1653         ip := MustParseAddr("::1")
1654         for i := 0; i < b.N; i++ {
1655                 prefix.Contains(ip)
1656         }
1657 }
1658
1659 var parseBenchInputs = []struct {
1660         name string
1661         ip   string
1662 }{
1663         {"v4", "192.168.1.1"},
1664         {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
1665         {"v6_ellipsis", "fd7a:115c::626b:430b"},
1666         {"v6_v4", "::ffff:192.168.140.255"},
1667         {"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
1668 }
1669
1670 func BenchmarkParseAddr(b *testing.B) {
1671         sinkInternValue = intern.Get("eth1") // Pin to not benchmark the intern package
1672         for _, test := range parseBenchInputs {
1673                 b.Run(test.name, func(b *testing.B) {
1674                         b.ReportAllocs()
1675                         for i := 0; i < b.N; i++ {
1676                                 sinkIP, _ = ParseAddr(test.ip)
1677                         }
1678                 })
1679         }
1680 }
1681
1682 func BenchmarkStdParseIP(b *testing.B) {
1683         for _, test := range parseBenchInputs {
1684                 b.Run(test.name, func(b *testing.B) {
1685                         b.ReportAllocs()
1686                         for i := 0; i < b.N; i++ {
1687                                 sinkStdIP = net.ParseIP(test.ip)
1688                         }
1689                 })
1690         }
1691 }
1692
1693 func BenchmarkIPString(b *testing.B) {
1694         for _, test := range parseBenchInputs {
1695                 ip := MustParseAddr(test.ip)
1696                 b.Run(test.name, func(b *testing.B) {
1697                         b.ReportAllocs()
1698                         for i := 0; i < b.N; i++ {
1699                                 sinkString = ip.String()
1700                         }
1701                 })
1702         }
1703 }
1704
1705 func BenchmarkIPStringExpanded(b *testing.B) {
1706         for _, test := range parseBenchInputs {
1707                 ip := MustParseAddr(test.ip)
1708                 b.Run(test.name, func(b *testing.B) {
1709                         b.ReportAllocs()
1710                         for i := 0; i < b.N; i++ {
1711                                 sinkString = ip.StringExpanded()
1712                         }
1713                 })
1714         }
1715 }
1716
1717 func BenchmarkIPMarshalText(b *testing.B) {
1718         b.ReportAllocs()
1719         ip := MustParseAddr("66.55.44.33")
1720         for i := 0; i < b.N; i++ {
1721                 sinkBytes, _ = ip.MarshalText()
1722         }
1723 }
1724
1725 func BenchmarkAddrPortString(b *testing.B) {
1726         for _, test := range parseBenchInputs {
1727                 ip := MustParseAddr(test.ip)
1728                 ipp := AddrPortFrom(ip, 60000)
1729                 b.Run(test.name, func(b *testing.B) {
1730                         b.ReportAllocs()
1731                         for i := 0; i < b.N; i++ {
1732                                 sinkString = ipp.String()
1733                         }
1734                 })
1735         }
1736 }
1737
1738 func BenchmarkAddrPortMarshalText(b *testing.B) {
1739         for _, test := range parseBenchInputs {
1740                 ip := MustParseAddr(test.ip)
1741                 ipp := AddrPortFrom(ip, 60000)
1742                 b.Run(test.name, func(b *testing.B) {
1743                         b.ReportAllocs()
1744                         for i := 0; i < b.N; i++ {
1745                                 sinkBytes, _ = ipp.MarshalText()
1746                         }
1747                 })
1748         }
1749 }
1750
1751 func BenchmarkPrefixMasking(b *testing.B) {
1752         tests := []struct {
1753                 name string
1754                 ip   Addr
1755                 bits int
1756         }{
1757                 {
1758                         name: "IPv4 /32",
1759                         ip:   IPv4(192, 0, 2, 0),
1760                         bits: 32,
1761                 },
1762                 {
1763                         name: "IPv4 /17",
1764                         ip:   IPv4(192, 0, 2, 0),
1765                         bits: 17,
1766                 },
1767                 {
1768                         name: "IPv4 /0",
1769                         ip:   IPv4(192, 0, 2, 0),
1770                         bits: 0,
1771                 },
1772                 {
1773                         name: "IPv6 /128",
1774                         ip:   mustIP("2001:db8::1"),
1775                         bits: 128,
1776                 },
1777                 {
1778                         name: "IPv6 /65",
1779                         ip:   mustIP("2001:db8::1"),
1780                         bits: 65,
1781                 },
1782                 {
1783                         name: "IPv6 /0",
1784                         ip:   mustIP("2001:db8::1"),
1785                         bits: 0,
1786                 },
1787                 {
1788                         name: "IPv6 zone /128",
1789                         ip:   mustIP("2001:db8::1%eth0"),
1790                         bits: 128,
1791                 },
1792                 {
1793                         name: "IPv6 zone /65",
1794                         ip:   mustIP("2001:db8::1%eth0"),
1795                         bits: 65,
1796                 },
1797                 {
1798                         name: "IPv6 zone /0",
1799                         ip:   mustIP("2001:db8::1%eth0"),
1800                         bits: 0,
1801                 },
1802         }
1803
1804         for _, tt := range tests {
1805                 b.Run(tt.name, func(b *testing.B) {
1806                         b.ReportAllocs()
1807
1808                         for i := 0; i < b.N; i++ {
1809                                 sinkPrefix, _ = tt.ip.Prefix(tt.bits)
1810                         }
1811                 })
1812         }
1813 }
1814
1815 func BenchmarkPrefixMarshalText(b *testing.B) {
1816         b.ReportAllocs()
1817         ipp := MustParsePrefix("66.55.44.33/22")
1818         for i := 0; i < b.N; i++ {
1819                 sinkBytes, _ = ipp.MarshalText()
1820         }
1821 }
1822
1823 func BenchmarkParseAddrPort(b *testing.B) {
1824         for _, test := range parseBenchInputs {
1825                 var ipp string
1826                 if strings.HasPrefix(test.name, "v6") {
1827                         ipp = fmt.Sprintf("[%s]:1234", test.ip)
1828                 } else {
1829                         ipp = fmt.Sprintf("%s:1234", test.ip)
1830                 }
1831                 b.Run(test.name, func(b *testing.B) {
1832                         b.ReportAllocs()
1833
1834                         for i := 0; i < b.N; i++ {
1835                                 sinkAddrPort, _ = ParseAddrPort(ipp)
1836                         }
1837                 })
1838         }
1839 }
1840
1841 func TestAs4(t *testing.T) {
1842         tests := []struct {
1843                 ip        Addr
1844                 want      [4]byte
1845                 wantPanic bool
1846         }{
1847                 {
1848                         ip:   mustIP("1.2.3.4"),
1849                         want: [4]byte{1, 2, 3, 4},
1850                 },
1851                 {
1852                         ip:   AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6
1853                         want: [4]byte{1, 2, 3, 4},
1854                 },
1855                 {
1856                         ip:   mustIP("0.0.0.0"),
1857                         want: [4]byte{0, 0, 0, 0},
1858                 },
1859                 {
1860                         ip:        Addr{},
1861                         wantPanic: true,
1862                 },
1863                 {
1864                         ip:        mustIP("::1"),
1865                         wantPanic: true,
1866                 },
1867         }
1868         as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
1869                 defer func() {
1870                         if recover() != nil {
1871                                 gotPanic = true
1872                                 return
1873                         }
1874                 }()
1875                 v = ip.As4()
1876                 return
1877         }
1878         for i, tt := range tests {
1879                 got, gotPanic := as4(tt.ip)
1880                 if gotPanic != tt.wantPanic {
1881                         t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
1882                         continue
1883                 }
1884                 if got != tt.want {
1885                         t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
1886                 }
1887         }
1888 }
1889
1890 func TestPrefixOverlaps(t *testing.T) {
1891         pfx := mustPrefix
1892         tests := []struct {
1893                 a, b Prefix
1894                 want bool
1895         }{
1896                 {Prefix{}, pfx("1.2.0.0/16"), false},    // first zero
1897                 {pfx("1.2.0.0/16"), Prefix{}, false},    // second zero
1898                 {pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families
1899
1900                 {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal
1901
1902                 {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
1903                 {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
1904
1905                 {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
1906                 {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
1907
1908                 // Match /0 either order
1909                 {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
1910                 {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
1911
1912                 {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true
1913
1914                 // IPv6 overlapping
1915                 {pfx("5::1/128"), pfx("5::0/8"), true},
1916                 {pfx("5::0/8"), pfx("5::1/128"), true},
1917
1918                 // IPv6 not overlapping
1919                 {pfx("1::1/128"), pfx("2::2/128"), false},
1920                 {pfx("0100::0/8"), pfx("::1/128"), false},
1921
1922                 // IPv4-mapped IPv6 addresses should not overlap with IPv4.
1923                 {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
1924
1925                 // Invalid prefixes
1926                 {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
1927                 {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
1928         }
1929         for i, tt := range tests {
1930                 if got := tt.a.Overlaps(tt.b); got != tt.want {
1931                         t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
1932                 }
1933                 // Overlaps is commutative
1934                 if got := tt.b.Overlaps(tt.a); got != tt.want {
1935                         t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
1936                 }
1937         }
1938 }
1939
1940 // Sink variables are here to force the compiler to not elide
1941 // seemingly useless work in benchmarks and allocation tests. If you
1942 // were to just `_ = foo()` within a test function, the compiler could
1943 // correctly deduce that foo() does nothing and doesn't need to be
1944 // called. By writing results to a global variable, we hide that fact
1945 // from the compiler and force it to keep the code under test.
1946 var (
1947         sinkIP          Addr
1948         sinkStdIP       net.IP
1949         sinkAddrPort    AddrPort
1950         sinkPrefix      Prefix
1951         sinkPrefixSlice []Prefix
1952         sinkInternValue *intern.Value
1953         sinkIP16        [16]byte
1954         sinkIP4         [4]byte
1955         sinkBool        bool
1956         sinkString      string
1957         sinkBytes       []byte
1958         sinkUDPAddr     = &net.UDPAddr{IP: make(net.IP, 0, 16)}
1959 )
1960
1961 func TestNoAllocs(t *testing.T) {
1962         // Wrappers that panic on error, to prove that our alloc-free
1963         // methods are returning successfully.
1964         panicIP := func(ip Addr, err error) Addr {
1965                 if err != nil {
1966                         panic(err)
1967                 }
1968                 return ip
1969         }
1970         panicPfx := func(pfx Prefix, err error) Prefix {
1971                 if err != nil {
1972                         panic(err)
1973                 }
1974                 return pfx
1975         }
1976         panicIPP := func(ipp AddrPort, err error) AddrPort {
1977                 if err != nil {
1978                         panic(err)
1979                 }
1980                 return ipp
1981         }
1982         test := func(name string, f func()) {
1983                 t.Run(name, func(t *testing.T) {
1984                         n := testing.AllocsPerRun(1000, f)
1985                         if n != 0 {
1986                                 t.Fatalf("allocs = %d; want 0", int(n))
1987                         }
1988                 })
1989         }
1990
1991         // Addr constructors
1992         test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
1993         test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
1994         test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
1995         test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
1996         test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
1997         test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
1998         test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
1999         test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2000         test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2001         test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2002
2003         // Addr methods
2004         test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2005         test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2006         test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2007         test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2008         test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2009         test("Addr.Compare", func() {
2010                 a := MustParseAddr("1.2.3.4")
2011                 b := MustParseAddr("2.3.4.5")
2012                 sinkBool = a.Compare(b) == 0
2013         })
2014         test("Addr.Less", func() {
2015                 a := MustParseAddr("1.2.3.4")
2016                 b := MustParseAddr("2.3.4.5")
2017                 sinkBool = a.Less(b)
2018         })
2019         test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2020         test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2021         test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2022         test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2023         test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2024         test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2025         test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2026         test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2027         test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2028         test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2029         test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2030         test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2031         test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2032         test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2033         test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2034         test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2035         test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2036         test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2037         test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2038
2039         // AddrPort constructors
2040         test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2041         test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2042         test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2043
2044         // Prefix constructors
2045         test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2046         test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2047         test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2048         test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2049
2050         // Prefix methods
2051         test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2052         test("Prefix.Overlaps", func() {
2053                 a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2054                 sinkBool = a.Overlaps(b)
2055         })
2056         test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2057         test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2058         test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2059 }
2060
2061 func TestAddrStringAllocs(t *testing.T) {
2062         tests := []struct {
2063                 name       string
2064                 ip         Addr
2065                 wantAllocs int
2066         }{
2067                 {"zero", Addr{}, 0},
2068                 {"ipv4", MustParseAddr("192.168.1.1"), 1},
2069                 {"ipv6", MustParseAddr("2001:db8::1"), 1},
2070                 {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2071                 {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2072                 {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2073         }
2074         optimizationOff := testenv.OptimizationOff()
2075         for _, tc := range tests {
2076                 t.Run(tc.name, func(t *testing.T) {
2077                         if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2078                                 // Optimizations are required to remove some allocs.
2079                                 t.Skipf("skipping on %v", testenv.Builder())
2080                         }
2081                         allocs := int(testing.AllocsPerRun(1000, func() {
2082                                 sinkString = tc.ip.String()
2083                         }))
2084                         if allocs != tc.wantAllocs {
2085                                 t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2086                         }
2087                 })
2088         }
2089 }
2090
2091 func TestPrefixString(t *testing.T) {
2092         tests := []struct {
2093                 ipp  Prefix
2094                 want string
2095         }{
2096                 {Prefix{}, "invalid Prefix"},
2097                 {PrefixFrom(Addr{}, 8), "invalid Prefix"},
2098                 {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2099         }
2100
2101         for _, tt := range tests {
2102                 if got := tt.ipp.String(); got != tt.want {
2103                         t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2104                 }
2105         }
2106 }
2107
2108 func TestInvalidAddrPortString(t *testing.T) {
2109         tests := []struct {
2110                 ipp  AddrPort
2111                 want string
2112         }{
2113                 {AddrPort{}, "invalid AddrPort"},
2114                 {AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2115         }
2116
2117         for _, tt := range tests {
2118                 if got := tt.ipp.String(); got != tt.want {
2119                         t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2120                 }
2121         }
2122 }
2123
2124 func TestAsSlice(t *testing.T) {
2125         tests := []struct {
2126                 in   Addr
2127                 want []byte
2128         }{
2129                 {in: Addr{}, want: nil},
2130                 {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2131                 {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2132         }
2133
2134         for _, test := range tests {
2135                 got := test.in.AsSlice()
2136                 if !bytes.Equal(got, test.want) {
2137                         t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2138                 }
2139         }
2140 }
2141
2142 var sink16 [16]byte
2143
2144 func BenchmarkAs16(b *testing.B) {
2145         addr := MustParseAddr("1::10")
2146         for i := 0; i < b.N; i++ {
2147                 sink16 = addr.As16()
2148         }
2149 }