11 "github.com/anacrolix/log"
12 qt "github.com/frankban/quicktest"
15 // Show that multiple connections from the same local TCP port to the same remote port will fail.
16 func TestTcpPortReuseIsABadIdea(t *testing.T) {
17 remote, err := net.Listen("tcp", "localhost:0")
19 c.Assert(err, qt.IsNil)
21 dialer := net.Dialer{}
22 dialer.Control = func(network, address string, c syscall.RawConn) (err error) {
23 return c.Control(func(fd uintptr) {
24 err = setReusePortSockOpts(fd)
27 first, err := dialer.Dial("tcp", remote.Addr().String())
28 c.Assert(err, qt.IsNil)
30 dialer.LocalAddr = first.LocalAddr()
31 _, err = dialer.Dial("tcp", remote.Addr().String())
32 c.Assert(errors.Is(err, syscall.EADDRINUSE), qt.IsTrue)
35 // Show that multiple connections from the same local utp socket to the same remote port will
36 // succeed. This is necessary for ut_holepunch to work.
37 func TestUtpLocalPortIsReusable(t *testing.T) {
40 remote, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
41 c.Assert(err, qt.IsNil)
43 var remoteAccepts int32
44 doneAccepting := make(chan struct{})
46 defer close(doneAccepting)
48 c, err := remote.Accept()
50 if atomic.LoadInt32(&remoteAccepts) != 2 {
51 t.Logf("error accepting on remote: %v", err)
55 // This is not a leak, bugger off.
57 atomic.AddInt32(&remoteAccepts, 1)
60 local, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
61 c.Assert(err, qt.IsNil)
63 first, err := local.DialContext(context.Background(), network, remote.Addr().String())
64 c.Assert(err, qt.IsNil)
66 second, err := local.DialContext(context.Background(), network, remote.Addr().String())
67 c.Assert(err, qt.IsNil)
71 c.Assert(atomic.LoadInt32(&remoteAccepts), qt.Equals, int32(2))