client.go | 1 + client_test.go | 9 +++++---- config.go | 3 ++- portfwd.go | 41 +++++++++++++++++++++++++++++++++++++++++ diff --git a/client.go b/client.go index fba0b1e22e020ee95298ffaf05b7c701d2e77cab..ab2fe08d9fc8866b82f93e3c466e9422d42027e9 100644 --- a/client.go +++ b/client.go @@ -303,6 +303,7 @@ cl.config.ListenAddr) 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 13c6cbddd9811742be45e71786fe69d48119ed11..51f8ad4b70bf951daca7ad1f583470637ea39844 100644 --- a/client_test.go +++ b/client_test.go @@ -33,10 +33,11 @@ ) 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 cb597b200c7df7b38b9105891a97dc27a639f45e..a8933abaaaeca943cfe55d98a3ea03d55d2b64aa 100644 --- a/config.go +++ b/config.go @@ -33,7 +33,8 @@ DataDir string `long:"data-dir" description:"directory to store downloaded torrent data"` // 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 0000000000000000000000000000000000000000..1f1ecd58095081d016efabc2b8097c9f5019f2f1 --- /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() +}