github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/nat/natupnp.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2015 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package nat
    26  
    27  import (
    28  	"errors"
    29  	"fmt"
    30  	"net"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/huin/goupnp"
    35  	"github.com/huin/goupnp/dcps/internetgateway1"
    36  	"github.com/huin/goupnp/dcps/internetgateway2"
    37  )
    38  
    39  const soapRequestTimeout = 3 * time.Second
    40  
    41  type upnp struct {
    42  	dev     *goupnp.RootDevice
    43  	service string
    44  	client  upnpClient
    45  }
    46  
    47  type upnpClient interface {
    48  	GetExternalIPAddress() (string, error)
    49  	AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
    50  	DeletePortMapping(string, uint16, string) error
    51  	GetNATRSIPStatus() (sip bool, nat bool, err error)
    52  }
    53  
    54  func (n *upnp) ExternalIP() (addr net.IP, err error) {
    55  	ipString, err := n.client.GetExternalIPAddress()
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	ip := net.ParseIP(ipString)
    60  	if ip == nil {
    61  		return nil, errors.New("bad IP in response")
    62  	}
    63  	return ip, nil
    64  }
    65  
    66  func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
    67  	ip, err := n.internalAddress()
    68  	if err != nil {
    69  		return nil
    70  	}
    71  	protocol = strings.ToUpper(protocol)
    72  	lifetimeS := uint32(lifetime / time.Second)
    73  	n.DeleteMapping(protocol, extport, intport)
    74  	return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
    75  }
    76  
    77  func (n *upnp) internalAddress() (net.IP, error) {
    78  	devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	ifaces, err := net.Interfaces()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	for _, iface := range ifaces {
    87  		addrs, err := iface.Addrs()
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  		for _, addr := range addrs {
    92  			if x, ok := addr.(*net.IPNet); ok && x.Contains(devaddr.IP) {
    93  				return x.IP, nil
    94  			}
    95  		}
    96  	}
    97  	return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
    98  }
    99  
   100  func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
   101  	return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
   102  }
   103  
   104  func (n *upnp) String() string {
   105  	return "UPNP " + n.service
   106  }
   107  
   108  //Discoverupnp搜索Internet网关设备
   109  //并返回在本地网络上找到的第一个。
   110  func discoverUPnP() Interface {
   111  	found := make(chan *upnp, 2)
   112  //IGDV1
   113  	go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
   114  		switch sc.Service.ServiceType {
   115  		case internetgateway1.URN_WANIPConnection_1:
   116  			return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}}
   117  		case internetgateway1.URN_WANPPPConnection_1:
   118  			return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}}
   119  		}
   120  		return nil
   121  	})
   122  //IGDV2
   123  	go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
   124  		switch sc.Service.ServiceType {
   125  		case internetgateway2.URN_WANIPConnection_1:
   126  			return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}}
   127  		case internetgateway2.URN_WANIPConnection_2:
   128  			return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}}
   129  		case internetgateway2.URN_WANPPPConnection_1:
   130  			return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}}
   131  		}
   132  		return nil
   133  	})
   134  	for i := 0; i < cap(found); i++ {
   135  		if c := <-found; c != nil {
   136  			return c
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  //查找与给定目标匹配的设备并为所有设备调用Matcher
   143  //每个设备的广告服务。找到第一个非零服务
   144  //被送出去。如果没有匹配的服务,则发送nil。
   145  func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
   146  	devs, err := goupnp.DiscoverDevices(target)
   147  	if err != nil {
   148  		out <- nil
   149  		return
   150  	}
   151  	found := false
   152  	for i := 0; i < len(devs) && !found; i++ {
   153  		if devs[i].Root == nil {
   154  			continue
   155  		}
   156  		devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
   157  			if found {
   158  				return
   159  			}
   160  //检查匹配的IGD服务
   161  			sc := goupnp.ServiceClient{
   162  				SOAPClient: service.NewSOAPClient(),
   163  				RootDevice: devs[i].Root,
   164  				Location:   devs[i].Location,
   165  				Service:    service,
   166  			}
   167  			sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout
   168  			upnp := matcher(devs[i].Root, sc)
   169  			if upnp == nil {
   170  				return
   171  			}
   172  //检查是否启用端口映射
   173  			if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
   174  				return
   175  			}
   176  			out <- upnp
   177  			found = true
   178  		})
   179  	}
   180  	if !found {
   181  		out <- nil
   182  	}
   183  }