github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/nat/natupnp.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:44</date>
    10  //</624342658860453888>
    11  
    12  
    13  package nat
    14  
    15  import (
    16  	"errors"
    17  	"fmt"
    18  	"net"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/huin/goupnp"
    23  	"github.com/huin/goupnp/dcps/internetgateway1"
    24  	"github.com/huin/goupnp/dcps/internetgateway2"
    25  )
    26  
    27  const soapRequestTimeout = 3 * time.Second
    28  
    29  type upnp struct {
    30  	dev     *goupnp.RootDevice
    31  	service string
    32  	client  upnpClient
    33  }
    34  
    35  type upnpClient interface {
    36  	GetExternalIPAddress() (string, error)
    37  	AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
    38  	DeletePortMapping(string, uint16, string) error
    39  	GetNATRSIPStatus() (sip bool, nat bool, err error)
    40  }
    41  
    42  func (n *upnp) ExternalIP() (addr net.IP, err error) {
    43  	ipString, err := n.client.GetExternalIPAddress()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	ip := net.ParseIP(ipString)
    48  	if ip == nil {
    49  		return nil, errors.New("bad IP in response")
    50  	}
    51  	return ip, nil
    52  }
    53  
    54  func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
    55  	ip, err := n.internalAddress()
    56  	if err != nil {
    57  		return nil
    58  	}
    59  	protocol = strings.ToUpper(protocol)
    60  	lifetimeS := uint32(lifetime / time.Second)
    61  	n.DeleteMapping(protocol, extport, intport)
    62  	return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
    63  }
    64  
    65  func (n *upnp) internalAddress() (net.IP, error) {
    66  	devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	ifaces, err := net.Interfaces()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	for _, iface := range ifaces {
    75  		addrs, err := iface.Addrs()
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		for _, addr := range addrs {
    80  			if x, ok := addr.(*net.IPNet); ok && x.Contains(devaddr.IP) {
    81  				return x.IP, nil
    82  			}
    83  		}
    84  	}
    85  	return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
    86  }
    87  
    88  func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
    89  	return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
    90  }
    91  
    92  func (n *upnp) String() string {
    93  	return "UPNP " + n.service
    94  }
    95  
    96  //Discoverupnp搜索Internet网关设备
    97  //并返回在本地网络上找到的第一个。
    98  func discoverUPnP() Interface {
    99  	found := make(chan *upnp, 2)
   100  //IGDV1
   101  	go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
   102  		switch sc.Service.ServiceType {
   103  		case internetgateway1.URN_WANIPConnection_1:
   104  			return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}}
   105  		case internetgateway1.URN_WANPPPConnection_1:
   106  			return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}}
   107  		}
   108  		return nil
   109  	})
   110  //IGDV2
   111  	go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
   112  		switch sc.Service.ServiceType {
   113  		case internetgateway2.URN_WANIPConnection_1:
   114  			return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}}
   115  		case internetgateway2.URN_WANIPConnection_2:
   116  			return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}}
   117  		case internetgateway2.URN_WANPPPConnection_1:
   118  			return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}}
   119  		}
   120  		return nil
   121  	})
   122  	for i := 0; i < cap(found); i++ {
   123  		if c := <-found; c != nil {
   124  			return c
   125  		}
   126  	}
   127  	return nil
   128  }
   129  
   130  //查找与给定目标匹配的设备并为所有设备调用Matcher
   131  //每个设备的广告服务。找到第一个非零服务
   132  //被送出去。如果没有匹配的服务,则发送nil。
   133  func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
   134  	devs, err := goupnp.DiscoverDevices(target)
   135  	if err != nil {
   136  		out <- nil
   137  		return
   138  	}
   139  	found := false
   140  	for i := 0; i < len(devs) && !found; i++ {
   141  		if devs[i].Root == nil {
   142  			continue
   143  		}
   144  		devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
   145  			if found {
   146  				return
   147  			}
   148  //检查匹配的IGD服务
   149  			sc := goupnp.ServiceClient{
   150  				SOAPClient: service.NewSOAPClient(),
   151  				RootDevice: devs[i].Root,
   152  				Location:   devs[i].Location,
   153  				Service:    service,
   154  			}
   155  			sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout
   156  			upnp := matcher(devs[i].Root, sc)
   157  			if upnp == nil {
   158  				return
   159  			}
   160  //检查是否启用端口映射
   161  			if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
   162  				return
   163  			}
   164  			out <- upnp
   165  			found = true
   166  		})
   167  	}
   168  	if !found {
   169  		out <- nil
   170  	}
   171  }
   172