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