// Copyright 2011 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 (
	"bufio"
	"context"
	"errors"
	"fmt"
	"internal/testenv"
	"io"
	"os"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"testing"
	"time"
)

var prohibitionaryDialArgTests = []struct {
	network string
	address string
}{
	{"tcp6", "127.0.0.1"},
	{"tcp6", "::ffff:127.0.0.1"},
}

func TestProhibitionaryDialArg(t *testing.T) {
	testenv.MustHaveExternalNetwork(t)

	switch runtime.GOOS {
	case "plan9":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if !supportsIPv4map() {
		t.Skip("mapping ipv4 address inside ipv6 address not supported")
	}

	ln, err := Listen("tcp", "[::]:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	_, port, err := SplitHostPort(ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}

	for i, tt := range prohibitionaryDialArgTests {
		c, err := Dial(tt.network, JoinHostPort(tt.address, port))
		if err == nil {
			c.Close()
			t.Errorf("#%d: %v", i, err)
		}
	}
}

func TestDialLocal(t *testing.T) {
	ln := newLocalListener(t, "tcp")
	defer ln.Close()
	_, port, err := SplitHostPort(ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	c, err := Dial("tcp", JoinHostPort("", port))
	if err != nil {
		t.Fatal(err)
	}
	c.Close()
}

func TestDialerDualStackFDLeak(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
	case "windows":
		t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
	case "openbsd":
		testenv.SkipFlaky(t, 15157)
	}
	if !supportsIPv4() || !supportsIPv6() {
		t.Skip("both IPv4 and IPv6 are required")
	}

	before := sw.Sockets()
	origTestHookLookupIP := testHookLookupIP
	defer func() { testHookLookupIP = origTestHookLookupIP }()
	testHookLookupIP = lookupLocalhost
	handler := func(dss *dualStackServer, ln Listener) {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Close()
		}
	}
	dss, err := newDualStackServer()
	if err != nil {
		t.Fatal(err)
	}
	if err := dss.buildup(handler); err != nil {
		dss.teardown()
		t.Fatal(err)
	}

	const N = 10
	var wg sync.WaitGroup
	wg.Add(N)
	d := &Dialer{DualStack: true, Timeout: 5 * time.Second}
	for i := 0; i < N; i++ {
		go func() {
			defer wg.Done()
			c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
			if err != nil {
				t.Error(err)
				return
			}
			c.Close()
		}()
	}
	wg.Wait()
	dss.teardown()
	after := sw.Sockets()
	if len(after) != len(before) {
		t.Errorf("got %d; want %d", len(after), len(before))
	}
}

// Define a pair of blackholed (IPv4, IPv6) addresses, for which dialTCP is
// expected to hang until the timeout elapses. These addresses are reserved
// for benchmarking by RFC 6890.
const (
	slowDst4 = "198.18.0.254"
	slowDst6 = "2001:2::254"
)

// In some environments, the slow IPs may be explicitly unreachable, and fail
// more quickly than expected. This test hook prevents dialTCP from returning
// before the deadline.
func slowDialTCP(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	sd := &sysDialer{network: network, address: raddr.String()}
	c, err := sd.doDialTCP(ctx, laddr, raddr)
	if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
		// Wait for the deadline, or indefinitely if none exists.
		<-ctx.Done()
	}
	return c, err
}

func dialClosedPort(t *testing.T) (dialLatency time.Duration) {
	// On most platforms, dialing a closed port should be nearly instantaneous —
	// less than a few hundred milliseconds. However, on some platforms it may be
	// much slower: on Windows and OpenBSD, it has been observed to take up to a
	// few seconds.

	l, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("dialClosedPort: Listen failed: %v", err)
	}
	addr := l.Addr().String()
	l.Close()

	startTime := time.Now()
	c, err := Dial("tcp", addr)
	if err == nil {
		c.Close()
	}
	elapsed := time.Since(startTime)
	t.Logf("dialClosedPort: measured delay %v", elapsed)
	return elapsed
}

