]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Support operating when IPv6 isn't available
authorMatt Joiner <anacrolix@gmail.com>
Thu, 22 Feb 2024 03:30:21 +0000 (14:30 +1100)
committerMatt Joiner <anacrolix@gmail.com>
Thu, 22 Feb 2024 03:30:21 +0000 (14:30 +1100)
Dockerfile [new file with mode: 0644]
client-nowasm_test.go
client.go
client_test.go
network_test.go
socket.go
test/issue377_test.go
tracker/udp/udp_test.go

diff --git a/Dockerfile b/Dockerfile
new file mode 100644 (file)
index 0000000..5cfe148
--- /dev/null
@@ -0,0 +1,26 @@
+# On macOS, docker does not support IPv6.
+
+FROM alpine
+
+RUN apk add go fuse bash
+
+RUN go install github.com/anacrolix/godo@v1
+RUN echo "$HOME"
+ENV PATH="/root/go/bin:$PATH"
+
+WORKDIR /src
+
+COPY . .
+
+ARG GOCACHE=/root/.cache/go-build
+ARG GOMODCACHE=/root/go/pkg/mod
+
+RUN --mount=type=cache,target=$GOCACHE \
+       --mount=type=cache,target=$GOMODCACHE \
+       go test -failfast ./...
+
+# Can't use fuse inside Docker? Asks for modprobe fuse.
+
+# RUN --mount=type=cache,target=$GOCACHE \
+#      --mount=type=cache,target=$GOMODCACHE \
+#      ./fs/test.sh
index 9b93139da51142e200bc081f7b82c8941bdd04e2..08ed80ced4df8637df8919c5193939c9fa6fe56d 100644 (file)
@@ -15,6 +15,7 @@ import (
 )
 
 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
+       c := qt.New(t)
        cfg := TestingConfig(t)
        pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
        require.NoError(t, err)
@@ -22,7 +23,7 @@ func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
        defer ci.Close()
        cfg.DefaultStorage = ci
        cl, err := NewClient(cfg)
-       require.NoError(t, err)
+       c.Assert(err, qt.IsNil, qt.Commentf("%#v", err))
        cl.Close()
        // And again, https://github.com/anacrolix/torrent/issues/158
        cl, err = NewClient(cfg)
index 7aab0402a70fc5333e44e759ac96b306ae07c696..7e3fa0f13425162595c5b3aeb5bd3b553123cde9 100644 (file)
--- a/client.go
+++ b/client.go
@@ -256,10 +256,21 @@ func NewClient(cfg *ClientConfig) (cl *Client, err error) {
                }
        }
 
-       sockets, err := listenAll(cl.listenNetworks(), cl.config.ListenHost, cl.config.ListenPort, cl.firewallCallback, cl.logger)
+       builtinListenNetworks := cl.listenNetworks()
+       sockets, err := listenAll(
+               builtinListenNetworks,
+               cl.config.ListenHost,
+               cl.config.ListenPort,
+               cl.firewallCallback,
+               cl.logger,
+       )
        if err != nil {
                return
        }
+       if len(sockets) == 0 && len(builtinListenNetworks) != 0 {
+               err = fmt.Errorf("no sockets created for networks %v", builtinListenNetworks)
+               return
+       }
 
        // Check for panics.
        cl.LocalPort()
index d2a88e9e7a769abcd1871092c006e37b33573a51..5463d41c3f37e09dbc3f7dd9bea30cc8792520e9 100644 (file)
@@ -339,6 +339,7 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
 }
 
 func TestDhtInheritBlocklist(t *testing.T) {
+       c := qt.New(t)
        ipl := iplist.New(nil)
        require.NotNil(t, ipl)
        cfg := TestingConfig(t)
@@ -353,7 +354,7 @@ func TestDhtInheritBlocklist(t *testing.T) {
                assert.Equal(t, ipl, s.(AnacrolixDhtServerWrapper).Server.IPBlocklist())
                numServers++
        })
-       assert.EqualValues(t, 2, numServers)
+       c.Assert(numServers, qt.Not(qt.Equals), 0)
 }
 
 // Check that stuff is merged in subsequent AddTorrentSpec for the same
