1// Copyright 2015 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	"flag"
9	"fmt"
10	"net/internal/socktest"
11	"os"
12	"runtime"
13	"slices"
14	"strings"
15	"sync"
16	"testing"
17	"time"
18)
19
20var (
21	sw socktest.Switch
22
23	// uninstallTestHooks runs just before a run of benchmarks.
24	testHookUninstaller sync.Once
25)
26
27var (
28	testTCPBig = flag.Bool("tcpbig", false, "whether to test massive size of data per read or write call on TCP connection")
29
30	testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding")
31
32	// If external IPv4 connectivity exists, we can try dialing
33	// non-node/interface local scope IPv4 addresses.
34	// On Windows, Lookup APIs may not return IPv4-related
35	// resource records when a node has no external IPv4
36	// connectivity.
37	testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists")
38
39	// If external IPv6 connectivity exists, we can try dialing
40	// non-node/interface local scope IPv6 addresses.
41	// On Windows, Lookup APIs may not return IPv6-related
42	// resource records when a node has no external IPv6
43	// connectivity.
44	testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists")
45)
46
47func TestMain(m *testing.M) {
48	setupTestData()
49	installTestHooks()
50
51	st := m.Run()
52
53	testHookUninstaller.Do(uninstallTestHooks)
54	if testing.Verbose() {
55		printRunningGoroutines()
56		printInflightSockets()
57		printSocketStats()
58	}
59	forceCloseSockets()
60	os.Exit(st)
61}
62
63// mustSetDeadline calls the bound method m to set a deadline on a Conn.
64// If the call fails, mustSetDeadline skips t if the current GOOS is believed
65// not to support deadlines, or fails the test otherwise.
66func mustSetDeadline(t testing.TB, m func(time.Time) error, d time.Duration) {
67	err := m(time.Now().Add(d))
68	if err != nil {
69		t.Helper()
70		if runtime.GOOS == "plan9" {
71			t.Skipf("skipping: %s does not support deadlines", runtime.GOOS)
72		}
73		t.Fatal(err)
74	}
75}
76
77type ipv6LinkLocalUnicastTest struct {
78	network, address string
79	nameLookup       bool
80}
81
82var (
83	ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
84	ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
85)
86
87func setupTestData() {
88	if supportsIPv4() {
89		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
90			{"tcp", "localhost:1", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil},
91			{"tcp4", "localhost:2", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil},
92		}...)
93		resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
94			{"udp", "localhost:1", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil},
95			{"udp4", "localhost:2", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil},
96		}...)
97		resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
98			{"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
99			{"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
100		}...)
101	}
102
103	if supportsIPv6() {
104		resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil})
105		resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil})
106		resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
107
108		// Issue 20911: don't return IPv4 addresses for
109		// Resolve*Addr calls of the IPv6 unspecified address.
110		resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp", "[::]:4", &TCPAddr{IP: IPv6unspecified, Port: 4}, nil})
111		resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp", "[::]:4", &UDPAddr{IP: IPv6unspecified, Port: 4}, nil})
112		resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip", "::", &IPAddr{IP: IPv6unspecified}, nil})
113	}
114
115	ifi := loopbackInterface()
116	if ifi != nil {
117		index := fmt.Sprintf("%v", ifi.Index)
118		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
119			{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneCache.name(ifi.Index)}, nil},
120			{"tcp6", "[fe80::1%" + index + "]:2", &TCPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil},
121		}...)
122		resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
123			{"udp6", "[fe80::1%" + ifi.Name + "]:1", &UDPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneCache.name(ifi.Index)}, nil},
124			{"udp6", "[fe80::1%" + index + "]:2", &UDPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil},
125		}...)
126		resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
127			{"ip6", "fe80::1%" + ifi.Name, &IPAddr{IP: ParseIP("fe80::1"), Zone: zoneCache.name(ifi.Index)}, nil},
128			{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
129		}...)
130	}
131
132	addr := ipv6LinkLocalUnicastAddr(ifi)
133	if addr != "" {
134		if runtime.GOOS != "dragonfly" {
135			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
136				{"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
137			}...)
138			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
139				{"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
140			}...)
141		}
142		ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
143			{"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
144		}...)
145		ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
146			{"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
147		}...)
148		switch runtime.GOOS {
149		case "darwin", "ios", "dragonfly", "freebsd", "openbsd", "netbsd":
150			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
151				{"tcp", "[localhost%" + ifi.Name + "]:0", true},
152				{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
153			}...)
154			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
155				{"udp", "[localhost%" + ifi.Name + "]:0", true},
156				{"udp6", "[localhost%" + ifi.Name + "]:0", true},
157			}...)
158		case "linux":
159			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
160				{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
161				{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
162			}...)
163			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
164				{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
165				{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
166			}...)
167		}
168	}
169}
170
171func printRunningGoroutines() {
172	gss := runningGoroutines()
173	if len(gss) == 0 {
174		return
175	}
176	fmt.Fprintf(os.Stderr, "Running goroutines:\n")
177	for _, gs := range gss {
178		fmt.Fprintf(os.Stderr, "%v\n", gs)
179	}
180	fmt.Fprintf(os.Stderr, "\n")
181}
182
183// runningGoroutines returns a list of remaining goroutines.
184func runningGoroutines() []string {
185	var gss []string
186	b := make([]byte, 2<<20)
187	b = b[:runtime.Stack(b, true)]
188	for _, s := range strings.Split(string(b), "\n\n") {
189		_, stack, _ := strings.Cut(s, "\n")
190		stack = strings.TrimSpace(stack)
191		if !strings.Contains(stack, "created by net") {
192			continue
193		}
194		gss = append(gss, stack)
195	}
196	slices.Sort(gss)
197	return gss
198}
199
200func printInflightSockets() {
201	sos := sw.Sockets()
202	if len(sos) == 0 {
203		return
204	}
205	fmt.Fprintf(os.Stderr, "Inflight sockets:\n")
206	for s, so := range sos {
207		fmt.Fprintf(os.Stderr, "%v: %v\n", s, so)
208	}
209	fmt.Fprintf(os.Stderr, "\n")
210}
211
212func printSocketStats() {
213	sts := sw.Stats()
214	if len(sts) == 0 {
215		return
216	}
217	fmt.Fprintf(os.Stderr, "Socket statistical information:\n")
218	for _, st := range sts {
219		fmt.Fprintf(os.Stderr, "%v\n", st)
220	}
221	fmt.Fprintf(os.Stderr, "\n")
222}
223