func TestDialParallel(t *testing.T) {
	const instant time.Duration = 0
	const fallbackDelay = 200 * time.Millisecond

	nCopies := func(s string, n int) []string {
		out := make([]string, n)
		for i := 0; i < n; i++ {
			out[i] = s
		}
		return out
	}

	var testCases = []struct {
		primaries       []string
		fallbacks       []string
		teardownNetwork string
		expectOk        bool
		expectElapsed   time.Duration
	}{
		// These should just work on the first try.
		{[]string{"127.0.0.1"}, []string{}, "", true, instant},
		{[]string{"::1"}, []string{}, "", true, instant},
		{[]string{"127.0.0.1", "::1"}, []string{slowDst6}, "tcp6", true, instant},
		{[]string{"::1", "127.0.0.1"}, []string{slowDst4}, "tcp4", true, instant},
		// Primary is slow; fallback should kick in.
		{[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay},
		// Skip a "connection refused" in the primary thread.
		{[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, instant},
		{[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, instant},
		// Skip a "connection refused" in the fallback thread.
		{[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay},
		// Primary refused, fallback without delay.
		{[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, instant},
		{[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, instant},
		// Everything is refused.
		{[]string{"127.0.0.1"}, []string{}, "tcp4", false, instant},
		// Nothing to do; fail instantly.
		{[]string{}, []string{}, "", false, instant},
		// Connecting to tons of addresses should not trip the deadline.
		{nCopies("::1", 1000), []string{}, "", true, instant},
	}

	// Convert a list of IP strings into TCPAddrs.
	makeAddrs := func(ips []string, port string) addrList {
		var out addrList
		for _, ip := range ips {
			addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, port))
			if err != nil {
				t.Fatal(err)
			}
			out = append(out, addr)
		}
		return out
	}

	for i, tt := range testCases {
		i, tt := i, tt
		t.Run(fmt.Sprint(i), func(t *testing.T) {
			dialTCP := func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) {
				n := "tcp6"
				if raddr.IP.To4() != nil {
					n = "tcp4"
				}
				if n == tt.teardownNetwork {
					return nil, errors.New("unreachable")
				}
				if r := raddr.IP.String(); r == slowDst4 || r == slowDst6 {
					<-ctx.Done()
					return nil, ctx.Err()
				}
				return &TCPConn{}, nil
			}

			primaries := makeAddrs(tt.primaries, "80")
			fallbacks := makeAddrs(tt.fallbacks, "80")
			d := Dialer{
				FallbackDelay: fallbackDelay,
			}
			const forever = 60 * time.Minute
			if tt.expectElapsed == instant {
				d.FallbackDelay = forever
			}
			startTime := time.Now()
			sd := &sysDialer{
				Dialer:          d,
				network:         "tcp",
				address:         "?",
				testHookDialTCP: dialTCP,
			}
			c, err := sd.dialParallel(context.Background(), primaries, fallbacks)
			elapsed := time.Since(startTime)

			if c != nil {
				c.Close()
			}

			if tt.expectOk && err != nil {
				t.Errorf("#%d: got %v; want nil", i, err)
			} else if !tt.expectOk && err == nil {
				t.Errorf("#%d: got nil; want non-nil", i)
			}

			if elapsed < tt.expectElapsed || elapsed >= forever {
				t.Errorf("#%d: got %v; want >= %v, < forever", i, elapsed, tt.expectElapsed)
			}

			// Repeat each case, ensuring that it can be canceled.
			ctx, cancel := context.WithCancel(context.Background())
			var wg sync.WaitGroup
			wg.Add(1)
			go func() {
				time.Sleep(5 * time.Millisecond)
				cancel()
				wg.Done()
			}()
			// Ignore errors, since all we care about is that the
			// call can be canceled.
			c, _ = sd.dialParallel(ctx, primaries, fallbacks)
			if c != nil {
				c.Close()
			}
			wg.Wait()
		})
	}
}

func lookupSlowFast(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
	switch host {
	case "slow6loopback4":
		// Returns a slow IPv6 address, and a local IPv4 address.
		return []IPAddr{
			{IP: ParseIP(slowDst6)},
			{IP: ParseIP("127.0.0.1")},
		}, nil
	default:
		return fn(ctx, network, host)
	}
}

func TestDialerFallbackDelay(t *testing.T) {
	testenv.MustHaveExternalNetwork(t)

	if !supportsIPv4() || !supportsIPv6() {
		t.Skip("both IPv4 and IPv6 are required")
	}

	origTestHookLookupIP := testHookLookupIP
	defer func() { testHookLookupIP = origTestHookLookupIP }()
	testHookLookupIP = lookupSlowFast

	origTestHookDialTCP := testHookDialTCP
	defer func() { testHookDialTCP = origTestHookDialTCP }()
	testHookDialTCP = slowDialTCP

	var testCases = []struct {
		dualstack     bool
		delay         time.Duration
		expectElapsed time.Duration
	}{
		// Use a very brief delay, which should fallback immediately.
		{true, 1 * time.Nanosecond, 0},
		// Use a 200ms explicit timeout.
		{true, 200 * time.Millisecond, 200 * time.Millisecond},
		// The default is 300ms.
		{true, 0, 300 * time.Millisecond},
	}

	handler := func(dss *dualStackServer, ln Listener) {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Close()
		}
	}
	dss, err := newDualStackServer()
	if err != nil {
		t.Fatal(err)
	}
	defer dss.teardown()
	if err := dss.buildup(handler); err != nil {
		t.Fatal(err)
	}

	for i, tt := range testCases {
		d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay}

		startTime := time.Now()
		c, err := d.Dial("tcp", JoinHostPort("slow6loopback4", dss.port))
		elapsed := time.Since(startTime)
		if err == nil {
			c.Close()
		} else if tt.dualstack {
			t.Error(err)
		}
		expectMin := tt.expectElapsed - 1*time.Millisecond
		expectMax := tt.expectElapsed + 95*time.Millisecond
		if elapsed < expectMin {
			t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectMin)
		}
		if elapsed > expectMax {
			t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectMax)
		}
	}
}

