1// Copyright 2012 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	"internal/testenv"
9	"reflect"
10	"runtime"
11	"testing"
12	"time"
13)
14
15func BenchmarkUDP6LinkLocalUnicast(b *testing.B) {
16	testHookUninstaller.Do(uninstallTestHooks)
17
18	if !supportsIPv6() {
19		b.Skip("IPv6 is not supported")
20	}
21	ifi := loopbackInterface()
22	if ifi == nil {
23		b.Skip("loopback interface not found")
24	}
25	lla := ipv6LinkLocalUnicastAddr(ifi)
26	if lla == "" {
27		b.Skip("IPv6 link-local unicast address not found")
28	}
29
30	c1, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
31	if err != nil {
32		b.Fatal(err)
33	}
34	defer c1.Close()
35	c2, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
36	if err != nil {
37		b.Fatal(err)
38	}
39	defer c2.Close()
40
41	var buf [1]byte
42	for i := 0; i < b.N; i++ {
43		if _, err := c1.WriteTo(buf[:], c2.LocalAddr()); err != nil {
44			b.Fatal(err)
45		}
46		if _, _, err := c2.ReadFrom(buf[:]); err != nil {
47			b.Fatal(err)
48		}
49	}
50}
51
52type resolveUDPAddrTest struct {
53	network       string
54	litAddrOrName string
55	addr          *UDPAddr
56	err           error
57}
58
59var resolveUDPAddrTests = []resolveUDPAddrTest{
60	{"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
61	{"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
62
63	{"udp", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil},
64	{"udp6", "[::1]:65535", &UDPAddr{IP: ParseIP("::1"), Port: 65535}, nil},
65
66	{"udp", "[::1%en0]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
67	{"udp6", "[::1%911]:2", &UDPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
68
69	{"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
70	{"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil},         // Go 1.0 behavior
71
72	{"udp", ":12345", &UDPAddr{Port: 12345}, nil},
73
74	{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
75
76	{"udp", "127.0.0.1:domain", &UDPAddr{IP: ParseIP("127.0.0.1"), Port: 53}, nil},
77	{"udp", "[::ffff:127.0.0.1]:domain", &UDPAddr{IP: ParseIP("::ffff:127.0.0.1"), Port: 53}, nil},
78	{"udp", "[2001:db8::1]:domain", &UDPAddr{IP: ParseIP("2001:db8::1"), Port: 53}, nil},
79	{"udp4", "127.0.0.1:domain", &UDPAddr{IP: ParseIP("127.0.0.1"), Port: 53}, nil},
80	{"udp4", "[::ffff:127.0.0.1]:domain", &UDPAddr{IP: ParseIP("127.0.0.1"), Port: 53}, nil},
81	{"udp6", "[2001:db8::1]:domain", &UDPAddr{IP: ParseIP("2001:db8::1"), Port: 53}, nil},
82
83	{"udp4", "[2001:db8::1]:domain", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "2001:db8::1"}},
84	{"udp6", "127.0.0.1:domain", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "127.0.0.1"}},
85	{"udp6", "[::ffff:127.0.0.1]:domain", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "::ffff:127.0.0.1"}},
86}
87
88func TestResolveUDPAddr(t *testing.T) {
89	origTestHookLookupIP := testHookLookupIP
90	defer func() { testHookLookupIP = origTestHookLookupIP }()
91	testHookLookupIP = lookupLocalhost
92
93	for _, tt := range resolveUDPAddrTests {
94		addr, err := ResolveUDPAddr(tt.network, tt.litAddrOrName)
95		if !reflect.DeepEqual(addr, tt.addr) || !reflect.DeepEqual(err, tt.err) {
96			t.Errorf("ResolveUDPAddr(%q, %q) = %#v, %v, want %#v, %v", tt.network, tt.litAddrOrName, addr, err, tt.addr, tt.err)
97			continue
98		}
99		if err == nil {
100			addr2, err := ResolveUDPAddr(addr.Network(), addr.String())
101			if !reflect.DeepEqual(addr2, tt.addr) || err != tt.err {
102				t.Errorf("(%q, %q): ResolveUDPAddr(%q, %q) = %#v, %v, want %#v, %v", tt.network, tt.litAddrOrName, addr.Network(), addr.String(), addr2, err, tt.addr, tt.err)
103			}
104		}
105	}
106}
107
108func TestWriteToUDP(t *testing.T) {
109	switch runtime.GOOS {
110	case "plan9":
111		t.Skipf("not supported on %s", runtime.GOOS)
112	}
113
114	c, err := ListenPacket("udp", "127.0.0.1:0")
115	if err != nil {
116		t.Fatal(err)
117	}
118	defer c.Close()
119
120	testWriteToConn(t, c.LocalAddr().String())
121	testWriteToPacketConn(t, c.LocalAddr().String())
122}
123
124func testWriteToConn(t *testing.T, raddr string) {
125	c, err := Dial("udp", raddr)
126	if err != nil {
127		t.Fatal(err)
128	}
129	defer c.Close()
130
131	ra, err := ResolveUDPAddr("udp", raddr)
132	if err != nil {
133		t.Fatal(err)
134	}
135
136	b := []byte("CONNECTED-MODE SOCKET")
137	_, err = c.(*UDPConn).WriteToUDP(b, ra)
138	if err == nil {
139		t.Fatal("should fail")
140	}
141	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
142		t.Fatalf("should fail as ErrWriteToConnected: %v", err)
143	}
144	_, err = c.(*UDPConn).WriteTo(b, ra)
145	if err == nil {
146		t.Fatal("should fail")
147	}
148	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
149		t.Fatalf("should fail as ErrWriteToConnected: %v", err)
150	}
151	_, err = c.Write(b)
152	if err != nil {
153		t.Fatal(err)
154	}
155	_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, ra)
156	if err == nil {
157		t.Fatal("should fail")
158	}
159	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
160		t.Fatalf("should fail as ErrWriteToConnected: %v", err)
161	}
162	_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, nil)
163	switch runtime.GOOS {
164	case "nacl", "windows": // see golang.org/issue/9252
165		t.Skipf("not implemented yet on %s", runtime.GOOS)
166	default:
167		if err != nil {
168			t.Fatal(err)
169		}
170	}
171}
172
173func testWriteToPacketConn(t *testing.T, raddr string) {
174	c, err := ListenPacket("udp", "127.0.0.1:0")
175	if err != nil {
176		t.Fatal(err)
177	}
178	defer c.Close()
179
180	ra, err := ResolveUDPAddr("udp", raddr)
181	if err != nil {
182		t.Fatal(err)
183	}
184
185	b := []byte("UNCONNECTED-MODE SOCKET")
186	_, err = c.(*UDPConn).WriteToUDP(b, ra)
187	if err != nil {
188		t.Fatal(err)
189	}
190	_, err = c.WriteTo(b, ra)
191	if err != nil {
192		t.Fatal(err)
193	}
194	_, err = c.(*UDPConn).Write(b)
195	if err == nil {
196		t.Fatal("should fail")
197	}
198	_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, nil)
199	if err == nil {
200		t.Fatal("should fail")
201	}
202	if err != nil && err.(*OpError).Err != errMissingAddress {
203		t.Fatalf("should fail as errMissingAddress: %v", err)
204	}
205	_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, ra)
206	switch runtime.GOOS {
207	case "nacl", "windows": // see golang.org/issue/9252
208		t.Skipf("not implemented yet on %s", runtime.GOOS)
209	default:
210		if err != nil {
211			t.Fatal(err)
212		}
213	}
214}
215
216var udpConnLocalNameTests = []struct {
217	net   string
218	laddr *UDPAddr
219}{
220	{"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
221	{"udp4", &UDPAddr{}},
222	{"udp4", nil},
223}
224
225func TestUDPConnLocalName(t *testing.T) {
226	testenv.MustHaveExternalNetwork(t)
227
228	for _, tt := range udpConnLocalNameTests {
229		c, err := ListenUDP(tt.net, tt.laddr)
230		if err != nil {
231			t.Fatal(err)
232		}
233		defer c.Close()
234		la := c.LocalAddr()
235		if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
236			t.Fatalf("got %v; expected a proper address with non-zero port number", la)
237		}
238	}
239}
240
241func TestUDPConnLocalAndRemoteNames(t *testing.T) {
242	for _, laddr := range []string{"", "127.0.0.1:0"} {
243		c1, err := ListenPacket("udp", "127.0.0.1:0")
244		if err != nil {
245			t.Fatal(err)
246		}
247		defer c1.Close()
248
249		var la *UDPAddr
250		if laddr != "" {
251			var err error
252			if la, err = ResolveUDPAddr("udp", laddr); err != nil {
253				t.Fatal(err)
254			}
255		}
256		c2, err := DialUDP("udp", la, c1.LocalAddr().(*UDPAddr))
257		if err != nil {
258			t.Fatal(err)
259		}
260		defer c2.Close()
261
262		var connAddrs = [4]struct {
263			got Addr
264			ok  bool
265		}{
266			{c1.LocalAddr(), true},
267			{c1.(*UDPConn).RemoteAddr(), false},
268			{c2.LocalAddr(), true},
269			{c2.RemoteAddr(), true},
270		}
271		for _, ca := range connAddrs {
272			if a, ok := ca.got.(*UDPAddr); ok != ca.ok || ok && a.Port == 0 {
273				t.Fatalf("got %v; expected a proper address with non-zero port number", ca.got)
274			}
275		}
276	}
277}
278
279func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
280	testenv.MustHaveExternalNetwork(t)
281
282	if !supportsIPv6() {
283		t.Skip("IPv6 is not supported")
284	}
285
286	for i, tt := range ipv6LinkLocalUnicastUDPTests {
287		c1, err := ListenPacket(tt.network, tt.address)
288		if err != nil {
289			// It might return "LookupHost returned no
290			// suitable address" error on some platforms.
291			t.Log(err)
292			continue
293		}
294		ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
295		if err != nil {
296			t.Fatal(err)
297		}
298		defer ls.teardown()
299		ch := make(chan error, 1)
300		handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
301		if err := ls.buildup(handler); err != nil {
302			t.Fatal(err)
303		}
304		if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
305			t.Fatalf("got %v; expected a proper address with zone identifier", la)
306		}
307
308		c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
309		if err != nil {
310			t.Fatal(err)
311		}
312		defer c2.Close()
313		if la, ok := c2.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
314			t.Fatalf("got %v; expected a proper address with zone identifier", la)
315		}
316		if ra, ok := c2.RemoteAddr().(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
317			t.Fatalf("got %v; expected a proper address with zone identifier", ra)
318		}
319
320		if _, err := c2.Write([]byte("UDP OVER IPV6 LINKLOCAL TEST")); err != nil {
321			t.Fatal(err)
322		}
323		b := make([]byte, 32)
324		if _, err := c2.Read(b); err != nil {
325			t.Fatal(err)
326		}
327
328		for err := range ch {
329			t.Errorf("#%d: %v", i, err)
330		}
331	}
332}
333
334func TestUDPZeroBytePayload(t *testing.T) {
335	switch runtime.GOOS {
336	case "nacl", "plan9":
337		t.Skipf("not supported on %s", runtime.GOOS)
338	}
339
340	c, err := newLocalPacketListener("udp")
341	if err != nil {
342		t.Fatal(err)
343	}
344	defer c.Close()
345
346	for _, genericRead := range []bool{false, true} {
347		n, err := c.WriteTo(nil, c.LocalAddr())
348		if err != nil {
349			t.Fatal(err)
350		}
351		if n != 0 {
352			t.Errorf("got %d; want 0", n)
353		}
354		c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
355		var b [1]byte
356		if genericRead {
357			_, err = c.(Conn).Read(b[:])
358		} else {
359			_, _, err = c.ReadFrom(b[:])
360		}
361		switch err {
362		case nil: // ReadFrom succeeds
363		default: // Read may timeout, it depends on the platform
364			if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
365				t.Fatal(err)
366			}
367		}
368	}
369}
370
371func TestUDPZeroByteBuffer(t *testing.T) {
372	switch runtime.GOOS {
373	case "nacl", "plan9":
374		t.Skipf("not supported on %s", runtime.GOOS)
375	}
376
377	c, err := newLocalPacketListener("udp")
378	if err != nil {
379		t.Fatal(err)
380	}
381	defer c.Close()
382
383	b := []byte("UDP ZERO BYTE BUFFER TEST")
384	for _, genericRead := range []bool{false, true} {
385		n, err := c.WriteTo(b, c.LocalAddr())
386		if err != nil {
387			t.Fatal(err)
388		}
389		if n != len(b) {
390			t.Errorf("got %d; want %d", n, len(b))
391		}
392		c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
393		if genericRead {
394			_, err = c.(Conn).Read(nil)
395		} else {
396			_, _, err = c.ReadFrom(nil)
397		}
398		switch err {
399		case nil: // ReadFrom succeeds
400		default: // Read may timeout, it depends on the platform
401			if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZ
402				t.Fatal(err)
403			}
404		}
405	}
406}
407