| // Copyright 2015 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package net | 
 |  | 
 | import ( | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"net/internal/socktest" | 
 | 	"os" | 
 | 	"runtime" | 
 | 	"slices" | 
 | 	"strings" | 
 | 	"sync" | 
 | 	"testing" | 
 | 	"time" | 
 | ) | 
 |  | 
 | var ( | 
 | 	sw socktest.Switch | 
 |  | 
 | 	// uninstallTestHooks runs just before a run of benchmarks. | 
 | 	testHookUninstaller sync.Once | 
 | ) | 
 |  | 
 | var ( | 
 | 	testTCPBig = flag.Bool("tcpbig", false, "whether to test massive size of data per read or write call on TCP connection") | 
 |  | 
 | 	testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding") | 
 |  | 
 | 	// If external IPv4 connectivity exists, we can try dialing | 
 | 	// non-node/interface local scope IPv4 addresses. | 
 | 	// On Windows, Lookup APIs may not return IPv4-related | 
 | 	// resource records when a node has no external IPv4 | 
 | 	// connectivity. | 
 | 	testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists") | 
 |  | 
 | 	// If external IPv6 connectivity exists, we can try dialing | 
 | 	// non-node/interface local scope IPv6 addresses. | 
 | 	// On Windows, Lookup APIs may not return IPv6-related | 
 | 	// resource records when a node has no external IPv6 | 
 | 	// connectivity. | 
 | 	testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists") | 
 | ) | 
 |  | 
 | func TestMain(m *testing.M) { | 
 | 	setupTestData() | 
 | 	installTestHooks() | 
 |  | 
 | 	st := m.Run() | 
 |  | 
 | 	testHookUninstaller.Do(uninstallTestHooks) | 
 | 	if testing.Verbose() { | 
 | 		printRunningGoroutines() | 
 | 		printInflightSockets() | 
 | 		printSocketStats() | 
 | 	} | 
 | 	forceCloseSockets() | 
 | 	os.Exit(st) | 
 | } | 
 |  | 
 | // mustSetDeadline calls the bound method m to set a deadline on a Conn. | 
 | // If the call fails, mustSetDeadline skips t if the current GOOS is believed | 
 | // not to support deadlines, or fails the test otherwise. | 
 | func mustSetDeadline(t testing.TB, m func(time.Time) error, d time.Duration) { | 
 | 	err := m(time.Now().Add(d)) | 
 | 	if err != nil { | 
 | 		t.Helper() | 
 | 		if runtime.GOOS == "plan9" { | 
 | 			t.Skipf("skipping: %s does not support deadlines", runtime.GOOS) | 
 | 		} | 
 | 		t.Fatal(err) | 
 | 	} | 
 | } | 
 |  | 
 | type ipv6LinkLocalUnicastTest struct { | 
 | 	network, address string | 
 | 	nameLookup       bool | 
 | } | 
 |  | 
 | var ( | 
 | 	ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest | 
 | 	ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest | 
 | ) | 
 |  | 
 | func setupTestData() { | 
 | 	if supportsIPv4() { | 
 | 		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{ | 
 | 			{"tcp", "localhost:1", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil}, | 
 | 			{"tcp4", "localhost:2", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil}, | 
 | 		}...) | 
 | 		resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{ | 
 | 			{"udp", "localhost:1", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil}, | 
 | 			{"udp4", "localhost:2", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil}, | 
 | 		}...) | 
 | 		resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{ | 
 | 			{"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, | 
 | 			{"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, | 
 | 		}...) | 
 | 	} | 
 |  | 
 | 	if supportsIPv6() { | 
 | 		resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil}) | 
 | 		resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil}) | 
 | 		resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil}) | 
 |  | 
 | 		// Issue 20911: don't return IPv4 addresses for | 
 | 		// Resolve*Addr calls of the IPv6 unspecified address. | 
 | 		resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp", "[::]:4", &TCPAddr{IP: IPv6unspecified, Port: 4}, nil}) | 
 | 		resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp", "[::]:4", &UDPAddr{IP: IPv6unspecified, Port: 4}, nil}) | 
 | 		resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip", "::", &IPAddr{IP: IPv6unspecified}, nil}) | 
 | 	} | 
 |  | 
 | 	ifi := loopbackInterface() | 
 | 	if ifi != nil { | 
 | 		index := fmt.Sprintf("%v", ifi.Index) | 
 | 		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{ | 
 | 			{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneCache.name(ifi.Index)}, nil}, | 
 | 			{"tcp6", "[fe80::1%" + index + "]:2", &TCPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil}, | 
 | 		}...) | 
 | 		resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{ | 
 | 			{"udp6", "[fe80::1%" + ifi.Name + "]:1", &UDPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneCache.name(ifi.Index)}, nil}, | 
 | 			{"udp6", "[fe80::1%" + index + "]:2", &UDPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil}, | 
 | 		}...) | 
 | 		resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{ | 
 | 			{"ip6", "fe80::1%" + ifi.Name, &IPAddr{IP: ParseIP("fe80::1"), Zone: zoneCache.name(ifi.Index)}, nil}, | 
 | 			{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil}, | 
 | 		}...) | 
 | 	} | 
 |  | 
 | 	addr := ipv6LinkLocalUnicastAddr(ifi) | 
 | 	if addr != "" { | 
 | 		if runtime.GOOS != "dragonfly" { | 
 | 			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"tcp", "[" + addr + "%" + ifi.Name + "]:0", false}, | 
 | 			}...) | 
 | 			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"udp", "[" + addr + "%" + ifi.Name + "]:0", false}, | 
 | 			}...) | 
 | 		} | 
 | 		ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{ | 
 | 			{"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false}, | 
 | 		}...) | 
 | 		ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{ | 
 | 			{"udp6", "[" + addr + "%" + ifi.Name + "]:0", false}, | 
 | 		}...) | 
 | 		switch runtime.GOOS { | 
 | 		case "darwin", "ios", "dragonfly", "freebsd", "openbsd", "netbsd": | 
 | 			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"tcp", "[localhost%" + ifi.Name + "]:0", true}, | 
 | 				{"tcp6", "[localhost%" + ifi.Name + "]:0", true}, | 
 | 			}...) | 
 | 			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"udp", "[localhost%" + ifi.Name + "]:0", true}, | 
 | 				{"udp6", "[localhost%" + ifi.Name + "]:0", true}, | 
 | 			}...) | 
 | 		case "linux": | 
 | 			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true}, | 
 | 				{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true}, | 
 | 			}...) | 
 | 			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{ | 
 | 				{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true}, | 
 | 				{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true}, | 
 | 			}...) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func printRunningGoroutines() { | 
 | 	gss := runningGoroutines() | 
 | 	if len(gss) == 0 { | 
 | 		return | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "Running goroutines:\n") | 
 | 	for _, gs := range gss { | 
 | 		fmt.Fprintf(os.Stderr, "%v\n", gs) | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "\n") | 
 | } | 
 |  | 
 | // runningGoroutines returns a list of remaining goroutines. | 
 | func runningGoroutines() []string { | 
 | 	var gss []string | 
 | 	b := make([]byte, 2<<20) | 
 | 	b = b[:runtime.Stack(b, true)] | 
 | 	for _, s := range strings.Split(string(b), "\n\n") { | 
 | 		_, stack, _ := strings.Cut(s, "\n") | 
 | 		stack = strings.TrimSpace(stack) | 
 | 		if !strings.Contains(stack, "created by net") { | 
 | 			continue | 
 | 		} | 
 | 		gss = append(gss, stack) | 
 | 	} | 
 | 	slices.Sort(gss) | 
 | 	return gss | 
 | } | 
 |  | 
 | func printInflightSockets() { | 
 | 	sos := sw.Sockets() | 
 | 	if len(sos) == 0 { | 
 | 		return | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "Inflight sockets:\n") | 
 | 	for s, so := range sos { | 
 | 		fmt.Fprintf(os.Stderr, "%v: %v\n", s, so) | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "\n") | 
 | } | 
 |  | 
 | func printSocketStats() { | 
 | 	sts := sw.Stats() | 
 | 	if len(sts) == 0 { | 
 | 		return | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "Socket statistical information:\n") | 
 | 	for _, st := range sts { | 
 | 		fmt.Fprintf(os.Stderr, "%v\n", st) | 
 | 	} | 
 | 	fmt.Fprintf(os.Stderr, "\n") | 
 | } |