natupnp.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. package eth
  2. // Just enough UPnP to be able to forward ports
  3. //
  4. import (
  5. "bytes"
  6. "encoding/xml"
  7. "errors"
  8. "net"
  9. "net/http"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "time"
  14. )
  15. type upnpNAT struct {
  16. serviceURL string
  17. ourIP string
  18. }
  19. func Discover() (nat NAT, err error) {
  20. ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
  21. if err != nil {
  22. return
  23. }
  24. conn, err := net.ListenPacket("udp4", ":0")
  25. if err != nil {
  26. return
  27. }
  28. socket := conn.(*net.UDPConn)
  29. defer socket.Close()
  30. err = socket.SetDeadline(time.Now().Add(10 * time.Second))
  31. if err != nil {
  32. return
  33. }
  34. st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
  35. buf := bytes.NewBufferString(
  36. "M-SEARCH * HTTP/1.1\r\n" +
  37. "HOST: 239.255.255.250:1900\r\n" +
  38. st +
  39. "MAN: \"ssdp:discover\"\r\n" +
  40. "MX: 2\r\n\r\n")
  41. message := buf.Bytes()
  42. answerBytes := make([]byte, 1024)
  43. for i := 0; i < 3; i++ {
  44. _, err = socket.WriteToUDP(message, ssdp)
  45. if err != nil {
  46. return
  47. }
  48. var n int
  49. n, _, err = socket.ReadFromUDP(answerBytes)
  50. if err != nil {
  51. continue
  52. // socket.Close()
  53. // return
  54. }
  55. answer := string(answerBytes[0:n])
  56. if strings.Index(answer, "\r\n"+st) < 0 {
  57. continue
  58. }
  59. // HTTP header field names are case-insensitive.
  60. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
  61. locString := "\r\nlocation: "
  62. answer = strings.ToLower(answer)
  63. locIndex := strings.Index(answer, locString)
  64. if locIndex < 0 {
  65. continue
  66. }
  67. loc := answer[locIndex+len(locString):]
  68. endIndex := strings.Index(loc, "\r\n")
  69. if endIndex < 0 {
  70. continue
  71. }
  72. locURL := loc[0:endIndex]
  73. var serviceURL string
  74. serviceURL, err = getServiceURL(locURL)
  75. if err != nil {
  76. return
  77. }
  78. var ourIP string
  79. ourIP, err = getOurIP()
  80. if err != nil {
  81. return
  82. }
  83. nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
  84. return
  85. }
  86. err = errors.New("UPnP port discovery failed.")
  87. return
  88. }
  89. // service represents the Service type in an UPnP xml description.
  90. // Only the parts we care about are present and thus the xml may have more
  91. // fields than present in the structure.
  92. type service struct {
  93. ServiceType string `xml:"serviceType"`
  94. ControlURL string `xml:"controlURL"`
  95. }
  96. // deviceList represents the deviceList type in an UPnP xml description.
  97. // Only the parts we care about are present and thus the xml may have more
  98. // fields than present in the structure.
  99. type deviceList struct {
  100. XMLName xml.Name `xml:"deviceList"`
  101. Device []device `xml:"device"`
  102. }
  103. // serviceList represents the serviceList type in an UPnP xml description.
  104. // Only the parts we care about are present and thus the xml may have more
  105. // fields than present in the structure.
  106. type serviceList struct {
  107. XMLName xml.Name `xml:"serviceList"`
  108. Service []service `xml:"service"`
  109. }
  110. // device represents the device type in an UPnP xml description.
  111. // Only the parts we care about are present and thus the xml may have more
  112. // fields than present in the structure.
  113. type device struct {
  114. XMLName xml.Name `xml:"device"`
  115. DeviceType string `xml:"deviceType"`
  116. DeviceList deviceList `xml:"deviceList"`
  117. ServiceList serviceList `xml:"serviceList"`
  118. }
  119. // specVersion represents the specVersion in a UPnP xml description.
  120. // Only the parts we care about are present and thus the xml may have more
  121. // fields than present in the structure.
  122. type specVersion struct {
  123. XMLName xml.Name `xml:"specVersion"`
  124. Major int `xml:"major"`
  125. Minor int `xml:"minor"`
  126. }
  127. // root represents the Root document for a UPnP xml description.
  128. // Only the parts we care about are present and thus the xml may have more
  129. // fields than present in the structure.
  130. type root struct {
  131. XMLName xml.Name `xml:"root"`
  132. SpecVersion specVersion
  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 dl[i].DeviceType == deviceType {
  139. return &dl[i]
  140. }
  141. }
  142. return nil
  143. }
  144. func getChildService(d *device, serviceType string) *service {
  145. sl := d.ServiceList.Service
  146. for i := 0; i < len(sl); i++ {
  147. if sl[i].ServiceType == serviceType {
  148. return &sl[i]
  149. }
  150. }
  151. return nil
  152. }
  153. func getOurIP() (ip string, err error) {
  154. hostname, err := os.Hostname()
  155. if err != nil {
  156. return
  157. }
  158. p, err := net.LookupIP(hostname)
  159. if err != nil && len(p) > 0 {
  160. return
  161. }
  162. return p[0].String(), nil
  163. }
  164. func getServiceURL(rootURL string) (url string, err error) {
  165. r, err := http.Get(rootURL)
  166. if err != nil {
  167. return
  168. }
  169. defer r.Body.Close()
  170. if r.StatusCode >= 400 {
  171. err = errors.New(string(r.StatusCode))
  172. return
  173. }
  174. var root root
  175. err = xml.NewDecoder(r.Body).Decode(&root)
  176. if err != nil {
  177. return
  178. }
  179. a := &root.Device
  180. if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
  181. err = errors.New("No InternetGatewayDevice")
  182. return
  183. }
  184. b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
  185. if b == nil {
  186. err = errors.New("No WANDevice")
  187. return
  188. }
  189. c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
  190. if c == nil {
  191. err = errors.New("No WANConnectionDevice")
  192. return
  193. }
  194. d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
  195. if d == nil {
  196. err = errors.New("No WANIPConnection")
  197. return
  198. }
  199. url = combineURL(rootURL, d.ControlURL)
  200. return
  201. }
  202. func combineURL(rootURL, subURL string) string {
  203. protocolEnd := "://"
  204. protoEndIndex := strings.Index(rootURL, protocolEnd)
  205. a := rootURL[protoEndIndex+len(protocolEnd):]
  206. rootIndex := strings.Index(a, "/")
  207. return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
  208. }
  209. func soapRequest(url, function, message string) (r *http.Response, err error) {
  210. fullMessage := "<?xml version=\"1.0\" ?>" +
  211. "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
  212. "<s:Body>" + message + "</s:Body></s:Envelope>"
  213. req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
  214. if err != nil {
  215. return nil, err
  216. }
  217. req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
  218. req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
  219. //req.Header.Set("Transfer-Encoding", "chunked")
  220. req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
  221. req.Header.Set("Connection", "Close")
  222. req.Header.Set("Cache-Control", "no-cache")
  223. req.Header.Set("Pragma", "no-cache")
  224. // log.Stderr("soapRequest ", req)
  225. //fmt.Println(fullMessage)
  226. r, err = http.DefaultClient.Do(req)
  227. if err != nil {
  228. return
  229. }
  230. if r.Body != nil {
  231. defer r.Body.Close()
  232. }
  233. if r.StatusCode >= 400 {
  234. // log.Stderr(function, r.StatusCode)
  235. err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
  236. r = nil
  237. return
  238. }
  239. return
  240. }
  241. type statusInfo struct {
  242. externalIpAddress string
  243. }
  244. func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
  245. message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
  246. "</u:GetStatusInfo>"
  247. var response *http.Response
  248. response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
  249. if err != nil {
  250. return
  251. }
  252. // TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
  253. response.Body.Close()
  254. return
  255. }
  256. func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
  257. info, err := n.getStatusInfo()
  258. if err != nil {
  259. return
  260. }
  261. addr = net.ParseIP(info.externalIpAddress)
  262. return
  263. }
  264. func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
  265. // A single concatenation would break ARM compilation.
  266. message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
  267. "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
  268. message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
  269. message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
  270. "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
  271. "<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
  272. message += description +
  273. "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
  274. "</NewLeaseDuration></u:AddPortMapping>"
  275. var response *http.Response
  276. response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
  277. if err != nil {
  278. return
  279. }
  280. // TODO: check response to see if the port was forwarded
  281. // log.Println(message, response)
  282. mappedExternalPort = externalPort
  283. _ = response
  284. return
  285. }
  286. func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
  287. message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
  288. "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
  289. "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
  290. "</u:DeletePortMapping>"
  291. var response *http.Response
  292. response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
  293. if err != nil {
  294. return
  295. }
  296. // TODO: check response to see if the port was deleted
  297. // log.Println(message, response)
  298. _ = response
  299. return
  300. }