index a1fd8806c9edec772028afb69a74c800ebe7fe2e..691d6725b852427201cd3c5fbf8776ccfb84db0b 100644 (file)
@@ -16,6 +16,9 @@ func testListenerNetwork(
        expectedNet, givenNet, addr string, validIp4 bool,
 ) {
        l, err := listenFunc(givenNet, addr)
+       if isUnsupportedNetworkError(err) {
+               return
+       }
        require.NoError(t, err)
        defer l.Close()
        assert.EqualValues(t, expectedNet, l.Addr().Network())
@@ -49,10 +52,13 @@ func testAcceptedConnAddr(
        require.NoError(t, err)
        defer c.Close()
        assert.EqualValues(t, network, c.RemoteAddr().Network())
-       assert.Equal(t, valid4, missinggo.AddrIP(c.RemoteAddr()).To4() != nil)
+       assert.Equal(t, valid4, missinggo.AddrIP(c.RemoteAddr()).To4() == nil)
 }
 
-func listenClosure(rawListenFunc func(string, string) (net.Listener, error), network, addr string) func() (net.Listener, error) {
+func listenClosure(
+       rawListenFunc func(string, string) (net.Listener, error),
+       network, addr string,
+) func() (net.Listener, error) {
        return func() (net.Listener, error) {
                return rawListenFunc(network, addr)
        }
@@ -76,6 +82,6 @@ func TestListenLocalhostNetwork(t *testing.T) {
                "tcp",
                false,
                dialClosure(net.Dial, "tcp"),
-               listenClosure(net.Listen, "tcp6", "localhost:0"),
+               listenClosure(net.Listen, "tcp4", "localhost:0"),
        )
 }
index 2d4ea863ac292f39e4d97effd297fe2b98afcfa5..0f45dcbae796a5bbb21d2945d6ce25edd4116af1 100644 (file)
--- a/socket.go
+++ b/socket.go
@@ -2,14 +2,17 @@ package torrent
 
 import (
        "context"
+       "errors"
+       "fmt"
+       g "github.com/anacrolix/generics"
        "net"
+       "os"
        "strconv"
        "syscall"
 
        "github.com/anacrolix/log"
        "github.com/anacrolix/missinggo/perf"
        "github.com/anacrolix/missinggo/v2"
-       "github.com/pkg/errors"
 )
 
 type Listener interface {
@@ -111,7 +114,13 @@ type tcpSocket struct {
        NetworkDialer
 }
 
-func listenAll(networks []network, getHost func(string) string, port int, f firewallCallback, logger log.Logger) ([]socket, error) {
+func listenAll(
+       networks []network,
+       getHost func(string) string,
+       port int,
+       f firewallCallback,
+       logger log.Logger,
+) ([]socket, error) {
        if len(networks) == 0 {
                return nil, nil
        }
@@ -132,13 +141,27 @@ type networkAndHost struct {
        Host    string
 }
 
-func listenAllRetry(nahs []networkAndHost, port int, f firewallCallback, logger log.Logger) (ss []socket, retry bool, err error) {
-       ss = make([]socket, 1, len(nahs))
-       portStr := strconv.FormatInt(int64(port), 10)
-       ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), f, logger)
-       if err != nil {
-               return nil, false, errors.Wrap(err, "first listen")
+func isUnsupportedNetworkError(err error) bool {
+       var sysErr *os.SyscallError
+       //spewCfg := spew.NewDefaultConfig()
+       //spewCfg.ContinueOnMethod = true
+       //spewCfg.Dump(err)
+       if !errors.As(err, &sysErr) {
+               return false
        }
+       //spewCfg.Dump(sysErr)
+       //spewCfg.Dump(sysErr.Err.Error())
+       // This might only be Linux specific.
+       return sysErr.Syscall == "bind" && sysErr.Err.Error() == "cannot assign requested address"
+}
+
+func listenAllRetry(
+       nahs []networkAndHost,
+       port int,
+       f firewallCallback,
+       logger log.Logger,
+) (ss []socket, retry bool, err error) {
+       // Close all sockets on error or retry.
        defer func() {
                if err != nil || retry {
                        for _, s := range ss {
@@ -147,15 +170,27 @@ func listenAllRetry(nahs []networkAndHost, port int, f firewallCallback, logger
                        ss = nil
                }
        }()
-       portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
-       for _, nah := range nahs[1:] {
-               s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f, logger)
+       g.MakeSliceWithCap(&ss, len(nahs))
+       portStr := strconv.FormatInt(int64(port), 10)
+       for _, nah := range nahs {
+               var s socket
+               s, err = listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f, logger)
                if err != nil {
-                       return ss,
-                               missinggo.IsAddrInUse(err) && port == 0,
-                               errors.Wrap(err, "subsequent listen")
+                       if isUnsupportedNetworkError(err) {
+                               err = nil
+                               continue
+                       }
+                       if len(ss) == 0 {
+                               // First relative to a possibly dynamic port (0).
+                               err = fmt.Errorf("first listen: %w", err)
+                       } else {
+                               err = fmt.Errorf("subsequent listen: %w", err)
+                       }
+                       retry = missinggo.IsAddrInUse(err) && port == 0
+                       return
                }
                ss = append(ss, s)
+               portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
        }
        return
 }
index 5b0e659fee5709e0dfbacf9c01d07955a58f354b..c5bfae2c38634d6f7ff9388a3c924c6f46bc34e2 100644 (file)
@@ -21,7 +21,7 @@ import (
 
 func justOneNetwork(cc *torrent.ClientConfig) {
        cc.DisableTCP = true
-       cc.DisableIPv4 = true
+       cc.DisableIPv6 = true
 }
 
 func TestReceiveChunkStorageFailureSeederFastExtensionDisabled(t *testing.T) {
index 64aeb80ff7426c514f5f63e7c2eab71fb1b5851b..378351cd81d760ef5f6e6eb7bd9563209245a2eb 100644 (file)
@@ -95,7 +95,10 @@ func TestConnClientLogDispatchUnknownTransactionId(t *testing.T) {
        c.Assert(err, qt.IsNil)
        defer pc.Close()
        ccAddr := *cc.LocalAddr().(*net.UDPAddr)
-       ccAddr.IP = net.IPv6loopback
+       ipAddrs, err := net.DefaultResolver.LookupIPAddr(context.Background(), "localhost")
+       c.Assert(err, qt.IsNil)
+       ccAddr.IP = ipAddrs[0].IP
+       ccAddr.Zone = ipAddrs[0].Zone
        _, err = pc.WriteTo(make([]byte, 30), &ccAddr)
        c.Assert(err, qt.IsNil)
 }