Skip to content

Commit

Permalink
Support operating when IPv6 isn't available
Browse files Browse the repository at this point in the history
  • Loading branch information
anacrolix committed Feb 22, 2024
1 parent fb4f31a commit 2cf5c7e
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 22 deletions.
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion client-nowasm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import (
)

func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
c := qt.New(t)
cfg := TestingConfig(t)
pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
require.NoError(t, err)
ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
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)
Expand Down
13 changes: 12 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
12 changes: 9 additions & 3 deletions network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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)
}
Expand All @@ -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"),
)
}
63 changes: 49 additions & 14 deletions socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion test/issue377_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

func justOneNetwork(cc *torrent.ClientConfig) {
cc.DisableTCP = true
cc.DisableIPv4 = true
cc.DisableIPv6 = true
}

func TestReceiveChunkStorageFailureSeederFastExtensionDisabled(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion tracker/udp/udp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit 2cf5c7e

Please sign in to comment.