func TestDialParallelSpuriousConnection(t *testing.T) {
	if !supportsIPv4() || !supportsIPv6() {
		t.Skip("both IPv4 and IPv6 are required")
	}

	var readDeadline time.Time
	if td, ok := t.Deadline(); ok {
		const arbitraryCleanupMargin = 1 * time.Second
		readDeadline = td.Add(-arbitraryCleanupMargin)
	} else {
		readDeadline = time.Now().Add(5 * time.Second)
	}

	var closed sync.WaitGroup
	closed.Add(2)
	handler := func(dss *dualStackServer, ln Listener) {
		// Accept one connection per address.
		c, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}

		// Workaround for https://go.dev/issue/37795.
		// On arm64 macOS (current as of macOS 12.4),
		// reading from a socket at the same time as the client
		// is closing it occasionally hangs for 60 seconds before
		// returning ECONNRESET. Sleep for a bit to give the
		// socket time to close before trying to read from it.
		if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
			time.Sleep(10 * time.Millisecond)
		}

		// The client should close itself, without sending data.
		c.SetReadDeadline(readDeadline)
		var b [1]byte
		if _, err := c.Read(b[:]); err != io.EOF {
			t.Errorf("got %v; want %v", err, io.EOF)
		}
		c.Close()
		closed.Done()
	}
	dss, err := newDualStackServer()
	if err != nil {
		t.Fatal(err)
	}
	defer dss.teardown()
	if err := dss.buildup(handler); err != nil {
		t.Fatal(err)
	}

	const fallbackDelay = 100 * time.Millisecond

	var dialing sync.WaitGroup
	dialing.Add(2)
	origTestHookDialTCP := testHookDialTCP
	defer func() { testHookDialTCP = origTestHookDialTCP }()
	testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
		// Wait until Happy Eyeballs kicks in and both connections are dialing,
		// and inhibit cancellation.
		// This forces dialParallel to juggle two successful connections.
		dialing.Done()
		dialing.Wait()

		// Now ignore the provided context (which will be canceled) and use a
		// different one to make sure this completes with a valid connection,
		// which we hope to be closed below:
		sd := &sysDialer{network: net, address: raddr.String()}
		return sd.doDialTCP(context.Background(), laddr, raddr)
	}

	d := Dialer{
		FallbackDelay: fallbackDelay,
	}
	sd := &sysDialer{
		Dialer:  d,
		network: "tcp",
		address: "?",
	}

	makeAddr := func(ip string) addrList {
		addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, dss.port))
		if err != nil {
			t.Fatal(err)
		}
		return addrList{addr}
	}

	// dialParallel returns one connection (and closes the other.)
	c, err := sd.dialParallel(context.Background(), makeAddr("127.0.0.1"), makeAddr("::1"))
	if err != nil {
		t.Fatal(err)
	}
	c.Close()

	// The server should've seen both connections.
	closed.Wait()
}

