github.com/anacrolix/torrent@v1.61.0/reuse_test.go (about) 1 package torrent 2 3 import ( 4 "context" 5 "net" 6 "sync/atomic" 7 "syscall" 8 "testing" 9 10 "github.com/anacrolix/log" 11 qt "github.com/go-quicktest/qt" 12 ) 13 14 // Show that multiple connections from the same local TCP port to the same remote port will fail. 15 func TestTcpPortReuseIsABadIdea(t *testing.T) { 16 remote, err := net.Listen("tcp", "localhost:0") 17 qt.Assert(t, qt.IsNil(err)) 18 defer remote.Close() 19 dialer := net.Dialer{} 20 // Show that we can't duplicate an existing connection even with various socket options. 21 dialer.Control = func(network, address string, c syscall.RawConn) (err error) { 22 return c.Control(func(fd uintptr) { 23 err = setReusePortSockOpts(fd) 24 }) 25 } 26 // Tie up a local port to the remote. 27 first, err := dialer.Dial("tcp", remote.Addr().String()) 28 qt.Assert(t, qt.IsNil(err)) 29 defer first.Close() 30 // Show that dialling the remote with the same local port fails. 31 dialer.LocalAddr = first.LocalAddr() 32 _, err = dialer.Dial("tcp", remote.Addr().String()) 33 qt.Assert(t, qt.IsNotNil(err)) 34 // Show that not fixing the local port again allows connections to succeed. 35 dialer.LocalAddr = nil 36 second, err := dialer.Dial("tcp", remote.Addr().String()) 37 qt.Assert(t, qt.IsNil(err)) 38 second.Close() 39 } 40 41 // Show that multiple connections from the same local utp socket to the same remote port will 42 // succeed. This is necessary for ut_holepunch to work. 43 func TestUtpLocalPortIsReusable(t *testing.T) { 44 const network = "udp" 45 remote, err := NewUtpSocket(network, "localhost:0", nil, log.Default) 46 qt.Assert(t, qt.IsNil(err)) 47 defer remote.Close() 48 var remoteAccepts int32 49 doneAccepting := make(chan struct{}) 50 go func() { 51 defer close(doneAccepting) 52 for { 53 c, err := remote.Accept() 54 if err != nil { 55 if atomic.LoadInt32(&remoteAccepts) != 2 { 56 t.Logf("error accepting on remote: %v", err) 57 } 58 break 59 } 60 // This is not a leak, bugger off. 61 defer c.Close() 62 atomic.AddInt32(&remoteAccepts, 1) 63 } 64 }() 65 local, err := NewUtpSocket(network, "localhost:0", nil, log.Default) 66 qt.Assert(t, qt.IsNil(err)) 67 defer local.Close() 68 first, err := local.DialContext(context.Background(), network, remote.Addr().String()) 69 qt.Assert(t, qt.IsNil(err)) 70 defer first.Close() 71 second, err := local.DialContext(context.Background(), network, remote.Addr().String()) 72 qt.Assert(t, qt.IsNil(err)) 73 defer second.Close() 74 remote.Close() 75 <-doneAccepting 76 qt.Assert(t, qt.Equals(atomic.LoadInt32(&remoteAccepts), int32(2))) 77 }