OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / btcsuite / btcd / upnp.go
1 package main
2
3 // Upnp code taken from Taipei Torrent license is below:
4 // Copyright (c) 2010 Jack Palevich. All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //    * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //    * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //    * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 // Just enough UPnP to be able to forward ports
33 //
34
35 import (
36         "bytes"
37         "encoding/xml"
38         "errors"
39         "net"
40         "net/http"
41         "os"
42         "strconv"
43         "strings"
44         "time"
45 )
46
47 // NAT is an interface representing a NAT traversal options for example UPNP or
48 // NAT-PMP. It provides methods to query and manipulate this traversal to allow
49 // access to services.
50 type NAT interface {
51         // Get the external address from outside the NAT.
52         GetExternalAddress() (addr net.IP, err error)
53         // Add a port mapping for protocol ("udp" or "tcp") from external port to
54         // internal port with description lasting for timeout.
55         AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
56         // Remove a previously added port mapping from external port to
57         // internal port.
58         DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
59 }
60
61 type upnpNAT struct {
62         serviceURL string
63         ourIP      string
64 }
65
66 // Discover searches the local network for a UPnP router returning a NAT
67 // for the network if so, nil if not.
68 func Discover() (nat NAT, err error) {
69         ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
70         if err != nil {
71                 return
72         }
73         conn, err := net.ListenPacket("udp4", ":0")
74         if err != nil {
75                 return
76         }
77         socket := conn.(*net.UDPConn)
78         defer socket.Close()
79
80         err = socket.SetDeadline(time.Now().Add(3 * time.Second))
81         if err != nil {
82                 return
83         }
84
85         st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
86         buf := bytes.NewBufferString(
87                 "M-SEARCH * HTTP/1.1\r\n" +
88                         "HOST: 239.255.255.250:1900\r\n" +
89                         st +
90                         "MAN: \"ssdp:discover\"\r\n" +
91                         "MX: 2\r\n\r\n")
92         message := buf.Bytes()
93         answerBytes := make([]byte, 1024)
94         for i := 0; i < 3; i++ {
95                 _, err = socket.WriteToUDP(message, ssdp)
96                 if err != nil {
97                         return
98                 }
99                 var n int
100                 n, _, err = socket.ReadFromUDP(answerBytes)
101                 if err != nil {
102                         continue
103                         // socket.Close()
104                         // return
105                 }
106                 answer := string(answerBytes[0:n])
107                 if !strings.Contains(answer, "\r\n"+st) {
108                         continue
109                 }
110                 // HTTP header field names are case-insensitive.
111                 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
112                 locString := "\r\nlocation: "
113                 locIndex := strings.Index(strings.ToLower(answer), locString)
114                 if locIndex < 0 {
115                         continue
116                 }
117                 loc := answer[locIndex+len(locString):]
118                 endIndex := strings.Index(loc, "\r\n")
119                 if endIndex < 0 {
120                         continue
121                 }
122                 locURL := loc[0:endIndex]
123                 var serviceURL string
124                 serviceURL, err = getServiceURL(locURL)
125                 if err != nil {
126                         return
127                 }
128                 var ourIP string
129                 ourIP, err = getOurIP()
130                 if err != nil {
131                         return
132                 }
133                 nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
134                 return
135         }
136         err = errors.New("UPnP port discovery failed")
137         return
138 }
139
140 // service represents the Service type in an UPnP xml description.
141 // Only the parts we care about are present and thus the xml may have more
142 // fields than present in the structure.
143 type service struct {
144         ServiceType string `xml:"serviceType"`
145         ControlURL  string `xml:"controlURL"`
146 }
147
148 // deviceList represents the deviceList type in an UPnP xml description.
149 // Only the parts we care about are present and thus the xml may have more
150 // fields than present in the structure.
151 type deviceList struct {
152         XMLName xml.Name `xml:"deviceList"`
153         Device  []device `xml:"device"`
154 }
155
156 // serviceList represents the serviceList type in an UPnP xml description.
157 // Only the parts we care about are present and thus the xml may have more
158 // fields than present in the structure.
159 type serviceList struct {
160         XMLName xml.Name  `xml:"serviceList"`
161         Service []service `xml:"service"`
162 }
163
164 // device represents the device type in an UPnP xml description.
165 // Only the parts we care about are present and thus the xml may have more
166 // fields than present in the structure.
167 type device struct {
168         XMLName     xml.Name    `xml:"device"`
169         DeviceType  string      `xml:"deviceType"`
170         DeviceList  deviceList  `xml:"deviceList"`
171         ServiceList serviceList `xml:"serviceList"`
172 }
173
174 // specVersion represents the specVersion in a UPnP xml description.
175 // Only the parts we care about are present and thus the xml may have more
176 // fields than present in the structure.
177 type specVersion struct {
178         XMLName xml.Name `xml:"specVersion"`
179         Major   int      `xml:"major"`
180         Minor   int      `xml:"minor"`
181 }
182
183 // root represents the Root document for a UPnP xml description.
184 // Only the parts we care about are present and thus the xml may have more
185 // fields than present in the structure.
186 type root struct {
187         XMLName     xml.Name `xml:"root"`
188         SpecVersion specVersion
189         Device      device
190 }
191
192 // getChildDevice searches the children of device for a device with the given
193 // type.
194 func getChildDevice(d *device, deviceType string) *device {
195         for i := range d.DeviceList.Device {
196                 if d.DeviceList.Device[i].DeviceType == deviceType {
197                         return &d.DeviceList.Device[i]
198                 }
199         }
200         return nil
201 }
202
203 // getChildDevice searches the service list of device for a service with the
204 // given type.
205 func getChildService(d *device, serviceType string) *service {
206         for i := range d.ServiceList.Service {
207                 if d.ServiceList.Service[i].ServiceType == serviceType {
208                         return &d.ServiceList.Service[i]
209                 }
210         }
211         return nil
212 }
213
214 // getOurIP returns a best guess at what the local IP is.
215 func getOurIP() (ip string, err error) {
216         hostname, err := os.Hostname()
217         if err != nil {
218                 return
219         }
220         return net.LookupCNAME(hostname)
221 }
222
223 // getServiceURL parses the xml description at the given root url to find the
224 // url for the WANIPConnection service to be used for port forwarding.
225 func getServiceURL(rootURL string) (url string, err error) {
226         r, err := http.Get(rootURL)
227         if err != nil {
228                 return
229         }
230         defer r.Body.Close()
231         if r.StatusCode >= 400 {
232                 err = errors.New(string(r.StatusCode))
233                 return
234         }
235         var root root
236         err = xml.NewDecoder(r.Body).Decode(&root)
237         if err != nil {
238                 return
239         }
240         a := &root.Device
241         if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
242                 err = errors.New("no InternetGatewayDevice")
243                 return
244         }
245         b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
246         if b == nil {
247                 err = errors.New("no WANDevice")
248                 return
249         }
250         c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
251         if c == nil {
252                 err = errors.New("no WANConnectionDevice")
253                 return
254         }
255         d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
256         if d == nil {
257                 err = errors.New("no WANIPConnection")
258                 return
259         }
260         url = combineURL(rootURL, d.ControlURL)
261         return
262 }
263
264 // combineURL appends subURL onto rootURL.
265 func combineURL(rootURL, subURL string) string {
266         protocolEnd := "://"
267         protoEndIndex := strings.Index(rootURL, protocolEnd)
268         a := rootURL[protoEndIndex+len(protocolEnd):]
269         rootIndex := strings.Index(a, "/")
270         return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
271 }
272
273 // soapBody represents the <s:Body> element in a SOAP reply.
274 // fields we don't care about are elided.
275 type soapBody struct {
276         XMLName xml.Name `xml:"Body"`
277         Data    []byte   `xml:",innerxml"`
278 }
279
280 // soapEnvelope represents the <s:Envelope> element in a SOAP reply.
281 // fields we don't care about are elided.
282 type soapEnvelope struct {
283         XMLName xml.Name `xml:"Envelope"`
284         Body    soapBody `xml:"Body"`
285 }
286
287 // soapRequests performs a soap request with the given parameters and returns
288 // the xml replied stripped of the soap headers. in the case that the request is
289 // unsuccessful the an error is returned.
290 func soapRequest(url, function, message string) (replyXML []byte, err error) {
291         fullMessage := "<?xml version=\"1.0\" ?>" +
292                 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
293                 "<s:Body>" + message + "</s:Body></s:Envelope>"
294
295         req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
296         if err != nil {
297                 return nil, err
298         }
299         req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
300         req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
301         //req.Header.Set("Transfer-Encoding", "chunked")
302         req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
303         req.Header.Set("Connection", "Close")
304         req.Header.Set("Cache-Control", "no-cache")
305         req.Header.Set("Pragma", "no-cache")
306
307         r, err := http.DefaultClient.Do(req)
308         if err != nil {
309                 return nil, err
310         }
311         if r.Body != nil {
312                 defer r.Body.Close()
313         }
314
315         if r.StatusCode >= 400 {
316                 // log.Stderr(function, r.StatusCode)
317                 err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
318                 r = nil
319                 return
320         }
321         var reply soapEnvelope
322         err = xml.NewDecoder(r.Body).Decode(&reply)
323         if err != nil {
324                 return nil, err
325         }
326         return reply.Body.Data, nil
327 }
328
329 // getExternalIPAddressResponse represents the XML response to a
330 // GetExternalIPAddress SOAP request.
331 type getExternalIPAddressResponse struct {
332         XMLName           xml.Name `xml:"GetExternalIPAddressResponse"`
333         ExternalIPAddress string   `xml:"NewExternalIPAddress"`
334 }
335
336 // GetExternalAddress implements the NAT interface by fetching the external IP
337 // from the UPnP router.
338 func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
339         message := "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>\r\n"
340         response, err := soapRequest(n.serviceURL, "GetExternalIPAddress", message)
341         if err != nil {
342                 return nil, err
343         }
344
345         var reply getExternalIPAddressResponse
346         err = xml.Unmarshal(response, &reply)
347         if err != nil {
348                 return nil, err
349         }
350
351         addr = net.ParseIP(reply.ExternalIPAddress)
352         if addr == nil {
353                 return nil, errors.New("unable to parse ip address")
354         }
355         return addr, nil
356 }
357
358 // AddPortMapping implements the NAT interface by setting up a port forwarding
359 // from the UPnP router to the local machine with the given ports and protocol.
360 func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
361         // A single concatenation would break ARM compilation.
362         message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
363                 "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
364         message += "</NewExternalPort><NewProtocol>" + strings.ToUpper(protocol) + "</NewProtocol>"
365         message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
366                 "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
367                 "<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
368         message += description +
369                 "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
370                 "</NewLeaseDuration></u:AddPortMapping>"
371
372         response, err := soapRequest(n.serviceURL, "AddPortMapping", message)
373         if err != nil {
374                 return
375         }
376
377         // TODO: check response to see if the port was forwarded
378         // If the port was not wildcard we don't get an reply with the port in
379         // it. Not sure about wildcard yet. miniupnpc just checks for error
380         // codes here.
381         mappedExternalPort = externalPort
382         _ = response
383         return
384 }
385
386 // DeletePortMapping implements the NAT interface by removing up a port forwarding
387 // from the UPnP router to the local machine with the given ports and.
388 func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
389
390         message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
391                 "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
392                 "</NewExternalPort><NewProtocol>" + strings.ToUpper(protocol) + "</NewProtocol>" +
393                 "</u:DeletePortMapping>"
394
395         response, err := soapRequest(n.serviceURL, "DeletePortMapping", message)
396         if err != nil {
397                 return
398         }
399
400         // TODO: check response to see if the port was deleted
401         // log.Println(message, response)
402         _ = response
403         return
404 }