func TestDialerPartialDeadline(t *testing.T) {
	now := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
	var testCases = []struct {
		now            time.Time
		deadline       time.Time
		addrs          int
		expectDeadline time.Time
		expectErr      error
	}{
		// Regular division.
		{now, now.Add(12 * time.Second), 1, now.Add(12 * time.Second), nil},
		{now, now.Add(12 * time.Second), 2, now.Add(6 * time.Second), nil},
		{now, now.Add(12 * time.Second), 3, now.Add(4 * time.Second), nil},
		// Bump against the 2-second sane minimum.
		{now, now.Add(12 * time.Second), 999, now.Add(2 * time.Second), nil},
		// Total available is now below the sane minimum.
		{now, now.Add(1900 * time.Millisecond), 999, now.Add(1900 * time.Millisecond), nil},
		// Null deadline.
		{now, noDeadline, 1, noDeadline, nil},
		// Step the clock forward and cross the deadline.
		{now.Add(-1 * time.Millisecond), now, 1, now, nil},
		{now.Add(0 * time.Millisecond), now, 1, noDeadline, errTimeout},
		{now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout},
	}
	for i, tt := range testCases {
		deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
		if err != tt.expectErr {
			t.Errorf("#%d: got %v; want %v", i, err, tt.expectErr)
		}
		if !deadline.Equal(tt.expectDeadline) {
			t.Errorf("#%d: got %v; want %v", i, deadline, tt.expectDeadline)
		}
	}
}

// isEADDRINUSE reports whether err is syscall.EADDRINUSE.
var isEADDRINUSE = func(err error) bool { return false }

