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.

378 lines
9.6 KiB

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