github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/nat/natupnp.go (about)

     1  package nat
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/fjl/goupnp"
    11  	"github.com/fjl/goupnp/dcps/internetgateway1"
    12  	"github.com/fjl/goupnp/dcps/internetgateway2"
    13  )
    14  
    15  type upnp struct {
    16  	dev     *goupnp.RootDevice
    17  	service string
    18  	client  upnpClient
    19  }
    20  
    21  type upnpClient interface {
    22  	GetExternalIPAddress() (string, error)
    23  	AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
    24  	DeletePortMapping(string, uint16, string) error
    25  	GetNATRSIPStatus() (sip bool, nat bool, err error)
    26  }
    27  
    28  func (n *upnp) ExternalIP() (addr net.IP, err error) {
    29  	ipString, err := n.client.GetExternalIPAddress()
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	ip := net.ParseIP(ipString)
    34  	if ip == nil {
    35  		return nil, errors.New("bad IP in response")
    36  	}
    37  	return ip, nil
    38  }
    39  
    40  func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
    41  	ip, err := n.internalAddress()
    42  	if err != nil {
    43  		return nil
    44  	}
    45  	protocol = strings.ToUpper(protocol)
    46  	lifetimeS := uint32(lifetime / time.Second)
    47  	return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
    48  }
    49  
    50  func (n *upnp) internalAddress() (net.IP, error) {
    51  	devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	ifaces, err := net.Interfaces()
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	for _, iface := range ifaces {
    60  		addrs, err := iface.Addrs()
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  		for _, addr := range addrs {
    65  			switch x := addr.(type) {
    66  			case *net.IPNet:
    67  				if x.Contains(devaddr.IP) {
    68  					return x.IP, nil
    69  				}
    70  			}
    71  		}
    72  	}
    73  	return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
    74  }
    75  
    76  func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
    77  	return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
    78  }
    79  
    80  func (n *upnp) String() string {
    81  	return "UPNP " + n.service
    82  }
    83  
    84  // discoverUPnP searches for Internet Gateway Devices
    85  // and returns the first one it can find on the local network.
    86  func discoverUPnP() Interface {
    87  	found := make(chan *upnp, 2)
    88  	// IGDv1
    89  	go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
    90  		switch sc.Service.ServiceType {
    91  		case internetgateway1.URN_WANIPConnection_1:
    92  			return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{sc}}
    93  		case internetgateway1.URN_WANPPPConnection_1:
    94  			return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{sc}}
    95  		}
    96  		return nil
    97  	})
    98  	// IGDv2
    99  	go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
   100  		switch sc.Service.ServiceType {
   101  		case internetgateway2.URN_WANIPConnection_1:
   102  			return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{sc}}
   103  		case internetgateway2.URN_WANIPConnection_2:
   104  			return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{sc}}
   105  		case internetgateway2.URN_WANPPPConnection_1:
   106  			return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{sc}}
   107  		}
   108  		return nil
   109  	})
   110  	for i := 0; i < cap(found); i++ {
   111  		if c := <-found; c != nil {
   112  			return c
   113  		}
   114  	}
   115  	return nil
   116  }
   117  
   118  func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
   119  	devs, err := goupnp.DiscoverDevices(target)
   120  	if err != nil {
   121  		return
   122  	}
   123  	found := false
   124  	for i := 0; i < len(devs) && !found; i++ {
   125  		if devs[i].Root == nil {
   126  			continue
   127  		}
   128  		devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
   129  			if found {
   130  				return
   131  			}
   132  			// check for a matching IGD service
   133  			sc := goupnp.ServiceClient{service.NewSOAPClient(), devs[i].Root, service}
   134  			upnp := matcher(devs[i].Root, sc)
   135  			if upnp == nil {
   136  				return
   137  			}
   138  			// check whether port mapping is enabled
   139  			if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
   140  				return
   141  			}
   142  			out <- upnp
   143  			found = true
   144  		})
   145  	}
   146  	if !found {
   147  		out <- nil
   148  	}
   149  }