func TestDialerLocalAddr(t *testing.T) {
	if !supportsIPv4() || !supportsIPv6() {
		t.Skip("both IPv4 and IPv6 are required")
	}

	type test struct {
		network, raddr string
		laddr          Addr
		error
	}
	var tests = []test{
		{"tcp4", "127.0.0.1", nil, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{}, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"}},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil},
		{"tcp4", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress},
		{"tcp4", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}},
		{"tcp4", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}},

		{"tcp6", "::1", nil, nil},
		{"tcp6", "::1", &TCPAddr{}, nil},
		{"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
		{"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
		{"tcp6", "::1", &TCPAddr{IP: ParseIP("::")}, nil},
		{"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress},
		{"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress},
		{"tcp6", "::1", &TCPAddr{IP: IPv6loopback}, nil},
		{"tcp6", "::1", &UDPAddr{}, &AddrError{Err: "some error"}},
		{"tcp6", "::1", &UnixAddr{}, &AddrError{Err: "some error"}},

		{"tcp", "127.0.0.1", nil, nil},
		{"tcp", "127.0.0.1", &TCPAddr{}, nil},
		{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
		{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
		{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil},
		{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil},
		{"tcp", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress},
		{"tcp", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}},
		{"tcp", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}},

		{"tcp", "::1", nil, nil},
		{"tcp", "::1", &TCPAddr{}, nil},
		{"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
		{"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
		{"tcp", "::1", &TCPAddr{IP: ParseIP("::")}, nil},
		{"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress},
		{"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress},
		{"tcp", "::1", &TCPAddr{IP: IPv6loopback}, nil},
		{"tcp", "::1", &UDPAddr{}, &AddrError{Err: "some error"}},
		{"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}},
	}

	issue34264Index := -1
	if supportsIPv4map() {
		issue34264Index = len(tests)
		tests = append(tests, test{
			"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil,
		})
	} else {
		tests = append(tests, test{
			"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"},
		})
	}

	origTestHookLookupIP := testHookLookupIP
	defer func() { testHookLookupIP = origTestHookLookupIP }()
	testHookLookupIP = lookupLocalhost
	handler := func(ls *localServer, ln Listener) {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Close()
		}
	}
	var lss [2]*localServer
	for i, network := range []string{"tcp4", "tcp6"} {
		lss[i] = newLocalServer(t, network)
		defer lss[i].teardown()
		if err := lss[i].buildup(handler); err != nil {
			t.Fatal(err)
		}
	}

	for i, tt := range tests {
		d := &Dialer{LocalAddr: tt.laddr}
		var addr string
		ip := ParseIP(tt.raddr)
		if ip.To4() != nil {
			addr = lss[0].Listener.Addr().String()
		}
		if ip.To16() != nil && ip.To4() == nil {
			addr = lss[1].Listener.Addr().String()
		}
		c, err := d.Dial(tt.network, addr)
		if err == nil && tt.error != nil || err != nil && tt.error == nil {
			if i == issue34264Index && runtime.GOOS == "freebsd" && isEADDRINUSE(err) {
				// https://golang.org/issue/34264: FreeBSD through at least version 12.2
				// has been observed to fail with EADDRINUSE when dialing from an IPv6
				// local address to an IPv4 remote address.
				t.Logf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
				t.Logf("(spurious EADDRINUSE ignored on freebsd: see https://golang.org/issue/34264)")
			} else {
				t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
			}
		}
		if err != nil {
			if perr := parseDialError(err); perr != nil {
				t.Error(perr)
			}
			continue
		}
		c.Close()
	}
}

func TestDialerDualStack(t *testing.T) {
	testenv.SkipFlaky(t, 13324)

	if !supportsIPv4() || !supportsIPv6() {
		t.Skip("both IPv4 and IPv6 are required")
	}

	closedPortDelay := dialClosedPort(t)

	origTestHookLookupIP := testHookLookupIP
	defer func() { testHookLookupIP = origTestHookLookupIP }()
	testHookLookupIP = lookupLocalhost
	handler := func(dss *dualStackServer, ln Listener) {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Close()
		}
	}

	var timeout = 150*time.Millisecond + closedPortDelay
	for _, dualstack := range []bool{false, true} {
		dss, err := newDualStackServer()
		if err != nil {
			t.Fatal(err)
		}
		defer dss.teardown()
		if err := dss.buildup(handler); err != nil {
			t.Fatal(err)
		}

		d := &Dialer{DualStack: dualstack, Timeout: timeout}
		for range dss.lns {
			c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
			if err != nil {
				t.Error(err)
				continue
			}
			switch addr := c.LocalAddr().(*TCPAddr); {
			case addr.IP.To4() != nil:
				dss.teardownNetwork("tcp4")
			case addr.IP.To16() != nil && addr.IP.To4() == nil:
				dss.teardownNetwork("tcp6")
			}
			c.Close()
		}
	}
}

func TestDialerKeepAlive(t *testing.T) {
	handler := func(ls *localServer, ln Listener) {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Close()
		}
	}
	ls := newLocalServer(t, "tcp")
	defer ls.teardown()
	if err := ls.buildup(handler); err != nil {
		t.Fatal(err)
	}
	defer func() { testHookSetKeepAlive = func(time.Duration) {} }()

	tests := []struct {
		ka       time.Duration
		expected time.Duration
	}{
		{-1, -1},
		{0, 15 * time.Second},
		{5 * time.Second, 5 * time.Second},
		{30 * time.Second, 30 * time.Second},
	}

	for _, test := range tests {
		var got time.Duration = -1
		testHookSetKeepAlive = func(d time.Duration) { got = d }
		d := Dialer{KeepAlive: test.ka}
		c, err := d.Dial("tcp", ls.Listener.Addr().String())
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
		if got != test.expected {
			t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive set to %v, want %v", d.KeepAlive, got, test.expected)
		}
	}
}

func TestDialCancel(t *testing.T) {
	mustHaveExternalNetwork(t)

	blackholeIPPort := JoinHostPort(slowDst4, "1234")
	if !supportsIPv4() {
		blackholeIPPort = JoinHostPort(slowDst6, "1234")
	}

	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	const cancelTick = 5 // the timer tick we cancel the dial at
	const timeoutTick = 100

	var d Dialer
	cancel := make(chan struct{})
	d.Cancel = cancel
	errc := make(chan error, 1)
	connc := make(chan Conn, 1)
	go func() {
		if c, err := d.Dial("tcp", blackholeIPPort); err != nil {
			errc <- err
		} else {
			connc <- c
		}
	}()
	ticks := 0
	for {
		select {
		case <-ticker.C:
			ticks++
			if ticks == cancelTick {
				close(cancel)
			}
			if ticks == timeoutTick {
				t.Fatal("timeout waiting for dial to fail")
			}
		case c := <-connc:
			c.Close()
			t.Fatal("unexpected successful connection")
		case err := <-errc:
			if perr := parseDialError(err); perr != nil {
				t.Error(perr)
			}
			if ticks < cancelTick {
				// Using strings.Contains is ugly but
				// may work on plan9 and windows.
				ignorable := []string{
					"connection refused",
					"unreachable",
					"no route to host",
					"invalid argument",
				}
				e := err.Error()
				for _, ignore := range ignorable {
					if strings.Contains(e, ignore) {
						t.Skipf("connection to %v failed fast with %v", blackholeIPPort, err)
					}
				}

				t.Fatalf("dial error after %d ticks (%d before cancel sent): %v",
					ticks, cancelTick-ticks, err)
			}
			if oe, ok := err.(*OpError); !ok || oe.Err != errCanceled {
				t.Fatalf("dial error = %v (%T); want OpError with Err == errCanceled", err, err)
			}
			return // success.
		}
	}
}

func TestCancelAfterDial(t *testing.T) {
	if testing.Short() {
		t.Skip("avoiding time.Sleep")
	}

	ln := newLocalListener(t, "tcp")

	var wg sync.WaitGroup
	wg.Add(1)
	defer func() {
		ln.Close()
		wg.Wait()
	}()

	// Echo back the first line of each incoming connection.
	go func() {
		for {
			c, err := ln.Accept()
			if err != nil {
				break
			}
			rb := bufio.NewReader(c)
			line, err := rb.ReadString('\n')
			if err != nil {
				t.Error(err)
				c.Close()
				continue
			}
			if _, err := c.Write([]byte(line)); err != nil {
				t.Error(err)
			}
			c.Close()
		}
		wg.Done()
	}()

	try := func() {
		cancel := make(chan struct{})
		d := &Dialer{Cancel: cancel}
		c, err := d.Dial("tcp", ln.Addr().String())

		// Immediately after dialing, request cancellation and sleep.
		// Before Issue 15078 was fixed, this would cause subsequent operations
		// to fail with an i/o timeout roughly 50% of the time.
		close(cancel)
		time.Sleep(10 * time.Millisecond)

		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		// Send some data to confirm that the connection is still alive.
		const message = "echo!\n"
		if _, err := c.Write([]byte(message)); err != nil {
			t.Fatal(err)
		}

		// The server should echo the line, and close the connection.
		rb := bufio.NewReader(c)
		line, err := rb.ReadString('\n')
		if err != nil {
			t.Fatal(err)
		}
		if line != message {
			t.Errorf("got %q; want %q", line, message)
		}
		if _, err := rb.ReadByte(); err != io.EOF {
			t.Errorf("got %v; want %v", err, io.EOF)
		}
	}

	// This bug manifested about 50% of the time, so try it a few times.
	for i := 0; i < 10; i++ {
		try()
	}
}

func TestDialClosedPortFailFast(t *testing.T) {
	if runtime.GOOS != "windows" {
		// Reported by go.dev/issues/23366.
		t.Skip("skipping windows only test")
	}
	for _, network := range []string{"tcp", "tcp4", "tcp6"} {
		t.Run(network, func(t *testing.T) {
			if !testableNetwork(network) {
				t.Skipf("skipping: can't listen on %s", network)
			}
			// Reserve a local port till the end of the
			// test by opening a listener and connecting to
			// it using Dial.
			ln := newLocalListener(t, network)
			addr := ln.Addr().String()
			conn1, err := Dial(network, addr)
			if err != nil {
				ln.Close()
				t.Fatal(err)
			}
			defer conn1.Close()
			// Now close the listener so the next Dial fails
			// keeping conn1 alive so the port is not made
			// available.
			ln.Close()

			maxElapsed := time.Second
			// The host can be heavy-loaded and take
			// longer than configured. Retry until
			// Dial takes less than maxElapsed or
			// the test times out.
			for {
				startTime := time.Now()
				conn2, err := Dial(network, addr)
				if err == nil {
					conn2.Close()
					t.Fatal("error expected")
				}
				elapsed := time.Since(startTime)
				if elapsed < maxElapsed {
					break
				}
				t.Logf("got %v; want < %v", elapsed, maxElapsed)
			}
		})
	}
}

// Issue 18806: it should always be possible to net.Dial a
// net.Listener().Addr().String when the listen address was ":n", even
// if the machine has halfway configured IPv6 such that it can bind on
// "::" not connect back to that same address.
func TestDialListenerAddr(t *testing.T) {
	if !testableNetwork("tcp4") {
		t.Skipf("skipping: can't listen on tcp4")
	}

	// The original issue report was for listening on just ":0" on a system that
	// supports both tcp4 and tcp6 for external traffic but only tcp4 for loopback
	// traffic. However, the port opened by ":0" is externally-accessible, and may
	// trigger firewall alerts or otherwise be mistaken for malicious activity
	// (see https://go.dev/issue/59497). Moreover, it often does not reproduce
	// the scenario in the issue, in which the port *cannot* be dialed as tcp6.
	//
	// To address both of those problems, we open a tcp4-only localhost port, but
	// then dial the address string that the listener would have reported for a
	// dual-stack port.
	ln, err := Listen("tcp4", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	t.Logf("listening on %q", ln.Addr())
	_, port, err := SplitHostPort(ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}

	// If we had opened a dual-stack port without an explicit "localhost" address,
	// the Listener would arbitrarily report an empty tcp6 address in its Addr
	// string.
	//
	// The documentation for Dial says ‘if the host is empty or a literal
	// unspecified IP address, as in ":80", "0.0.0.0:80" or "[::]:80" for TCP and
	// UDP, "", "0.0.0.0" or "::" for IP, the local system is assumed.’
	// In #18806, it was decided that that should include the local tcp4 host
	// even if the string is in the tcp6 format.
	dialAddr := "[::]:" + port
	c, err := Dial("tcp4", dialAddr)
	if err != nil {
		t.Fatalf(`Dial("tcp4", %q): %v`, dialAddr, err)
	}
	c.Close()
	t.Logf(`Dial("tcp4", %q) succeeded`, dialAddr)
}

func TestDialerControl(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("not supported on %s", runtime.GOOS)
	case "js", "wasip1":
		t.Skipf("skipping: fake net does not support Dialer.Control")
	}

	t.Run("StreamDial", func(t *testing.T) {
		for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
			if !testableNetwork(network) {
				continue
			}
			ln := newLocalListener(t, network)
			defer ln.Close()
			d := Dialer{Control: controlOnConnSetup}
			c, err := d.Dial(network, ln.Addr().String())
			if err != nil {
				t.Error(err)
				continue
			}
			c.Close()
		}
	})
	t.Run("PacketDial", func(t *testing.T) {
		for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} {
			if !testableNetwork(network) {
				continue
			}
			c1 := newLocalPacketListener(t, network)
			if network == "unixgram" {
				defer os.Remove(c1.LocalAddr().String())
			}
			defer c1.Close()
			d := Dialer{Control: controlOnConnSetup}
			c2, err := d.Dial(network, c1.LocalAddr().String())
			if err != nil {
				t.Error(err)
				continue
			}
			c2.Close()
		}
	})
}

func TestDialerControlContext(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
	case "js", "wasip1":
		t.Skipf("skipping: fake net does not support Dialer.ControlContext")
	}
	t.Run("StreamDial", func(t *testing.T) {
		for i, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
			t.Run(network, func(t *testing.T) {
				if !testableNetwork(network) {
					t.Skipf("skipping: %s not available", network)
				}

				ln := newLocalListener(t, network)
				defer ln.Close()
				var id int
				d := Dialer{ControlContext: func(ctx context.Context, network string, address string, c syscall.RawConn) error {
					id = ctx.Value("id").(int)
					return controlOnConnSetup(network, address, c)
				}}
				c, err := d.DialContext(context.WithValue(context.Background(), "id", i+1), network, ln.Addr().String())
				if err != nil {
					t.Fatal(err)
				}
				if id != i+1 {
					t.Errorf("got id %d, want %d", id, i+1)
				}
				c.Close()
			})
		}
	})
}

