natupnp_test.go 8.8 KB

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