You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
9.8 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. // Taken from taipei-torrent.
  2. // Just enough UPnP to be able to forward ports
  3. // For more information, see: http://www.upnp-hacks.org/upnp.html
  4. package upnp
  5. // TODO: use syscalls to get actual ourIP, see issue #712
  6. import (
  7. "bytes"
  8. "encoding/xml"
  9. "errors"
  10. "io/ioutil"
  11. "net"
  12. "net/http"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. type upnpNAT struct {
  18. serviceURL string
  19. ourIP string
  20. urnDomain string
  21. }
  22. // protocol is either "udp" or "tcp"
  23. type NAT interface {
  24. GetExternalAddress() (addr net.IP, err error)
  25. AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
  26. DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
  27. }
  28. func Discover() (nat NAT, err error) {
  29. ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
  30. if err != nil {
  31. return
  32. }
  33. conn, err := net.ListenPacket("udp4", ":0")
  34. if err != nil {
  35. return
  36. }
  37. socket := conn.(*net.UDPConn)
  38. defer socket.Close() // nolint: errcheck
  39. if err := socket.SetDeadline(time.Now().Add(3 * time.Second)); err != nil {
  40. return nil, err
  41. }
  42. st := "InternetGatewayDevice:1"
  43. buf := bytes.NewBufferString(
  44. "M-SEARCH * HTTP/1.1\r\n" +
  45. "HOST: 239.255.255.250:1900\r\n" +
  46. "ST: ssdp:all\r\n" +
  47. "MAN: \"ssdp:discover\"\r\n" +
  48. "MX: 2\r\n\r\n")
  49. message := buf.Bytes()
  50. answerBytes := make([]byte, 1024)
  51. for i := 0; i < 3; i++ {
  52. _, err = socket.WriteToUDP(message, ssdp)
  53. if err != nil {
  54. return
  55. }
  56. var n int
  57. _, _, err = socket.ReadFromUDP(answerBytes)
  58. if err != nil {
  59. return
  60. }
  61. for {
  62. n, _, err = socket.ReadFromUDP(answerBytes)
  63. if err != nil {
  64. break
  65. }
  66. answer := string(answerBytes[0:n])
  67. if !strings.Contains(answer, st) {
  68. continue
  69. }
  70. // HTTP header field names are case-insensitive.
  71. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
  72. locString := "\r\nlocation:"
  73. answer = strings.ToLower(answer)
  74. locIndex := strings.Index(answer, locString)
  75. if locIndex < 0 {
  76. continue
  77. }
  78. loc := answer[locIndex+len(locString):]
  79. endIndex := strings.Index(loc, "\r\n")
  80. if endIndex < 0 {
  81. continue
  82. }
  83. locURL := strings.TrimSpace(loc[0:endIndex])
  84. var serviceURL, urnDomain string
  85. serviceURL, urnDomain, err = getServiceURL(locURL)
  86. if err != nil {
  87. return
  88. }
  89. var ourIP net.IP
  90. ourIP, err = localIPv4()
  91. if err != nil {
  92. return
  93. }
  94. nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
  95. return
  96. }
  97. }
  98. err = errors.New("UPnP port discovery failed.")
  99. return
  100. }
  101. type Envelope struct {
  102. XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
  103. Soap *SoapBody
  104. }
  105. type SoapBody struct {
  106. XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
  107. ExternalIP *ExternalIPAddressResponse
  108. }
  109. type ExternalIPAddressResponse struct {
  110. XMLName xml.Name `xml:"GetExternalIPAddressResponse"`
  111. IPAddress string `xml:"NewExternalIPAddress"`
  112. }
  113. type ExternalIPAddress struct {
  114. XMLName xml.Name `xml:"NewExternalIPAddress"`
  115. IP string
  116. }
  117. type UPNPService struct {
  118. ServiceType string `xml:"serviceType"`
  119. ControlURL string `xml:"controlURL"`
  120. }
  121. type DeviceList struct {
  122. Device []Device `xml:"device"`
  123. }
  124. type ServiceList struct {
  125. Service []UPNPService `xml:"service"`
  126. }
  127. type Device struct {
  128. XMLName xml.Name `xml:"device"`
  129. DeviceType string `xml:"deviceType"`
  130. DeviceList DeviceList `xml:"deviceList"`
  131. ServiceList ServiceList `xml:"serviceList"`
  132. }
  133. type Root struct {
  134. Device Device
  135. }
  136. func getChildDevice(d *Device, deviceType string) *Device {
  137. dl := d.DeviceList.Device
  138. for i := 0; i < len(dl); i++ {
  139. if strings.Contains(dl[i].DeviceType, deviceType) {
  140. return &dl[i]
  141. }
  142. }
  143. return nil
  144. }
  145. func getChildService(d *Device, serviceType string) *UPNPService {
  146. sl := d.ServiceList.Service
  147. for i := 0; i < len(sl); i++ {
  148. if strings.Contains(sl[i].ServiceType, serviceType) {
  149. return &sl[i]
  150. }
  151. }
  152. return nil
  153. }
  154. func localIPv4() (net.IP, error) {
  155. tt, err := net.Interfaces()
  156. if err != nil {
  157. return nil, err
  158. }
  159. for _, t := range tt {
  160. aa, err := t.Addrs()
  161. if err != nil {
  162. return nil, err
  163. }
  164. for _, a := range aa {
  165. ipnet, ok := a.(*net.IPNet)
  166. if !ok {
  167. continue
  168. }
  169. v4 := ipnet.IP.To4()
  170. if v4 == nil || v4[0] == 127 { // loopback address
  171. continue
  172. }
  173. return v4, nil
  174. }
  175. }
  176. return nil, errors.New("cannot find local IP address")
  177. }
  178. func getServiceURL(rootURL string) (url, urnDomain string, err error) {
  179. r, err := http.Get(rootURL)
  180. if err != nil {
  181. return
  182. }
  183. defer r.Body.Close() // nolint: errcheck
  184. if r.StatusCode >= 400 {
  185. err = errors.New(string(r.StatusCode))
  186. return
  187. }
  188. var root Root
  189. err = xml.NewDecoder(r.Body).Decode(&root)
  190. if err != nil {
  191. return
  192. }
  193. a := &root.Device
  194. if !strings.Contains(a.DeviceType, "InternetGatewayDevice:1") {
  195. err = errors.New("No InternetGatewayDevice")
  196. return
  197. }
  198. b := getChildDevice(a, "WANDevice:1")
  199. if b == nil {
  200. err = errors.New("No WANDevice")
  201. return
  202. }
  203. c := getChildDevice(b, "WANConnectionDevice:1")
  204. if c == nil {
  205. err = errors.New("No WANConnectionDevice")
  206. return
  207. }
  208. d := getChildService(c, "WANIPConnection:1")
  209. if d == nil {
  210. // Some routers don't follow the UPnP spec, and put WanIPConnection under WanDevice,
  211. // instead of under WanConnectionDevice
  212. d = getChildService(b, "WANIPConnection:1")
  213. if d == nil {
  214. err = errors.New("No WANIPConnection")
  215. return
  216. }
  217. }
  218. // Extract the domain name, which isn't always 'schemas-upnp-org'
  219. urnDomain = strings.Split(d.ServiceType, ":")[1]
  220. url = combineURL(rootURL, d.ControlURL)
  221. return
  222. }
  223. func combineURL(rootURL, subURL string) string {
  224. protocolEnd := "://"
  225. protoEndIndex := strings.Index(rootURL, protocolEnd)
  226. a := rootURL[protoEndIndex+len(protocolEnd):]
  227. rootIndex := strings.Index(a, "/")
  228. return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
  229. }
  230. func soapRequest(url, function, message, domain string) (r *http.Response, err error) {
  231. fullMessage := "<?xml version=\"1.0\" ?>" +
  232. "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
  233. "<s:Body>" + message + "</s:Body></s:Envelope>"
  234. req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
  235. if err != nil {
  236. return nil, err
  237. }
  238. req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
  239. req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
  240. //req.Header.Set("Transfer-Encoding", "chunked")
  241. req.Header.Set("SOAPAction", "\"urn:"+domain+":service:WANIPConnection:1#"+function+"\"")
  242. req.Header.Set("Connection", "Close")
  243. req.Header.Set("Cache-Control", "no-cache")
  244. req.Header.Set("Pragma", "no-cache")
  245. // log.Stderr("soapRequest ", req)
  246. r, err = http.DefaultClient.Do(req)
  247. if err != nil {
  248. return nil, err
  249. }
  250. /*if r.Body != nil {
  251. defer r.Body.Close()
  252. }*/
  253. if r.StatusCode >= 400 {
  254. // log.Stderr(function, r.StatusCode)
  255. err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
  256. r = nil
  257. return
  258. }
  259. return
  260. }
  261. type statusInfo struct {
  262. externalIpAddress string
  263. }
  264. func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
  265. message := "<u:GetExternalIPAddress xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
  266. "</u:GetExternalIPAddress>"
  267. var response *http.Response
  268. response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
  269. if response != nil {
  270. defer response.Body.Close() // nolint: errcheck
  271. }
  272. if err != nil {
  273. return
  274. }
  275. var envelope Envelope
  276. data, err := ioutil.ReadAll(response.Body)
  277. if err != nil {
  278. return
  279. }
  280. reader := bytes.NewReader(data)
  281. err = xml.NewDecoder(reader).Decode(&envelope)
  282. if err != nil {
  283. return
  284. }
  285. info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
  286. if err != nil {
  287. return
  288. }
  289. return
  290. }
  291. func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
  292. info, err := n.getExternalIPAddress()
  293. if err != nil {
  294. return
  295. }
  296. addr = net.ParseIP(info.externalIpAddress)
  297. return
  298. }
  299. func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
  300. // A single concatenation would break ARM compilation.
  301. message := "<u:AddPortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
  302. "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
  303. message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
  304. message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
  305. "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
  306. "<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
  307. message += description +
  308. "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
  309. "</NewLeaseDuration></u:AddPortMapping>"
  310. var response *http.Response
  311. response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
  312. if response != nil {
  313. defer response.Body.Close() // nolint: errcheck
  314. }
  315. if err != nil {
  316. return
  317. }
  318. // TODO: check response to see if the port was forwarded
  319. // log.Println(message, response)
  320. // JAE:
  321. // body, err := ioutil.ReadAll(response.Body)
  322. // fmt.Println(string(body), err)
  323. mappedExternalPort = externalPort
  324. _ = response
  325. return
  326. }
  327. func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
  328. message := "<u:DeletePortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
  329. "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
  330. "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
  331. "</u:DeletePortMapping>"
  332. var response *http.Response
  333. response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
  334. if response != nil {
  335. defer response.Body.Close() // nolint: errcheck
  336. }
  337. if err != nil {
  338. return
  339. }
  340. // TODO: check response to see if the port was deleted
  341. // log.Println(message, response)
  342. _ = response
  343. return
  344. }