diff --git a/client.go b/client.go index fba0b1e2..ab2fe08d 100644 --- a/client.go +++ b/client.go @@ -303,6 +303,7 @@ func NewClient(cfg *Config) (cl *Client, err error) { if err != nil { return } + go cl.forwardPort() if cl.tcpListener != nil { go cl.acceptConnections(cl.tcpListener, false) } diff --git a/client_test.go b/client_test.go index 13c6cbdd..51f8ad4b 100644 --- a/client_test.go +++ b/client_test.go @@ -33,10 +33,11 @@ import ( func TestingConfig() *Config { return &Config{ - ListenAddr: "localhost:0", - NoDHT: true, - DataDir: tempDir(), - DisableTrackers: true, + ListenAddr: "localhost:0", + NoDHT: true, + DataDir: tempDir(), + DisableTrackers: true, + NoDefaultPortForwarding: true, // Debug: true, } } diff --git a/config.go b/config.go index cb597b20..a8933aba 100644 --- a/config.go +++ b/config.go @@ -33,7 +33,8 @@ type Config struct { // The address to listen for new uTP and TCP bittorrent protocol // connections. DHT shares a UDP socket with uTP unless configured // otherwise. - ListenAddr string `long:"listen-addr" value-name:"HOST:PORT"` + ListenAddr string `long:"listen-addr" value-name:"HOST:PORT"` + NoDefaultPortForwarding bool // Don't announce to trackers. This only leaves DHT to discover peers. DisableTrackers bool `long:"disable-trackers"` DisablePEX bool `long:"disable-pex"` diff --git a/portfwd.go b/portfwd.go new file mode 100644 index 00000000..1f1ecd58 --- /dev/null +++ b/portfwd.go @@ -0,0 +1,41 @@ +package torrent + +import ( + "log" + "time" + + flog "github.com/anacrolix/log" + + "github.com/syncthing/syncthing/lib/nat" + "github.com/syncthing/syncthing/lib/upnp" +) + +func addPortMapping(d nat.Device, proto nat.Protocol, internalPort int, debug bool) { + externalPort, err := d.AddPortMapping(proto, internalPort, internalPort, "anacrolix/torrent", 0) + if err != nil { + log.Printf("error adding %s port mapping: %s", proto, err) + } else if externalPort != internalPort { + log.Printf("external port %d does not match internal port %d in port mapping", externalPort, internalPort) + } else if debug { + log.Printf("forwarded external %s port %d", proto, externalPort) + } +} + +func (cl *Client) forwardPort() { + cl.mu.Lock() + defer cl.mu.Unlock() + if cl.config.NoDefaultPortForwarding { + return + } + cl.mu.Unlock() + ds := upnp.Discover(0, 2*time.Second) + cl.mu.Lock() + flog.Default.Emit(flog.Fmsg("discovered %d upnp devices", len(ds))) + port := cl.incomingPeerPort() + cl.mu.Unlock() + for _, d := range ds { + go addPortMapping(d, nat.TCP, port, cl.config.Debug) + go addPortMapping(d, nat.UDP, port, cl.config.Debug) + } + cl.mu.Lock() +}