github.com/annchain/OG@v0.0.9/p2p/nat/natupnp.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package nat 18 19 import ( 20 "errors" 21 "fmt" 22 "github.com/annchain/OG/arefactor/common/goroutine" 23 "net" 24 "strings" 25 "time" 26 27 "github.com/huin/goupnp" 28 "github.com/huin/goupnp/dcps/internetgateway1" 29 "github.com/huin/goupnp/dcps/internetgateway2" 30 ) 31 32 const soapRequestTimeout = 3 * time.Second 33 34 type upnp struct { 35 dev *goupnp.RootDevice 36 service string 37 client upnpClient 38 } 39 40 type upnpClient interface { 41 GetExternalIPAddress() (string, error) 42 AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error 43 DeletePortMapping(string, uint16, string) error 44 GetNATRSIPStatus() (sip bool, nat bool, err error) 45 } 46 47 func (n *upnp) ExternalIP() (addr net.IP, err error) { 48 ipString, err := n.client.GetExternalIPAddress() 49 if err != nil { 50 return nil, err 51 } 52 ip := net.ParseIP(ipString) 53 if ip == nil { 54 return nil, errors.New("bad IP in response") 55 } 56 return ip, nil 57 } 58 59 func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error { 60 ip, err := n.internalAddress() 61 if err != nil { 62 return nil 63 } 64 protocol = strings.ToUpper(protocol) 65 lifetimeS := uint32(lifetime / time.Second) 66 n.DeleteMapping(protocol, extport, intport) 67 return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) 68 } 69 70 func (n *upnp) internalAddress() (net.IP, error) { 71 devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host) 72 if err != nil { 73 return nil, err 74 } 75 ifaces, err := net.Interfaces() 76 if err != nil { 77 return nil, err 78 } 79 for _, iface := range ifaces { 80 addrs, err := iface.Addrs() 81 if err != nil { 82 return nil, err 83 } 84 for _, addr := range addrs { 85 if x, ok := addr.(*net.IPNet); ok && x.Contains(devaddr.IP) { 86 return x.IP, nil 87 } 88 } 89 } 90 return nil, fmt.Errorf("could not find local address in same net as %v", devaddr) 91 } 92 93 func (n *upnp) DeleteMapping(protocol string, extport, intport int) error { 94 return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol)) 95 } 96 97 func (n *upnp) String() string { 98 return "UPNP " + n.service 99 } 100 101 // discoverUPnP searches for Internet Gateway Devices 102 // and returns the first one it can find on the local network. 103 func discoverUPnP() Interface { 104 found := make(chan *upnp, 2) 105 // IGDv1 106 funcV1 := func() { 107 discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 108 switch sc.Service.ServiceType { 109 case internetgateway1.URN_WANIPConnection_1: 110 return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}} 111 case internetgateway1.URN_WANPPPConnection_1: 112 return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}} 113 } 114 return nil 115 }) 116 } 117 goroutine.New(funcV1) 118 // IGDv2 119 funcV2 := func() { 120 discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 121 switch sc.Service.ServiceType { 122 case internetgateway2.URN_WANIPConnection_1: 123 return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}} 124 case internetgateway2.URN_WANIPConnection_2: 125 return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}} 126 case internetgateway2.URN_WANPPPConnection_1: 127 return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}} 128 } 129 return nil 130 }) 131 } 132 goroutine.New(funcV2) 133 for i := 0; i < cap(found); i++ { 134 if c := <-found; c != nil { 135 return c 136 } 137 } 138 return nil 139 } 140 141 // finds devices matching the given target and calls matcher for all 142 // advertised services of each device. The first non-nil service found 143 // is sent into out. If no service matched, nil is sent. 144 func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) { 145 devs, err := goupnp.DiscoverDevices(target) 146 if err != nil { 147 out <- nil 148 return 149 } 150 found := false 151 for i := 0; i < len(devs) && !found; i++ { 152 if devs[i].Root == nil { 153 continue 154 } 155 devs[i].Root.Device.VisitServices(func(service *goupnp.Service) { 156 if found { 157 return 158 } 159 // check for a matching IGD service 160 sc := goupnp.ServiceClient{ 161 SOAPClient: service.NewSOAPClient(), 162 RootDevice: devs[i].Root, 163 Location: devs[i].Location, 164 Service: service, 165 } 166 sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout 167 upnp := matcher(devs[i].Root, sc) 168 if upnp == nil { 169 return 170 } 171 // check whether port mapping is enabled 172 if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat { 173 return 174 } 175 out <- upnp 176 found = true 177 }) 178 } 179 if !found { 180 out <- nil 181 } 182 }