]> Sergey Matveev's repositories - btrtrc.git/blob - reuse_test.go
Drop support for go 1.20
[btrtrc.git] / reuse_test.go
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/frankban/quicktest"
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         c := qt.New(t)
18         c.Assert(err, qt.IsNil)
19         defer remote.Close()
20         dialer := net.Dialer{}
21         // Show that we can't duplicate an existing connection even with various socket options.
22         dialer.Control = func(network, address string, c syscall.RawConn) (err error) {
23                 return c.Control(func(fd uintptr) {
24                         err = setReusePortSockOpts(fd)
25                 })
26         }
27         // Tie up a local port to the remote.
28         first, err := dialer.Dial("tcp", remote.Addr().String())
29         c.Assert(err, qt.IsNil)
30         defer first.Close()
31         // Show that dialling the remote with the same local port fails.
32         dialer.LocalAddr = first.LocalAddr()
33         _, err = dialer.Dial("tcp", remote.Addr().String())
34         c.Assert(err, qt.IsNotNil)
35         // Show that not fixing the local port again allows connections to succeed.
36         dialer.LocalAddr = nil
37         second, err := dialer.Dial("tcp", remote.Addr().String())
38         c.Assert(err, qt.IsNil)
39         second.Close()
40 }
41
42 // Show that multiple connections from the same local utp socket to the same remote port will
43 // succeed. This is necessary for ut_holepunch to work.
44 func TestUtpLocalPortIsReusable(t *testing.T) {
45         const network = "udp"
46         c := qt.New(t)
47         remote, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
48         c.Assert(err, qt.IsNil)
49         defer remote.Close()
50         var remoteAccepts int32
51         doneAccepting := make(chan struct{})
52         go func() {
53                 defer close(doneAccepting)
54                 for {
55                         c, err := remote.Accept()
56                         if err != nil {
57                                 if atomic.LoadInt32(&remoteAccepts) != 2 {
58                                         t.Logf("error accepting on remote: %v", err)
59                                 }
60                                 break
61                         }
62                         // This is not a leak, bugger off.
63                         defer c.Close()
64                         atomic.AddInt32(&remoteAccepts, 1)
65                 }
66         }()
67         local, err := NewUtpSocket(network, "localhost:0", nil, log.Default)
68         c.Assert(err, qt.IsNil)
69         defer local.Close()
70         first, err := local.DialContext(context.Background(), network, remote.Addr().String())
71         c.Assert(err, qt.IsNil)
72         defer first.Close()
73         second, err := local.DialContext(context.Background(), network, remote.Addr().String())
74         c.Assert(err, qt.IsNil)
75         defer second.Close()
76         remote.Close()
77         <-doneAccepting
78         c.Assert(atomic.LoadInt32(&remoteAccepts), qt.Equals, int32(2))
79 }