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