From: Matt Joiner Date: Fri, 28 Apr 2023 11:27:18 +0000 (+1000) Subject: Add test showing that reusing TCP ports isn't a good idea X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=c0c7536caa7bd00626dffd065158e2539a54670c;p=btrtrc.git Add test showing that reusing TCP ports isn't a good idea --- diff --git a/reuse_test.go b/reuse_test.go new file mode 100644 index 00000000..3adf002f --- /dev/null +++ b/reuse_test.go @@ -0,0 +1,70 @@ +package torrent + +import ( + "context" + "errors" + "net" + "sync/atomic" + "syscall" + "testing" + + "github.com/anacrolix/log" + + qt "github.com/frankban/quicktest" +) + +// Show that multiple connections from the same local TCP port to the same remote port will fail. +func TestTcpPortReuseIsABadIdea(t *testing.T) { + remote, err := net.Listen("tcp", "localhost:0") + c := qt.New(t) + c.Assert(err, qt.IsNil) + defer remote.Close() + dialer := net.Dialer{} + dialer.Control = func(network, address string, c syscall.RawConn) (err error) { + return c.Control(func(fd uintptr) { + err = setReusePortSockOpts(fd) + }) + } + first, err := dialer.Dial("tcp", remote.Addr().String()) + c.Assert(err, qt.IsNil) + defer first.Close() + dialer.LocalAddr = first.LocalAddr() + _, err = dialer.Dial("tcp", remote.Addr().String()) + c.Assert(errors.Is(err, syscall.EADDRINUSE), qt.IsTrue) +} + +// Show that multiple connections from the same local utp socket to the same remote port will +// succeed. This is necessary for ut_holepunch to work. +func TestUtpLocalPortIsReusable(t *testing.T) { + const network = "udp" + c := qt.New(t) + remote, err := NewUtpSocket(network, "localhost:0", nil, log.Default) + c.Assert(err, qt.IsNil) + defer remote.Close() + var remoteAccepts int32 + go func() { + for { + c, err := remote.Accept() + if err != nil { + if atomic.LoadInt32(&remoteAccepts) != 2 { + t.Logf("error accepting on remote: %v", err) + } + break + } + // This is not a leak, bugger off. + defer c.Close() + atomic.AddInt32(&remoteAccepts, 1) + } + }() + local, err := NewUtpSocket(network, "localhost:0", nil, log.Default) + c.Assert(err, qt.IsNil) + defer local.Close() + first, err := local.DialContext(context.Background(), network, remote.Addr().String()) + c.Assert(err, qt.IsNil) + defer first.Close() + second, err := local.DialContext(context.Background(), network, remote.Addr().String()) + c.Assert(err, qt.IsNil) + defer second.Close() + remote.Close() + c.Assert(atomic.LoadInt32(&remoteAccepts), qt.Equals, int32(2)) +}