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