portfwd.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package torrent
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. "github.com/anacrolix/log"
  7. "github.com/anacrolix/upnp"
  8. )
  9. const UpnpDiscoverLogTag = "upnp-discover"
  10. type upnpMapping struct {
  11. d upnp.Device
  12. proto upnp.Protocol
  13. externalPort int
  14. }
  15. func (cl *Client) addPortMapping(d upnp.Device, proto upnp.Protocol, internalPort int, upnpID string) {
  16. logger := cl.logger.WithContextText(fmt.Sprintf("UPnP device at %v: mapping internal %v port %v", d.GetLocalIPAddress(), proto, internalPort))
  17. externalPort, err := d.AddPortMapping(proto, internalPort, internalPort, upnpID, 0)
  18. if err != nil {
  19. logger.WithDefaultLevel(log.Warning).Printf("error: %v", err)
  20. return
  21. }
  22. cl.lock()
  23. cl.upnpMappings = append(cl.upnpMappings, &upnpMapping{d, proto, externalPort})
  24. cl.unlock()
  25. level := log.Info
  26. if externalPort != internalPort {
  27. level = log.Warning
  28. }
  29. logger.WithDefaultLevel(level).Printf("success: external port %v", externalPort)
  30. }
  31. func (cl *Client) forwardPort() {
  32. cl.lock()
  33. defer cl.unlock()
  34. if cl.config.NoDefaultPortForwarding {
  35. return
  36. }
  37. cl.unlock()
  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. cl.lock()
  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. func (cl *Client) clearPortMappings() {
  59. mLen := len(cl.upnpMappings)
  60. if mLen == 0 {
  61. return
  62. }
  63. var wg sync.WaitGroup
  64. wg.Add(mLen)
  65. for _, m := range cl.upnpMappings {
  66. go func(m *upnpMapping) {
  67. defer wg.Done()
  68. cl.deletePortMapping(m.d, m.proto, m.externalPort)
  69. }(m)
  70. }
  71. cl.upnpMappings = nil
  72. }