github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/nat/natupnp_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package nat 19 20 import ( 21 "fmt" 22 "io" 23 "net" 24 "net/http" 25 "runtime" 26 "strings" 27 "testing" 28 29 "github.com/huin/goupnp/httpu" 30 ) 31 32 func TestUPNP_DDWRT(t *testing.T) { 33 if runtime.GOOS == "windows" { 34 t.Skipf("disabled to avoid firewall prompt") 35 } 36 37 dev := &fakeIGD{ 38 t: t, 39 ssdpResp: "HTTP/1.1 200 OK\r\n" + 40 "Cache-Control: max-age=300\r\n" + 41 "Date: Sun, 10 May 2015 10:05:33 GMT\r\n" + 42 "Ext: \r\n" + 43 "Location: http://{{listenAddr}}/InternetGatewayDevice.xml\r\n" + 44 "Server: POSIX UPnP/1.0 DD-WRT Linux/V24\r\n" + 45 "ST: urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" + 46 "USN: uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800::urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" + 47 "\r\n", 48 httpResps: map[string]string{ 49 "GET /InternetGatewayDevice.xml": ` 50 <?xml version="1.0"?> 51 <root xmlns="urn:schemas-upnp-org:device-1-0"> 52 <specVersion> 53 <major>1</major> 54 <minor>0</minor> 55 </specVersion> 56 <device> 57 <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType> 58 <manufacturer>DD-WRT</manufacturer> 59 <manufacturerURL>http://www.dd-wrt.com</manufacturerURL> 60 <modelDescription>Gateway</modelDescription> 61 <friendlyName>Asus RT-N16:DD-WRT</friendlyName> 62 <modelName>Asus RT-N16</modelName> 63 <modelNumber>V24</modelNumber> 64 <serialNumber>0000001</serialNumber> 65 <modelURL>http://www.dd-wrt.com</modelURL> 66 <UDN>uuid:A13AB4C3-3A14-E386-DE6A-EFEA923A06FE</UDN> 67 <serviceList> 68 <service> 69 <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType> 70 <serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId> 71 <SCPDURL>/x_layer3forwarding.xml</SCPDURL> 72 <controlURL>/control?Layer3Forwarding</controlURL> 73 <eventSubURL>/event?Layer3Forwarding</eventSubURL> 74 </service> 75 </serviceList> 76 <deviceList> 77 <device> 78 <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType> 79 <friendlyName>WANDevice</friendlyName> 80 <manufacturer>DD-WRT</manufacturer> 81 <manufacturerURL>http://www.dd-wrt.com</manufacturerURL> 82 <modelDescription>Gateway</modelDescription> 83 <modelName>router</modelName> 84 <modelURL>http://www.dd-wrt.com</modelURL> 85 <UDN>uuid:48FD569B-F9A9-96AE-4EE6-EB403D3DB91A</UDN> 86 <serviceList> 87 <service> 88 <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType> 89 <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId> 90 <SCPDURL>/x_wancommoninterfaceconfig.xml</SCPDURL> 91 <controlURL>/control?WANCommonInterfaceConfig</controlURL> 92 <eventSubURL>/event?WANCommonInterfaceConfig</eventSubURL> 93 </service> 94 </serviceList> 95 <deviceList> 96 <device> 97 <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType> 98 <friendlyName>WAN Connection Device</friendlyName> 99 <manufacturer>DD-WRT</manufacturer> 100 <manufacturerURL>http://www.dd-wrt.com</manufacturerURL> 101 <modelDescription>Gateway</modelDescription> 102 <modelName>router</modelName> 103 <modelURL>http://www.dd-wrt.com</modelURL> 104 <UDN>uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800</UDN> 105 <serviceList> 106 <service> 107 <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType> 108 <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId> 109 <SCPDURL>/x_wanipconnection.xml</SCPDURL> 110 <controlURL>/control?WANIPConnection</controlURL> 111 <eventSubURL>/event?WANIPConnection</eventSubURL> 112 </service> 113 </serviceList> 114 </device> 115 </deviceList> 116 </device> 117 <device> 118 <deviceType>urn:schemas-upnp-org:device:LANDevice:1</deviceType> 119 <friendlyName>LANDevice</friendlyName> 120 <manufacturer>DD-WRT</manufacturer> 121 <manufacturerURL>http://www.dd-wrt.com</manufacturerURL> 122 <modelDescription>Gateway</modelDescription> 123 <modelName>router</modelName> 124 <modelURL>http://www.dd-wrt.com</modelURL> 125 <UDN>uuid:04021998-3B35-2BDB-7B3C-99DA4435DA09</UDN> 126 <serviceList> 127 <service> 128 <serviceType>urn:schemas-upnp-org:service:LANHostConfigManagement:1</serviceType> 129 <serviceId>urn:upnp-org:serviceId:LANHostCfg1</serviceId> 130 <SCPDURL>/x_lanhostconfigmanagement.xml</SCPDURL> 131 <controlURL>/control?LANHostConfigManagement</controlURL> 132 <eventSubURL>/event?LANHostConfigManagement</eventSubURL> 133 </service> 134 </serviceList> 135 </device> 136 </deviceList> 137 <presentationURL>http://{{listenAddr}}</presentationURL> 138 </device> 139 </root> 140 `, 141 // The response to our GetNATRSIPStatus call. This 142 // particular implementation has a bug where the elements 143 // inside u:GetNATRSIPStatusResponse are not properly 144 // namespaced. 145 "POST /control?WANIPConnection": ` 146 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 147 <s:Body> 148 <u:GetNATRSIPStatusResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"> 149 <NewRSIPAvailable>0</NewRSIPAvailable> 150 <NewNATEnabled>1</NewNATEnabled> 151 </u:GetNATRSIPStatusResponse> 152 </s:Body> 153 </s:Envelope> 154 `, 155 }, 156 } 157 if err := dev.listen(); err != nil { 158 t.Skipf("cannot listen: %v", err) 159 } 160 dev.serve() 161 defer dev.close() 162 163 // Attempt to discover the fake device. 164 discovered := discoverUPnP() 165 if discovered == nil { 166 t.Fatalf("not discovered") 167 } 168 upnp, _ := discovered.(*upnp) 169 if upnp.service != "IGDv1-IP1" { 170 t.Errorf("upnp.service mismatch: got %q, want %q", upnp.service, "IGDv1-IP1") 171 } 172 wantURL := "http://" + dev.listener.Addr().String() + "/InternetGatewayDevice.xml" 173 if upnp.dev.URLBaseStr != wantURL { 174 t.Errorf("upnp.dev.URLBaseStr mismatch: got %q, want %q", upnp.dev.URLBaseStr, wantURL) 175 } 176 } 177 178 // fakeIGD presents itself as a discoverable UPnP device which sends 179 // canned responses to HTTPU and HTTP requests. 180 type fakeIGD struct { 181 t *testing.T // for logging 182 183 listener net.Listener 184 mcastListener *net.UDPConn 185 186 // This should be a complete HTTP response (including headers). 187 // It is sent as the response to any sspd packet. Any occurrence 188 // of "{{listenAddr}}" is replaced with the actual TCP listen 189 // address of the HTTP server. 190 ssdpResp string 191 // This one should contain XML payloads for all requests 192 // performed. The keys contain method and path, e.g. "GET /foo/bar". 193 // As with ssdpResp, "{{listenAddr}}" is replaced with the TCP 194 // listen address. 195 httpResps map[string]string 196 } 197 198 // httpu.Handler 199 func (dev *fakeIGD) ServeMessage(r *http.Request) { 200 dev.t.Logf(`HTTPU request %s %s`, r.Method, r.RequestURI) 201 conn, err := net.Dial("udp4", r.RemoteAddr) 202 if err != nil { 203 fmt.Printf("reply Dial error: %v", err) 204 return 205 } 206 defer conn.Close() 207 io.WriteString(conn, dev.replaceListenAddr(dev.ssdpResp)) 208 } 209 210 // http.Handler 211 func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) { 212 if resp, ok := dev.httpResps[r.Method+" "+r.RequestURI]; ok { 213 dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 200) 214 io.WriteString(w, dev.replaceListenAddr(resp)) 215 } else { 216 dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 404) 217 w.WriteHeader(http.StatusNotFound) 218 } 219 } 220 221 func (dev *fakeIGD) replaceListenAddr(resp string) string { 222 return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1) 223 } 224 225 func (dev *fakeIGD) listen() (err error) { 226 if dev.listener, err = net.Listen("tcp", "127.0.0.1:0"); err != nil { 227 return err 228 } 229 laddr := &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 1900} 230 if dev.mcastListener, err = net.ListenMulticastUDP("udp", nil, laddr); err != nil { 231 dev.listener.Close() 232 return err 233 } 234 return nil 235 } 236 237 func (dev *fakeIGD) serve() { 238 go httpu.Serve(dev.mcastListener, dev) 239 go http.Serve(dev.listener, dev) 240 } 241 242 func (dev *fakeIGD) close() { 243 dev.mcastListener.Close() 244 dev.listener.Close() 245 }