github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/p2p/nat/natupnp.go (about) 1 package nat 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "strings" 8 "time" 9 10 "github.com/huin/goupnp" 11 "github.com/huin/goupnp/dcps/internetgateway1" 12 "github.com/huin/goupnp/dcps/internetgateway2" 13 ) 14 15 const soapRequestTimeout = 3 * time.Second 16 17 type upnp struct { 18 dev *goupnp.RootDevice 19 service string 20 client upnpClient 21 } 22 23 type upnpClient interface { 24 GetExternalIPAddress() (string, error) 25 AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error 26 DeletePortMapping(string, uint16, string) error 27 GetNATRSIPStatus() (sip bool, nat bool, err error) 28 } 29 30 func (n *upnp) ExternalIP() (addr net.IP, err error) { 31 ipString, err := n.client.GetExternalIPAddress() 32 if err != nil { 33 return nil, err 34 } 35 ip := net.ParseIP(ipString) 36 if ip == nil { 37 return nil, errors.New("bad IP in response") 38 } 39 return ip, nil 40 } 41 42 func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error { 43 ip, err := n.internalAddress() 44 if err != nil { 45 return nil 46 } 47 protocol = strings.ToUpper(protocol) 48 lifetimeS := uint32(lifetime / time.Second) 49 n.DeleteMapping(protocol, extport, intport) 50 return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) 51 } 52 53 func (n *upnp) internalAddress() (net.IP, error) { 54 devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host) 55 if err != nil { 56 return nil, err 57 } 58 ifaces, err := net.Interfaces() 59 if err != nil { 60 return nil, err 61 } 62 for _, iface := range ifaces { 63 addrs, err := iface.Addrs() 64 if err != nil { 65 return nil, err 66 } 67 for _, addr := range addrs { 68 switch x := addr.(type) { 69 case *net.IPNet: 70 if x.Contains(devaddr.IP) { 71 return x.IP, nil 72 } 73 } 74 } 75 } 76 return nil, fmt.Errorf("could not find local address in same net as %v", devaddr) 77 } 78 79 func (n *upnp) DeleteMapping(protocol string, extport, intport int) error { 80 return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol)) 81 } 82 83 func (n *upnp) String() string { 84 return "UPNP " + n.service 85 } 86 87 // discoverUPnP searches for Internet Gateway Devices 88 // and returns the first one it can find on the local network. 89 func discoverUPnP() Interface { 90 found := make(chan *upnp, 2) 91 // IGDv1 92 go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 93 switch sc.Service.ServiceType { 94 case internetgateway1.URN_WANIPConnection_1: 95 return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}} 96 case internetgateway1.URN_WANPPPConnection_1: 97 return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}} 98 } 99 return nil 100 }) 101 // IGDv2 102 go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { 103 switch sc.Service.ServiceType { 104 case internetgateway2.URN_WANIPConnection_1: 105 return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}} 106 case internetgateway2.URN_WANIPConnection_2: 107 return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}} 108 case internetgateway2.URN_WANPPPConnection_1: 109 return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}} 110 } 111 return nil 112 }) 113 for i := 0; i < cap(found); i++ { 114 if c := <-found; c != nil { 115 return c 116 } 117 } 118 return nil 119 } 120 121 // finds devices matching the given target and calls matcher for all 122 // advertised services of each device. The first non-nil service found 123 // is sent into out. If no service matched, nil is sent. 124 func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) { 125 devs, err := goupnp.DiscoverDevices(target) 126 if err != nil { 127 out <- nil 128 return 129 } 130 found := false 131 for i := 0; i < len(devs) && !found; i++ { 132 if devs[i].Root == nil { 133 continue 134 } 135 devs[i].Root.Device.VisitServices(func(service *goupnp.Service) { 136 if found { 137 return 138 } 139 // check for a matching IGD service 140 sc := goupnp.ServiceClient{ 141 SOAPClient: service.NewSOAPClient(), 142 RootDevice: devs[i].Root, 143 Location: devs[i].Location, 144 Service: service, 145 } 146 sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout 147 upnp := matcher(devs[i].Root, sc) 148 if upnp == nil { 149 return 150 } 151 // check whether port mapping is enabled 152 if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat { 153 return 154 } 155 out <- upnp 156 found = true 157 }) 158 } 159 if !found { 160 out <- nil 161 } 162 }