upnp.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /****************************************************************************
  2. * Copyright (c) 2011 by Michael Fischer. All rights reserved.
  3. *
  4. * This work based on source from proconX Pty Ltd. Therefore
  5. * partial copyright by: Copyright (c) 2010 proconX Pty Ltd.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. Neither the name of the author nor the names of its contributors may
  17. * be used to endorse or promote products derived from this software
  18. * 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
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  24. * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. *****************************************************************************
  34. * History:
  35. *
  36. * 27.05.2011 mifi First Version based on source from proconX Pty Ltd.
  37. * Information about UPnP can be found at "www.upnp.org".
  38. * Note: I had no success with the UPnP Device Validator
  39. * from the "Open Software Projects". The old original
  40. * Intel tools from the book "UPnP Design by Example"
  41. * works a little bit better, but has problems too.
  42. ****************************************************************************/
  43. #define __MAIN_C__
  44. #include <sys/version.h>
  45. #include <sys/thread.h>
  46. #include <sys/socket.h>
  47. #include <sys/device.h>
  48. #include <sys/event.h>
  49. #include <sys/timer.h>
  50. #include <net/if_var.h>
  51. #include <arpa/inet.h>
  52. #include <pro/httpd.h>
  53. #include <stdlib.h>
  54. #include <stdio.h>
  55. #include <stdint.h>
  56. #include <string.h>
  57. /*=========================================================================*/
  58. /* DEFINE: All Structures and Common Constants */
  59. /*=========================================================================*/
  60. /*
  61. * Enable to fulfil the UPnP specification for notify messages.
  62. * I think this is not needed to advertise only the device on
  63. * windows networks.
  64. */
  65. #define UPNP_FULFIL_SPEC 0
  66. #ifndef UPNP_SERVICE_STACK
  67. #define UPNP_SERVICE_STACK ((1024 * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD)
  68. #endif
  69. #define UPNP_CGI_NAME "upnp.cgi"
  70. #define UPNP_UUID_PART1 "56F9C1D5-5083-4ee5-A6B3-"
  71. #define SSDP_IP 0xfaffffefU /* 239.255.255.250 */
  72. #define SSDP_PORT 1900
  73. #define SSDP_BUFFER_SIZE 512
  74. #define SSDP_NOTIFY_TIMEOUT 300000U /* 5 minutes */
  75. /* *INDENT-OFF* */
  76. static char HTTP_TEXT_XML[] = "text/xml; charset=\"utf-8\"";
  77. static char NOTIFY_ALIVE[] =
  78. "NOTIFY * HTTP/1.1\r\n"
  79. "Host: 239.255.255.250:1900\r\n"
  80. "Cache-Control: max-age=900\r\n"
  81. "Location: http://%s/cgi-bin/" UPNP_CGI_NAME "\r\n"
  82. "NT: %s\r\n"
  83. "NTS: ssdp:alive\r\n"
  84. "USN: %s%s\r\n"
  85. "Server: %s\r\n"
  86. "Content-Length: 0\r\n"
  87. "\r\n";
  88. static char NOTIFY_BYE[] =
  89. "NOTIFY * HTTP/1.1\r\n"
  90. "Host: 239.255.255.250:1900\r\n"
  91. "NT: upnp:rootdevice\r\n"
  92. "NTS: ssdp:byebye\r\n"
  93. "USN: %s::upnp:rootdevice\r\n"
  94. "Content-Length: 0\r\n"
  95. "\r\n";
  96. static char MSEARCH_RESPONSE[] =
  97. "HTTP/1.1 200 OK\r\n"
  98. "Cache-Control: max-age=900\r\n"
  99. "Ext:\r\n"
  100. "Location: http://%s/cgi-bin/" UPNP_CGI_NAME "\r\n"
  101. "Server: %s\r\n"
  102. "ST: upnp:rootdevice\r\n"
  103. "USN: %s::upnp:rootdevice\r\n"
  104. "Content-Length: 0\r\n"
  105. "\r\n";
  106. /*
  107. * All listed elements which are not tagged are "REQUIRED" as per
  108. * UPnP Device Architecture 1.0. Some other are "OPTIONAL" or "RECOMMENDED".
  109. */
  110. static char HTML_UPNP[] =
  111. "<?xml version=\"1.0\"?>\r\n"
  112. "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\r\n"
  113. "<specVersion>\r\n"
  114. "<major>1</major>\r\n"
  115. "<minor>0</minor>\r\n"
  116. "</specVersion>\r\n"
  117. "<URLBase>http://%s</URLBase>\r\n" /* OPTIONAL */
  118. "<device>\r\n"
  119. "<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\r\n"
  120. "<friendlyName>NutOS Server (%02X%02X%02X%02X%02X%02X)</friendlyName>\r\n"
  121. "<manufacturer>egnite</manufacturer>\r\n"
  122. "<manufacturerURL>http://www.egnite.de</manufacturerURL>\r\n" /* OPTIONAL */
  123. "<modelDescription>NutOS HTTP Daemon</modelDescription>\r\n" /* RECOMMENDED */
  124. "<modelName>Ethernut</modelName>\r\n"
  125. "<modelNumber>%02X%02X%02X%02X%02X%02X</modelNumber>\r\n" /* RECOMMENDED */
  126. "<modelURL>http://www.ethernut.de</modelURL>\r\n" /* OPTIONAL */
  127. "<serialNumber>%02X%02X%02X%02X%02X%02X</serialNumber>\r\n" /* RECOMMENDED */
  128. "<UDN>uuid:" UPNP_UUID_PART1 "%02X%02X%02X%02X%02X%02X</UDN>\r\n"
  129. /*
  130. * Despite being not required by the UPnP standard, we must
  131. * define at leat one service to make the device show up on Windows XP.
  132. */
  133. "<serviceList>\r\n"
  134. "<service>\r\n"
  135. "<serviceType>urn:schemas-upnp-org:service:Dummy:1</serviceType>\r\n"
  136. "<serviceId>urn:upnp-org:serviceId:Dummy</serviceId>\r\n"
  137. "<SCPDURL>/upnp/dummy.xml</SCPDURL>\r\n"
  138. "<controlURL>/upnp</controlURL>\r\n"
  139. "<eventSubURL></eventSubURL>\r\n"
  140. "</service>\r\n"
  141. "</serviceList>\r\n"
  142. "<presentationURL>http://%s/index.html</presentationURL>\r\n" /* RECOMMENDED */
  143. "</device>\r\n"
  144. "</root>\r\n";
  145. /* *INDENT-ON* */
  146. /*=========================================================================*/
  147. /* DEFINE: Prototypes */
  148. /*=========================================================================*/
  149. /*=========================================================================*/
  150. /* DEFINE: Definition of all local Data */
  151. /*=========================================================================*/
  152. static NUTDEVICE *dev;
  153. static IFNET *nif;
  154. static char MyUSN[48];
  155. static char ServerInfo[32];
  156. /*=========================================================================*/
  157. /* DEFINE: Definition of all local Procedures */
  158. /*=========================================================================*/
  159. /***************************************************************************/
  160. /* GetRand */
  161. /***************************************************************************/
  162. static uint32_t GetRand(uint32_t MaxValue)
  163. {
  164. uint32_t Value;
  165. Value = rand() & MaxValue;
  166. return (Value);
  167. } /* GetRand */
  168. /***************************************************************************/
  169. /* LocationCGIHandler */
  170. /***************************************************************************/
  171. static int LocationCGIHandler(FILE * stream, REQUEST * req)
  172. {
  173. /*
  174. * Generate HTTP header
  175. */
  176. NutHttpSendHeaderTop(stream, req, 200, "OK");
  177. NutHttpSendHeaderBottom(stream, 0, HTTP_TEXT_XML, -1L);
  178. /*
  179. * Output XML data
  180. */
  181. fprintf(stream, HTML_UPNP,
  182. inet_ntoa(nif->if_local_ip), /* URLBase */
  183. nif->if_mac[0], nif->if_mac[1], /* friendlyName */
  184. nif->if_mac[2], nif->if_mac[3],
  185. nif->if_mac[4], nif->if_mac[5],
  186. nif->if_mac[0], nif->if_mac[1], /* modelNumber */
  187. nif->if_mac[2], nif->if_mac[3],
  188. nif->if_mac[4], nif->if_mac[5],
  189. nif->if_mac[0], nif->if_mac[1], /* serialNumber */
  190. nif->if_mac[2], nif->if_mac[3],
  191. nif->if_mac[4], nif->if_mac[5],
  192. nif->if_mac[0], nif->if_mac[1], /* UDN */
  193. nif->if_mac[2], nif->if_mac[3],
  194. nif->if_mac[4], nif->if_mac[5],
  195. inet_ntoa(nif->if_local_ip)); /* presentationURL */
  196. return 0;
  197. } /* LocationCGIHandler */
  198. /***************************************************************************/
  199. /* SendNotifyAliveChunk */
  200. /***************************************************************************/
  201. static void SendNotifyAliveChunk(char *Buffer, UDPSOCKET * TxSock)
  202. {
  203. int Size;
  204. /*
  205. * Send device discovery messages.
  206. * The first message is really needed.
  207. * All other to fulfil the UPnP specification.
  208. */
  209. /* Send first alive message */
  210. Size = sprintf(Buffer, NOTIFY_ALIVE,
  211. inet_ntoa(nif->if_local_ip),
  212. "upnp:rootdevice", /* NT */
  213. MyUSN, "::upnp:rootdevice", /* USN */
  214. ServerInfo);
  215. NutUdpSendTo(TxSock, SSDP_IP, SSDP_PORT, Buffer, Size);
  216. NutSleep(25);
  217. #if (UPNP_FULFIL_SPEC >= 1)
  218. /* Send second alive message */
  219. Size = sprintf(Buffer, NOTIFY_ALIVE,
  220. inet_ntoa(nif->if_local_ip),
  221. MyUSN, /* NT */
  222. MyUSN, "", /* USN */
  223. ServerInfo);
  224. NutUdpSendTo(TxSock, SSDP_IP, SSDP_PORT, Buffer, Size);
  225. NutSleep(25);
  226. /* Send third alive message */
  227. Size = sprintf(Buffer, NOTIFY_ALIVE,
  228. inet_ntoa(nif->if_local_ip),
  229. "urn:schemas-upnp-org:device:Basic:1", /* NT */
  230. MyUSN, "::urn:schemas-upnp-org:device:Basic:1", /* USN */
  231. ServerInfo);
  232. NutUdpSendTo(TxSock, SSDP_IP, SSDP_PORT, Buffer, Size);
  233. NutSleep(25);
  234. /*
  235. * Send embedded device discovery messages
  236. */
  237. /* No embedded device available */
  238. /*
  239. * Send service discovery messages
  240. */
  241. Size = sprintf(Buffer, NOTIFY_ALIVE,
  242. inet_ntoa(nif->if_local_ip),
  243. "urn:schemas-upnp-org:service:Dummy:1", /* NT */
  244. MyUSN, "::urn:schemas-upnp-org:service:Dummy:1", /* USN */
  245. ServerInfo);
  246. NutUdpSendTo(TxSock, SSDP_IP, SSDP_PORT, Buffer, Size);
  247. #endif
  248. } /* SendNotifyAliveChunk */
  249. /***************************************************************************/
  250. /* SendNotifyAlive */
  251. /***************************************************************************/
  252. static void SendNotifyAlive(char *Buffer, UDPSOCKET * TxSock)
  253. {
  254. uint32_t RandomDelay;
  255. /*
  256. * Send device discovery messages. Here two chunks will be send
  257. * with a random delay between each chunk.
  258. */
  259. SendNotifyAliveChunk(Buffer, TxSock);
  260. /* Create random value between 500...2000ms */
  261. RandomDelay = 500 + GetRand(1500);
  262. NutSleep(RandomDelay);
  263. SendNotifyAliveChunk(Buffer, TxSock);
  264. } /* SendNotifyAlive */
  265. /***************************************************************************/
  266. /* NotifyTask */
  267. /***************************************************************************/
  268. THREAD(NotifyTask, arg)
  269. {
  270. char *Buffer;
  271. UDPSOCKET *TxSock;
  272. /*
  273. * Alloc buffer and create sockets
  274. */
  275. Buffer = malloc(SSDP_BUFFER_SIZE + 1); /* Add one for the string termination 0 */
  276. TxSock = NutUdpCreateSocket(0);
  277. for (;;) {
  278. NutSleep(SSDP_NOTIFY_TIMEOUT);
  279. /* Send alive message. */
  280. SendNotifyAlive(Buffer, TxSock);
  281. NutThreadYield();
  282. }
  283. } /* NotifyTask */
  284. /***************************************************************************/
  285. /* SSDPTask */
  286. /***************************************************************************/
  287. THREAD(SSDPTask, arg)
  288. {
  289. int Size;
  290. char *Buffer;
  291. UDPSOCKET *RxSock;
  292. UDPSOCKET *TxSock;
  293. uint32_t RemoteIP;
  294. uint16_t Port;
  295. char *Start;
  296. char *End;
  297. uint32_t MaxDelay;
  298. uint32_t Delay;
  299. sprintf(MyUSN, "uuid:%s%02X%02X%02X%02X%02X%02X",
  300. UPNP_UUID_PART1,
  301. nif->if_mac[0], nif->if_mac[1],
  302. nif->if_mac[2], nif->if_mac[3],
  303. nif->if_mac[4], nif->if_mac[5]);
  304. sprintf(ServerInfo, "NutOS/%d.%d UPnP/1.0", NUT_VERSION_MAJOR, NUT_VERSION_MINOR);
  305. /*
  306. * Alloc buffer and create sockets
  307. */
  308. Buffer = malloc(SSDP_BUFFER_SIZE + 1); /* Add one for the string termination 0 */
  309. RxSock = NutUdpCreateSocket(SSDP_PORT);
  310. TxSock = NutUdpCreateSocket(0);
  311. /*
  312. * First send ByeBye to the network.
  313. */
  314. Size = sprintf(Buffer, NOTIFY_BYE, MyUSN);
  315. NutUdpSendTo(TxSock, SSDP_IP, SSDP_PORT, Buffer, Size);
  316. NutSleep(500);
  317. /*
  318. * Now we can send a "hello" to the network, we are back.
  319. */
  320. SendNotifyAlive(Buffer, TxSock);
  321. for (;;) {
  322. Size = NutUdpReceiveFrom(RxSock, &RemoteIP, &Port, Buffer, SSDP_BUFFER_SIZE, NUT_WAIT_INFINITE);
  323. if (Size > 0) {
  324. Buffer[Size] = 0; /* Terminate string */
  325. /*
  326. * Check if a control point serach for a network device.
  327. */
  328. if ((strstr(Buffer, "M-SEARCH") != NULL) && /* <= REQUIRED */
  329. (strstr(Buffer, "ssdp:discover") != NULL)) { /* <= REQUIRED */
  330. /*
  331. * Check if the control point search for "all", "root devices"
  332. * or for a special device.
  333. */
  334. if ((strstr(Buffer, "ssdp:all") != NULL) ||
  335. (strstr(Buffer, "upnp:rootdevice") != NULL) || (strstr(Buffer, MyUSN) != NULL)) {
  336. Start = strstr(Buffer, "MX:");
  337. if (Start != NULL) {
  338. /* Jump over the "MX:" */
  339. Start += 3;
  340. /* Get delay in seconds */
  341. MaxDelay = strtol(Start, &End, 10);
  342. if (MaxDelay == 0) {
  343. MaxDelay = 1;
  344. }
  345. /* Change to millisecond */
  346. MaxDelay *= 1000;
  347. /* Get random value */
  348. Delay = GetRand(MaxDelay);
  349. } else {
  350. /* Ups, error */
  351. Delay = 1234;
  352. }
  353. NutSleep(Delay);
  354. /*
  355. * We must send a M-SEARCH response
  356. */
  357. Size = sprintf(Buffer, MSEARCH_RESPONSE,
  358. inet_ntoa(nif->if_local_ip),
  359. ServerInfo, MyUSN);
  360. NutUdpSendTo(TxSock, RemoteIP, Port, Buffer, Size);
  361. NutSleep(25);
  362. NutUdpSendTo(TxSock, RemoteIP, Port, Buffer, Size);
  363. }
  364. }
  365. }
  366. NutThreadYield();
  367. }
  368. } /* SSDPTask */
  369. /*=========================================================================*/
  370. /* DEFINE: All code exported */
  371. /*=========================================================================*/
  372. /***************************************************************************/
  373. /* upnp_Init */
  374. /***************************************************************************/
  375. void upnp_Init(void)
  376. {
  377. /* Set UPnP multicast address */
  378. printf("Add UPnP multicast address...");
  379. if (NutNetIfAddMcastAddr("eth0", SSDP_IP) != 0) {
  380. puts("failed");
  381. } else {
  382. puts("OK");
  383. /*
  384. * Multicast will be supported,
  385. * get device and network interface info for eth0.
  386. */
  387. dev = NutDeviceLookup("eth0");
  388. if (dev != NULL) {
  389. /* Get network interface pointer */
  390. nif = dev->dev_icb;
  391. if (NutRegisterCgi(UPNP_CGI_NAME, LocationCGIHandler) == 0) {
  392. /* Start the UPnP threads */
  393. NutThreadCreate("ssdp", SSDPTask, NULL, UPNP_SERVICE_STACK);
  394. NutThreadCreate("ssdp-notify", NotifyTask, NULL, UPNP_SERVICE_STACK);
  395. }
  396. }
  397. }
  398. } /* upnp_Init */
  399. /*** EOF ***/