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.

380 lines
9.6 KiB

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