// mustHaveExternalNetwork is like testenv.MustHaveExternalNetwork
// except on non-Linux, non-mobile builders it permits the test to
// run in -short mode.
func mustHaveExternalNetwork(t *testing.T) {
	t.Helper()
	definitelyHasLongtestBuilder := runtime.GOOS == "linux"
	mobile := runtime.GOOS == "android" || runtime.GOOS == "ios"
	fake := runtime.GOOS == "js" || runtime.GOOS == "wasip1"
	if testenv.Builder() != "" && !definitelyHasLongtestBuilder && !mobile && !fake {
		// On a non-Linux, non-mobile builder (e.g., freebsd-amd64-13_0).
		//
		// Don't skip testing because otherwise the test may never run on
		// any builder if this port doesn't also have a -longtest builder.
		return
	}
	testenv.MustHaveExternalNetwork(t)
}

type contextWithNonZeroDeadline struct {
	context.Context
}

func (contextWithNonZeroDeadline) Deadline() (time.Time, bool) {
	// Return non-zero time.Time value with false indicating that no deadline is set.
	return time.Unix(0, 0), false
}

func TestDialWithNonZeroDeadline(t *testing.T) {
	ln := newLocalListener(t, "tcp")
	defer ln.Close()
	_, port, err := SplitHostPort(ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}

	ctx := contextWithNonZeroDeadline{Context: context.Background()}
	var dialer Dialer
	c, err := dialer.DialContext(ctx, "tcp", JoinHostPort("", port))
	if err != nil {
		t.Fatal(err)
	}
	c.Close()
}
