github.com/anacrolix/torrent@v1.61.0/portfwd.go (about) 1 package torrent 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/anacrolix/log" 9 "github.com/anacrolix/upnp" 10 ) 11 12 const UpnpDiscoverLogTag = "upnp-discover" 13 14 type upnpMapping struct { 15 d upnp.Device 16 proto upnp.Protocol 17 externalPort int 18 } 19 20 func (cl *Client) addPortMapping(d upnp.Device, proto upnp.Protocol, internalPort int, upnpID string) { 21 logger := cl.logger.WithContextText(fmt.Sprintf("UPnP device at %v: mapping internal %v port %v", d.GetLocalIPAddress(), proto, internalPort)) 22 externalPort, err := d.AddPortMapping(proto, internalPort, internalPort, upnpID, 0) 23 if err != nil { 24 logger.WithDefaultLevel(log.Warning).Printf("error: %v", err) 25 return 26 } 27 cl.lock() 28 cl.upnpMappings = append(cl.upnpMappings, &upnpMapping{d, proto, externalPort}) 29 cl.unlock() 30 level := log.Info 31 if externalPort != internalPort { 32 level = log.Warning 33 } 34 logger.WithDefaultLevel(level).Printf("success: external port %v", externalPort) 35 } 36 37 func (cl *Client) forwardPort() { 38 ds := upnp.Discover(0, 2*time.Second, cl.logger.WithValues(UpnpDiscoverLogTag)) 39 cl.lock() 40 cl.logger.WithDefaultLevel(log.Debug).Printf("discovered %d upnp devices", len(ds)) 41 port := cl.incomingPeerPort() 42 id := cl.config.UpnpID 43 cl.unlock() 44 for _, d := range ds { 45 go cl.addPortMapping(d, upnp.TCP, port, id) 46 go cl.addPortMapping(d, upnp.UDP, port, id) 47 } 48 } 49 50 func (cl *Client) deletePortMapping(d upnp.Device, proto upnp.Protocol, externalPort int) { 51 logger := cl.logger.WithContextText(fmt.Sprintf("UPnP device at %v: delete mapping internal %v port %v", d.GetLocalIPAddress(), proto, externalPort)) 52 err := d.DeletePortMapping(proto, externalPort) 53 if err != nil { 54 logger.WithDefaultLevel(log.Warning).Printf("error: %v", err) 55 return 56 } 57 } 58 59 func (cl *Client) clearPortMappings() { 60 mLen := len(cl.upnpMappings) 61 if mLen == 0 { 62 return 63 } 64 65 var wg sync.WaitGroup 66 wg.Add(mLen) 67 for _, m := range cl.upnpMappings { 68 go func(m *upnpMapping) { 69 defer wg.Done() 70 cl.deletePortMapping(m.d, m.proto, m.externalPort) 71 }(m) 72 } 73 cl.upnpMappings = nil 74 }