11 "github.com/anacrolix/log"
13 qt "github.com/frankban/quicktest"
16 // Show that multiple connections from the same local TCP port to the same remote port will fail.
17 func TestTcpPortReuseIsABadIdea(t *testing.T) {
18 remote, err := net.Listen("tcp", "localhost:0")
20 c.Assert(err, qt.IsNil)
22 dialer := net.Dialer{}
23 dialer.Control = func(network, address string, c syscall.RawConn) (err error) {
24 return c.Control(func(fd uintptr) {
25 err = setReusePortSockOpts(fd)
28 first, err := dialer.Dial("tcp", remote.Addr().String())
29 c.Assert(err, qt.IsNil)
31 dialer.LocalAddr = first.LocalAddr()
32 _, err = dialer.Dial("tcp", remote.Addr().String())
33 c.Assert(errors.Is(err, syscall.EADDRINUSE), qt.IsTrue)
36 // Show that multiple connections from the same local utp socket to the same remote port will
37 // succeed. This is necessary for ut_holepunch to work.
38 func TestUtpLocalPortIsReusable(t *testing.T) {
41 remote, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
42 c.Assert(err, qt.IsNil)
44 var remoteAccepts int32
45 doneAccepting := make(chan struct{})
47 defer close(doneAccepting)
49 c, err := remote.Accept()
51 if atomic.LoadInt32(&remoteAccepts) != 2 {
52 t.Logf("error accepting on remote: %v", err)
56 // This is not a leak, bugger off.
58 atomic.AddInt32(&remoteAccepts, 1)
61 local, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
62 c.Assert(err, qt.IsNil)
64 first, err := local.DialContext(context.Background(), network, remote.Addr().String())
65 c.Assert(err, qt.IsNil)
67 second, err := local.DialContext(context.Background(), network, remote.Addr().String())
68 c.Assert(err, qt.IsNil)
72 c.Assert(atomic.LoadInt32(&remoteAccepts), qt.Equals, int32(2))