github.com/energicryptocurrency/go-energi@v1.1.7/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 "net" 23 "strings" 24 "time" 25 26 "github.com/huin/goupnp" 27 "github.com/huin/goupnp/dcps/internetgateway1" 28 "github.com/huin/goupnp/dcps/internetgateway2" 29 ) 30 31 const soapRequestTimeout = 3 * time.Second 32 33 type upnp struct { 34 dev *goupnp.RootDevice 35 service string 36 client upnpClient 37 } 38 39 type upnpClient interface { 40 GetExternalIPAddress() (string, error) 41 AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error 42 DeletePortMapping(string, uint16, string) error 43 GetNATRSIPStatus() (sip bool, nat bool, err error) 44 } 45 46 func (n *upnp) ExternalIP() (addr net.IP, err error) { 47 ipString, err := n.client.GetExternalIPAddress() 48 if err != nil { 49 return nil, err 50 } 51 ip := net.ParseIP(ipString) 52 if ip == nil { 53 return nil, errors.New("bad IP in response") 54 } 55 return ip, nil 56 } 57 58 func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, 59 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), 68 ip.String(), true, desc, lifetimeS) 69 } 70 71 func (n *upnp) internalAddress() (net.IP, error) { 72 devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host) 73 if err != nil { 74 return nil, err 75 } 76 ifaces, err := net.Interfaces() 77 if err != nil { 78 return nil, err 79 } 80 for _, iface := range ifaces { 81 addrs, err := iface.Addrs() 82 if err != nil { 83 return nil, err 84 } 85 for _, addr := range addrs { 86 if x, ok := addr.(*net.IPNet); ok && x.Contains(devaddr.IP) { 87 return x.IP, nil 88 } 89 } 90 } 91 return nil, fmt.Errorf("could not find local address in same net as %v", devaddr) 92 } 93 94 func (n *upnp) DeleteMapping(protocol string, extport, intport int) error { 95 return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol)) 96 } 97 98 func (n *upnp) String() string { 99 return "UPNP " + n.service 100 } 101 102 // discoverUPnP searches for Internet Gateway Devices 103 // and returns the first one it can find on the local network. 104 func discoverUPnP() Interface { 105 found := make(chan *upnp, 2) 106 // IGDv1 107 go 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 // IGDv2 117 go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 118 switch sc.Service.ServiceType { 119 case internetgateway2.URN_WANIPConnection_1: 120 return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}} 121 case internetgateway2.URN_WANIPConnection_2: 122 return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}} 123 case internetgateway2.URN_WANPPPConnection_1: 124 return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}} 125 } 126 return nil 127 }) 128 for i := 0; i < cap(found); i++ { 129 if c := <-found; c != nil { 130 return c 131 } 132 } 133 return nil 134 } 135 136 // getAllUPnP searches for Internet Gateway Devices 137 // and returns a slice of them. 138 func getAllUPnP() (result []*upnp) { 139 found := make(chan *upnp, 2) 140 // IGDv1 141 go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 142 switch sc.Service.ServiceType { 143 case internetgateway1.URN_WANIPConnection_1: 144 return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}} 145 case internetgateway1.URN_WANPPPConnection_1: 146 return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}} 147 } 148 return nil 149 }) 150 // IGDv2 151 go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 152 switch sc.Service.ServiceType { 153 case internetgateway2.URN_WANIPConnection_1: 154 return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}} 155 case internetgateway2.URN_WANIPConnection_2: 156 return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}} 157 case internetgateway2.URN_WANPPPConnection_1: 158 return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}} 159 } 160 return nil 161 }) 162 for i := 0; i < cap(found); i++ { 163 if c := <-found; c != nil { 164 result = append(result, c) 165 } 166 } 167 return result 168 } 169 170 // finds devices matching the given target and calls matcher for all 171 // advertised services of each device. The first non-nil service found 172 // is sent into out. If no service matched, nil is sent. 173 func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) { 174 devs, err := goupnp.DiscoverDevices(target) 175 if err != nil { 176 out <- nil 177 return 178 } 179 found := false 180 for i := 0; i < len(devs) && !found; i++ { 181 if devs[i].Root == nil { 182 continue 183 } 184 devs[i].Root.Device.VisitServices(func(service *goupnp.Service) { 185 if found { 186 return 187 } 188 // check for a matching IGD service 189 sc := goupnp.ServiceClient{ 190 SOAPClient: service.NewSOAPClient(), 191 RootDevice: devs[i].Root, 192 Location: devs[i].Location, 193 Service: service, 194 } 195 sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout 196 upnp := matcher(devs[i].Root, sc) 197 if upnp == nil { 198 return 199 } 200 // check whether port mapping is enabled 201 if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat { 202 return 203 } 204 out <- upnp 205 found = true 206 }) 207 } 208 if !found { 209 out <- nil 210 } 211 }