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  }