1// Copyright 2013 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 net
6
7import (
8	"reflect"
9	"testing"
10)
11
12var testInetaddr = func(ip IPAddr) Addr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
13
14var addrListTests = []struct {
15	filter    func(IPAddr) bool
16	ips       []IPAddr
17	inetaddr  func(IPAddr) Addr
18	first     Addr
19	primaries addrList
20	fallbacks addrList
21	err       error
22}{
23	{
24		nil,
25		[]IPAddr{
26			{IP: IPv4(127, 0, 0, 1)},
27			{IP: IPv6loopback},
28		},
29		testInetaddr,
30		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
31		addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
32		addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
33		nil,
34	},
35	{
36		nil,
37		[]IPAddr{
38			{IP: IPv6loopback},
39			{IP: IPv4(127, 0, 0, 1)},
40		},
41		testInetaddr,
42		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
43		addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
44		addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
45		nil,
46	},
47	{
48		nil,
49		[]IPAddr{
50			{IP: IPv4(127, 0, 0, 1)},
51			{IP: IPv4(192, 168, 0, 1)},
52		},
53		testInetaddr,
54		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
55		addrList{
56			&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
57			&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
58		},
59		nil,
60		nil,
61	},
62	{
63		nil,
64		[]IPAddr{
65			{IP: IPv6loopback},
66			{IP: ParseIP("fe80::1"), Zone: "eth0"},
67		},
68		testInetaddr,
69		&TCPAddr{IP: IPv6loopback, Port: 5682},
70		addrList{
71			&TCPAddr{IP: IPv6loopback, Port: 5682},
72			&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
73		},
74		nil,
75		nil,
76	},
77	{
78		nil,
79		[]IPAddr{
80			{IP: IPv4(127, 0, 0, 1)},
81			{IP: IPv4(192, 168, 0, 1)},
82			{IP: IPv6loopback},
83			{IP: ParseIP("fe80::1"), Zone: "eth0"},
84		},
85		testInetaddr,
86		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
87		addrList{
88			&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
89			&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
90		},
91		addrList{
92			&TCPAddr{IP: IPv6loopback, Port: 5682},
93			&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
94		},
95		nil,
96	},
97	{
98		nil,
99		[]IPAddr{
100			{IP: IPv6loopback},
101			{IP: ParseIP("fe80::1"), Zone: "eth0"},
102			{IP: IPv4(127, 0, 0, 1)},
103			{IP: IPv4(192, 168, 0, 1)},
104		},
105		testInetaddr,
106		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
107		addrList{
108			&TCPAddr{IP: IPv6loopback, Port: 5682},
109			&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
110		},
111		addrList{
112			&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
113			&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
114		},
115		nil,
116	},
117	{
118		nil,
119		[]IPAddr{
120			{IP: IPv4(127, 0, 0, 1)},
121			{IP: IPv6loopback},
122			{IP: IPv4(192, 168, 0, 1)},
123			{IP: ParseIP("fe80::1"), Zone: "eth0"},
124		},
125		testInetaddr,
126		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
127		addrList{
128			&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
129			&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
130		},
131		addrList{
132			&TCPAddr{IP: IPv6loopback, Port: 5682},
133			&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
134		},
135		nil,
136	},
137	{
138		nil,
139		[]IPAddr{
140			{IP: IPv6loopback},
141			{IP: IPv4(127, 0, 0, 1)},
142			{IP: ParseIP("fe80::1"), Zone: "eth0"},
143			{IP: IPv4(192, 168, 0, 1)},
144		},
145		testInetaddr,
146		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
147		addrList{
148			&TCPAddr{IP: IPv6loopback, Port: 5682},
149			&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
150		},
151		addrList{
152			&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
153			&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
154		},
155		nil,
156	},
157
158	{
159		ipv4only,
160		[]IPAddr{
161			{IP: IPv4(127, 0, 0, 1)},
162			{IP: IPv6loopback},
163		},
164		testInetaddr,
165		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
166		addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
167		nil,
168		nil,
169	},
170	{
171		ipv4only,
172		[]IPAddr{
173			{IP: IPv6loopback},
174			{IP: IPv4(127, 0, 0, 1)},
175		},
176		testInetaddr,
177		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
178		addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
179		nil,
180		nil,
181	},
182
183	{
184		ipv6only,
185		[]IPAddr{
186			{IP: IPv4(127, 0, 0, 1)},
187			{IP: IPv6loopback},
188		},
189		testInetaddr,
190		&TCPAddr{IP: IPv6loopback, Port: 5682},
191		addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
192		nil,
193		nil,
194	},
195	{
196		ipv6only,
197		[]IPAddr{
198			{IP: IPv6loopback},
199			{IP: IPv4(127, 0, 0, 1)},
200		},
201		testInetaddr,
202		&TCPAddr{IP: IPv6loopback, Port: 5682},
203		addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
204		nil,
205		nil,
206	},
207
208	{nil, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
209
210	{ipv4only, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
211	{ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
212
213	{ipv6only, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
214	{ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
215}
216
217func TestAddrList(t *testing.T) {
218	if !supportsIPv4() || !supportsIPv6() {
219		t.Skip("both IPv4 and IPv6 are required")
220	}
221
222	for i, tt := range addrListTests {
223		addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr, "ADDR")
224		if !reflect.DeepEqual(err, tt.err) {
225			t.Errorf("#%v: got %v; want %v", i, err, tt.err)
226		}
227		if tt.err != nil {
228			if len(addrs) != 0 {
229				t.Errorf("#%v: got %v; want 0", i, len(addrs))
230			}
231			continue
232		}
233		first := addrs.first(isIPv4)
234		if !reflect.DeepEqual(first, tt.first) {
235			t.Errorf("#%v: got %v; want %v", i, first, tt.first)
236		}
237		primaries, fallbacks := addrs.partition(isIPv4)
238		if !reflect.DeepEqual(primaries, tt.primaries) {
239			t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
240		}
241		if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
242			t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
243		}
244		expectedLen := len(primaries) + len(fallbacks)
245		if len(addrs) != expectedLen {
246			t.Errorf("#%v: got %v; want %v", i, len(addrs), expectedLen)
247		}
248	}
249}
250
251func TestAddrListPartition(t *testing.T) {
252	addrs := addrList{
253		&IPAddr{IP: ParseIP("fe80::"), Zone: "eth0"},
254		&IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
255		&IPAddr{IP: ParseIP("fe80::2"), Zone: "eth0"},
256	}
257	cases := []struct {
258		lastByte  byte
259		primaries addrList
260		fallbacks addrList
261	}{
262		{0, addrList{addrs[0]}, addrList{addrs[1], addrs[2]}},
263		{1, addrList{addrs[0], addrs[2]}, addrList{addrs[1]}},
264		{2, addrList{addrs[0], addrs[1]}, addrList{addrs[2]}},
265		{3, addrList{addrs[0], addrs[1], addrs[2]}, nil},
266	}
267	for i, tt := range cases {
268		// Inverting the function's output should not affect the outcome.
269		for _, invert := range []bool{false, true} {
270			primaries, fallbacks := addrs.partition(func(a Addr) bool {
271				ip := a.(*IPAddr).IP
272				return (ip[len(ip)-1] == tt.lastByte) != invert
273			})
274			if !reflect.DeepEqual(primaries, tt.primaries) {
275				t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
276			}
277			if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
278				t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
279			}
280		}
281	}
282}
283