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  }