/thirdparty/libportfwd/third-party/miniupnpc-1.6/miniupnpc.c

http://github.com/tomahawk-player/tomahawk · C · 906 lines · 762 code · 35 blank · 109 comment · 134 complexity · 60cfe29f195c644b27d20973ed3eb19c MD5 · raw file

  1. /* $Id: miniupnpc.c,v 1.95 2011/05/15 21:42:26 nanard Exp $ */
  2. /* Project : miniupnp
  3. * Author : Thomas BERNARD
  4. * copyright (c) 2005-2011 Thomas Bernard
  5. * This software is subjet to the conditions detailed in the
  6. * provided LICENSE file. */
  7. #define __EXTENSIONS__ 1
  8. #if !defined(MACOSX) && !defined(__sun)
  9. #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
  10. #ifndef __cplusplus
  11. #define _XOPEN_SOURCE 600
  12. #endif
  13. #endif
  14. #ifndef __BSD_VISIBLE
  15. #define __BSD_VISIBLE 1
  16. #endif
  17. #endif
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #ifdef WIN32
  22. /* Win32 Specific includes and defines */
  23. #include <winsock2.h>
  24. #include <ws2tcpip.h>
  25. #include <io.h>
  26. #include <iphlpapi.h>
  27. #define snprintf _snprintf
  28. #ifndef strncasecmp
  29. #if defined(_MSC_VER) && (_MSC_VER >= 1400)
  30. #define strncasecmp _memicmp
  31. #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  32. #define strncasecmp memicmp
  33. #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  34. #endif /* #ifndef strncasecmp */
  35. #define MAXHOSTNAMELEN 64
  36. #else /* #ifdef WIN32 */
  37. /* Standard POSIX includes */
  38. #include <unistd.h>
  39. #if defined(__amigaos__) && !defined(__amigaos4__)
  40. /* Amiga OS 3 specific stuff */
  41. #define socklen_t int
  42. #else
  43. #include <sys/select.h>
  44. #endif
  45. #include <sys/socket.h>
  46. #include <sys/types.h>
  47. #include <sys/param.h>
  48. #include <netinet/in.h>
  49. #include <arpa/inet.h>
  50. #include <netdb.h>
  51. #include <net/if.h>
  52. #if !defined(__amigaos__) && !defined(__amigaos4__)
  53. #include <poll.h>
  54. #endif
  55. #include <strings.h>
  56. #include <errno.h>
  57. #define closesocket close
  58. #endif /* #else WIN32 */
  59. #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
  60. #include <sys/time.h>
  61. #endif
  62. #if defined(__amigaos__) || defined(__amigaos4__)
  63. /* Amiga OS specific stuff */
  64. #define TIMEVAL struct timeval
  65. #endif
  66. #include "miniupnpc.h"
  67. #include "minissdpc.h"
  68. #include "miniwget.h"
  69. #include "minisoap.h"
  70. #include "minixml.h"
  71. #include "upnpcommands.h"
  72. #include "connecthostport.h"
  73. #include "receivedata.h"
  74. #ifdef WIN32
  75. #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
  76. #else
  77. #define PRINT_SOCKET_ERROR(x) perror(x)
  78. #endif
  79. #define SOAPPREFIX "s"
  80. #define SERVICEPREFIX "u"
  81. #define SERVICEPREFIX2 'u'
  82. /* root description parsing */
  83. LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
  84. {
  85. struct xmlparser parser;
  86. /* xmlparser object */
  87. parser.xmlstart = buffer;
  88. parser.xmlsize = bufsize;
  89. parser.data = data;
  90. parser.starteltfunc = IGDstartelt;
  91. parser.endeltfunc = IGDendelt;
  92. parser.datafunc = IGDdata;
  93. parser.attfunc = 0;
  94. parsexml(&parser);
  95. #ifdef DEBUG
  96. printIGD(data);
  97. #endif
  98. }
  99. /* simpleUPnPcommand2 :
  100. * not so simple !
  101. * return values :
  102. * pointer - OK
  103. * NULL - error */
  104. char * simpleUPnPcommand2(int s, const char * url, const char * service,
  105. const char * action, struct UPNParg * args,
  106. int * bufsize, const char * httpversion)
  107. {
  108. char hostname[MAXHOSTNAMELEN+1];
  109. unsigned short port = 0;
  110. char * path;
  111. char soapact[128];
  112. char soapbody[2048];
  113. char * buf;
  114. int n;
  115. *bufsize = 0;
  116. snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
  117. if(args==NULL)
  118. {
  119. /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
  120. "<?xml version=\"1.0\"?>\r\n"
  121. "<" SOAPPREFIX ":Envelope "
  122. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  123. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  124. "<" SOAPPREFIX ":Body>"
  125. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
  126. "</" SERVICEPREFIX ":%s>"
  127. "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
  128. "\r\n", action, service, action);
  129. }
  130. else
  131. {
  132. char * p;
  133. const char * pe, * pv;
  134. int soapbodylen;
  135. soapbodylen = snprintf(soapbody, sizeof(soapbody),
  136. "<?xml version=\"1.0\"?>\r\n"
  137. "<" SOAPPREFIX ":Envelope "
  138. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  139. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  140. "<" SOAPPREFIX ":Body>"
  141. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
  142. action, service);
  143. p = soapbody + soapbodylen;
  144. while(args->elt)
  145. {
  146. /* check that we are never overflowing the string... */
  147. if(soapbody + sizeof(soapbody) <= p + 100)
  148. {
  149. /* we keep a margin of at least 100 bytes */
  150. return NULL;
  151. }
  152. *(p++) = '<';
  153. pe = args->elt;
  154. while(*pe)
  155. *(p++) = *(pe++);
  156. *(p++) = '>';
  157. if((pv = args->val))
  158. {
  159. while(*pv)
  160. *(p++) = *(pv++);
  161. }
  162. *(p++) = '<';
  163. *(p++) = '/';
  164. pe = args->elt;
  165. while(*pe)
  166. *(p++) = *(pe++);
  167. *(p++) = '>';
  168. args++;
  169. }
  170. *(p++) = '<';
  171. *(p++) = '/';
  172. *(p++) = SERVICEPREFIX2;
  173. *(p++) = ':';
  174. pe = action;
  175. while(*pe)
  176. *(p++) = *(pe++);
  177. strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
  178. soapbody + sizeof(soapbody) - p);
  179. }
  180. if(!parseURL(url, hostname, &port, &path)) return NULL;
  181. if(s<0)
  182. {
  183. s = connecthostport(hostname, port);
  184. if(s < 0)
  185. {
  186. return NULL;
  187. }
  188. }
  189. n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
  190. if(n<=0) {
  191. #ifdef DEBUG
  192. printf("Error sending SOAP request\n");
  193. #endif
  194. closesocket(s);
  195. return NULL;
  196. }
  197. buf = getHTTPResponse(s, bufsize);
  198. #ifdef DEBUG
  199. if(*bufsize > 0 && buf)
  200. {
  201. printf("SOAP Response :\n%.*s\n", *bufsize, buf);
  202. }
  203. #endif
  204. closesocket(s);
  205. return buf;
  206. }
  207. /* simpleUPnPcommand :
  208. * not so simple !
  209. * return values :
  210. * pointer - OK
  211. * NULL - error */
  212. char * simpleUPnPcommand(int s, const char * url, const char * service,
  213. const char * action, struct UPNParg * args,
  214. int * bufsize)
  215. {
  216. char * buf;
  217. buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
  218. /*
  219. buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
  220. if (!buf || *bufsize == 0)
  221. {
  222. #if DEBUG
  223. printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
  224. #endif
  225. buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
  226. }
  227. */
  228. return buf;
  229. }
  230. /* parseMSEARCHReply()
  231. * the last 4 arguments are filled during the parsing :
  232. * - location/locationsize : "location:" field of the SSDP reply packet
  233. * - st/stsize : "st:" field of the SSDP reply packet.
  234. * The strings are NOT null terminated */
  235. static void
  236. parseMSEARCHReply(const char * reply, int size,
  237. const char * * location, int * locationsize,
  238. const char * * st, int * stsize)
  239. {
  240. int a, b, i;
  241. i = 0;
  242. a = i; /* start of the line */
  243. b = 0; /* end of the "header" (position of the colon) */
  244. while(i<size)
  245. {
  246. switch(reply[i])
  247. {
  248. case ':':
  249. if(b==0)
  250. {
  251. b = i; /* end of the "header" */
  252. /*for(j=a; j<b; j++)
  253. {
  254. putchar(reply[j]);
  255. }
  256. */
  257. }
  258. break;
  259. case '\x0a':
  260. case '\x0d':
  261. if(b!=0)
  262. {
  263. /*for(j=b+1; j<i; j++)
  264. {
  265. putchar(reply[j]);
  266. }
  267. putchar('\n');*/
  268. /* skip the colon and white spaces */
  269. do { b++; } while(reply[b]==' ');
  270. if(0==strncasecmp(reply+a, "location", 8))
  271. {
  272. *location = reply+b;
  273. *locationsize = i-b;
  274. }
  275. else if(0==strncasecmp(reply+a, "st", 2))
  276. {
  277. *st = reply+b;
  278. *stsize = i-b;
  279. }
  280. b = 0;
  281. }
  282. a = i+1;
  283. break;
  284. default:
  285. break;
  286. }
  287. i++;
  288. }
  289. }
  290. /* port upnp discover : SSDP protocol */
  291. #define PORT 1900
  292. #define XSTR(s) STR(s)
  293. #define STR(s) #s
  294. #define UPNP_MCAST_ADDR "239.255.255.250"
  295. /* for IPv6 */
  296. #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
  297. #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
  298. /* upnpDiscover() :
  299. * return a chained list of all devices found or NULL if
  300. * no devices was found.
  301. * It is up to the caller to free the chained list
  302. * delay is in millisecond (poll) */
  303. LIBSPEC struct UPNPDev *
  304. upnpDiscover(int delay, const char * multicastif,
  305. const char * minissdpdsock, int sameport,
  306. int ipv6,
  307. int * error)
  308. {
  309. struct UPNPDev * tmp;
  310. struct UPNPDev * devlist = 0;
  311. int opt = 1;
  312. static const char MSearchMsgFmt[] =
  313. "M-SEARCH * HTTP/1.1\r\n"
  314. "HOST: %s:" XSTR(PORT) "\r\n"
  315. "ST: %s\r\n"
  316. "MAN: \"ssdp:discover\"\r\n"
  317. "MX: %u\r\n"
  318. "\r\n";
  319. static const char * const deviceList[] = {
  320. #if 0
  321. "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
  322. "urn:schemas-upnp-org:service:WANIPConnection:2",
  323. #endif
  324. "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
  325. "urn:schemas-upnp-org:service:WANIPConnection:1",
  326. "urn:schemas-upnp-org:service:WANPPPConnection:1",
  327. "upnp:rootdevice",
  328. 0
  329. };
  330. int deviceIndex = 0;
  331. char bufr[1536]; /* reception and emission buffer */
  332. int sudp;
  333. int n;
  334. struct sockaddr_storage sockudp_r;
  335. unsigned int mx;
  336. #ifdef NO_GETADDRINFO
  337. struct sockaddr_storage sockudp_w;
  338. #else
  339. int rv;
  340. struct addrinfo hints, *servinfo, *p;
  341. #endif
  342. #ifdef WIN32
  343. MIB_IPFORWARDROW ip_forward;
  344. #endif
  345. int linklocal = 1;
  346. if(error)
  347. *error = UPNPDISCOVER_UNKNOWN_ERROR;
  348. #if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  349. /* first try to get infos from minissdpd ! */
  350. if(!minissdpdsock)
  351. minissdpdsock = "/var/run/minissdpd.sock";
  352. while(!devlist && deviceList[deviceIndex]) {
  353. devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
  354. minissdpdsock);
  355. /* We return what we have found if it was not only a rootdevice */
  356. if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
  357. if(error)
  358. *error = UPNPDISCOVER_SUCCESS;
  359. return devlist;
  360. }
  361. deviceIndex++;
  362. }
  363. deviceIndex = 0;
  364. #endif
  365. /* fallback to direct discovery */
  366. #ifdef WIN32
  367. sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  368. #else
  369. sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
  370. #endif
  371. if(sudp < 0)
  372. {
  373. if(error)
  374. *error = UPNPDISCOVER_SOCKET_ERROR;
  375. PRINT_SOCKET_ERROR("socket");
  376. return NULL;
  377. }
  378. /* reception */
  379. memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
  380. if(ipv6) {
  381. struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
  382. p->sin6_family = AF_INET6;
  383. if(sameport)
  384. p->sin6_port = htons(PORT);
  385. p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
  386. } else {
  387. struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
  388. p->sin_family = AF_INET;
  389. if(sameport)
  390. p->sin_port = htons(PORT);
  391. p->sin_addr.s_addr = INADDR_ANY;
  392. }
  393. #ifdef WIN32
  394. /* This code could help us to use the right Network interface for
  395. * SSDP multicast traffic */
  396. /* Get IP associated with the index given in the ip_forward struct
  397. * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
  398. if(!ipv6
  399. && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
  400. DWORD dwRetVal = 0;
  401. PMIB_IPADDRTABLE pIPAddrTable;
  402. DWORD dwSize = 0;
  403. #ifdef DEBUG
  404. IN_ADDR IPAddr;
  405. #endif
  406. int i;
  407. #ifdef DEBUG
  408. printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
  409. #endif
  410. pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
  411. if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
  412. free(pIPAddrTable);
  413. pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
  414. }
  415. if(pIPAddrTable) {
  416. dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
  417. #ifdef DEBUG
  418. printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
  419. #endif
  420. for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
  421. #ifdef DEBUG
  422. printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
  423. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
  424. printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
  425. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
  426. printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
  427. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
  428. printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
  429. printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
  430. printf("\tType and State[%d]:", i);
  431. printf("\n");
  432. #endif
  433. if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
  434. /* Set the address of this interface to be used */
  435. struct in_addr mc_if;
  436. memset(&mc_if, 0, sizeof(mc_if));
  437. mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
  438. if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
  439. PRINT_SOCKET_ERROR("setsockopt");
  440. }
  441. ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
  442. #ifndef DEBUG
  443. break;
  444. #endif
  445. }
  446. }
  447. free(pIPAddrTable);
  448. pIPAddrTable = NULL;
  449. }
  450. }
  451. #endif
  452. #ifdef WIN32
  453. if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
  454. #else
  455. if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
  456. #endif
  457. {
  458. if(error)
  459. *error = UPNPDISCOVER_SOCKET_ERROR;
  460. PRINT_SOCKET_ERROR("setsockopt");
  461. return NULL;
  462. }
  463. if(multicastif)
  464. {
  465. if(ipv6) {
  466. #if !defined(WIN32)
  467. /* according to MSDN, if_nametoindex() is supported since
  468. * MS Windows Vista and MS Windows Server 2008.
  469. * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
  470. unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
  471. if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
  472. {
  473. PRINT_SOCKET_ERROR("setsockopt");
  474. }
  475. #else
  476. #ifdef DEBUG
  477. printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
  478. #endif
  479. #endif
  480. } else {
  481. struct in_addr mc_if;
  482. mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
  483. ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
  484. if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
  485. {
  486. PRINT_SOCKET_ERROR("setsockopt");
  487. }
  488. }
  489. }
  490. /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
  491. if (bind(sudp, (const struct sockaddr *)&sockudp_r,
  492. ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
  493. {
  494. if(error)
  495. *error = UPNPDISCOVER_SOCKET_ERROR;
  496. PRINT_SOCKET_ERROR("bind");
  497. closesocket(sudp);
  498. return NULL;
  499. }
  500. if(error)
  501. *error = UPNPDISCOVER_SUCCESS;
  502. /* Calculating maximum response time in seconds */
  503. mx = ((unsigned int)delay) / 1000u;
  504. /* receiving SSDP response packet */
  505. for(n = 0; deviceList[deviceIndex]; deviceIndex++)
  506. {
  507. if(n == 0)
  508. {
  509. /* sending the SSDP M-SEARCH packet */
  510. n = snprintf(bufr, sizeof(bufr),
  511. MSearchMsgFmt,
  512. ipv6 ?
  513. (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
  514. : UPNP_MCAST_ADDR,
  515. deviceList[deviceIndex], mx);
  516. #ifdef DEBUG
  517. printf("Sending %s", bufr);
  518. #endif
  519. #ifdef NO_GETADDRINFO
  520. /* the following code is not using getaddrinfo */
  521. /* emission */
  522. memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
  523. if(ipv6) {
  524. struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
  525. p->sin6_family = AF_INET6;
  526. p->sin6_port = htons(PORT);
  527. inet_pton(AF_INET6,
  528. linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
  529. &(p->sin6_addr));
  530. } else {
  531. struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
  532. p->sin_family = AF_INET;
  533. p->sin_port = htons(PORT);
  534. p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
  535. }
  536. n = sendto(sudp, bufr, n, 0,
  537. &sockudp_w,
  538. ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
  539. if (n < 0) {
  540. if(error)
  541. *error = UPNPDISCOVER_SOCKET_ERROR;
  542. PRINT_SOCKET_ERROR("sendto");
  543. break;
  544. }
  545. #else /* #ifdef NO_GETADDRINFO */
  546. memset(&hints, 0, sizeof(hints));
  547. hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
  548. hints.ai_socktype = SOCK_DGRAM;
  549. /*hints.ai_flags = */
  550. if ((rv = getaddrinfo(ipv6
  551. ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
  552. : UPNP_MCAST_ADDR,
  553. XSTR(PORT), &hints, &servinfo)) != 0) {
  554. if(error)
  555. *error = UPNPDISCOVER_SOCKET_ERROR;
  556. #ifdef WIN32
  557. fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
  558. #else
  559. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
  560. #endif
  561. break;
  562. }
  563. for(p = servinfo; p; p = p->ai_next) {
  564. n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
  565. if (n < 0) {
  566. PRINT_SOCKET_ERROR("sendto");
  567. continue;
  568. }
  569. }
  570. freeaddrinfo(servinfo);
  571. if(n < 0) {
  572. if(error)
  573. *error = UPNPDISCOVER_SOCKET_ERROR;
  574. break;
  575. }
  576. #endif /* #ifdef NO_GETADDRINFO */
  577. }
  578. /* Waiting for SSDP REPLY packet to M-SEARCH */
  579. n = receivedata(sudp, bufr, sizeof(bufr), delay);
  580. if (n < 0) {
  581. /* error */
  582. if(error)
  583. *error = UPNPDISCOVER_SOCKET_ERROR;
  584. break;
  585. } else if (n == 0) {
  586. /* no data or Time Out */
  587. if (devlist) {
  588. /* no more device type to look for... */
  589. if(error)
  590. *error = UPNPDISCOVER_SUCCESS;
  591. break;
  592. }
  593. if(ipv6) {
  594. if(linklocal) {
  595. linklocal = 0;
  596. --deviceIndex;
  597. } else {
  598. linklocal = 1;
  599. }
  600. }
  601. } else {
  602. const char * descURL=NULL;
  603. int urlsize=0;
  604. const char * st=NULL;
  605. int stsize=0;
  606. /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
  607. parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
  608. if(st&&descURL)
  609. {
  610. #ifdef DEBUG
  611. printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
  612. stsize, st, urlsize, descURL);
  613. #endif
  614. for(tmp=devlist; tmp; tmp = tmp->pNext) {
  615. if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
  616. tmp->descURL[urlsize] == '\0' &&
  617. memcmp(tmp->st, st, stsize) == 0 &&
  618. tmp->st[stsize] == '\0')
  619. break;
  620. }
  621. /* at the exit of the loop above, tmp is null if
  622. * no duplicate device was found */
  623. if(tmp)
  624. continue;
  625. tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
  626. if(!tmp) {
  627. /* memory allocation error */
  628. if(error)
  629. *error = UPNPDISCOVER_MEMORY_ERROR;
  630. break;
  631. }
  632. tmp->pNext = devlist;
  633. tmp->descURL = tmp->buffer;
  634. tmp->st = tmp->buffer + 1 + urlsize;
  635. memcpy(tmp->buffer, descURL, urlsize);
  636. tmp->buffer[urlsize] = '\0';
  637. memcpy(tmp->buffer + urlsize + 1, st, stsize);
  638. tmp->buffer[urlsize+1+stsize] = '\0';
  639. devlist = tmp;
  640. }
  641. }
  642. }
  643. closesocket(sudp);
  644. return devlist;
  645. }
  646. /* freeUPNPDevlist() should be used to
  647. * free the chained list returned by upnpDiscover() */
  648. LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
  649. {
  650. struct UPNPDev * next;
  651. while(devlist)
  652. {
  653. next = devlist->pNext;
  654. free(devlist);
  655. devlist = next;
  656. }
  657. }
  658. static void
  659. url_cpy_or_cat(char * dst, const char * src, int n)
  660. {
  661. if( (src[0] == 'h')
  662. &&(src[1] == 't')
  663. &&(src[2] == 't')
  664. &&(src[3] == 'p')
  665. &&(src[4] == ':')
  666. &&(src[5] == '/')
  667. &&(src[6] == '/'))
  668. {
  669. strncpy(dst, src, n);
  670. }
  671. else
  672. {
  673. int l = strlen(dst);
  674. if(src[0] != '/')
  675. dst[l++] = '/';
  676. if(l<=n)
  677. strncpy(dst + l, src, n - l);
  678. }
  679. }
  680. /* Prepare the Urls for usage...
  681. */
  682. LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
  683. const char * descURL)
  684. {
  685. char * p;
  686. int n1, n2, n3, n4;
  687. n1 = strlen(data->urlbase);
  688. if(n1==0)
  689. n1 = strlen(descURL);
  690. n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
  691. n2 = n1; n3 = n1; n4 = n1;
  692. n1 += strlen(data->first.scpdurl);
  693. n2 += strlen(data->first.controlurl);
  694. n3 += strlen(data->CIF.controlurl);
  695. n4 += strlen(data->IPv6FC.controlurl);
  696. urls->ipcondescURL = (char *)malloc(n1);
  697. urls->controlURL = (char *)malloc(n2);
  698. urls->controlURL_CIF = (char *)malloc(n3);
  699. urls->controlURL_6FC = (char *)malloc(n4);
  700. /* maintenant on chope la desc du WANIPConnection */
  701. if(data->urlbase[0] != '\0')
  702. strncpy(urls->ipcondescURL, data->urlbase, n1);
  703. else
  704. strncpy(urls->ipcondescURL, descURL, n1);
  705. p = strchr(urls->ipcondescURL+7, '/');
  706. if(p) p[0] = '\0';
  707. strncpy(urls->controlURL, urls->ipcondescURL, n2);
  708. strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
  709. strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
  710. url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
  711. url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
  712. url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
  713. url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
  714. #ifdef DEBUG
  715. printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
  716. (unsigned)strlen(urls->ipcondescURL), n1);
  717. printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
  718. (unsigned)strlen(urls->controlURL), n2);
  719. printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
  720. (unsigned)strlen(urls->controlURL_CIF), n3);
  721. printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
  722. (unsigned)strlen(urls->controlURL_6FC), n4);
  723. #endif
  724. }
  725. LIBSPEC void
  726. FreeUPNPUrls(struct UPNPUrls * urls)
  727. {
  728. if(!urls)
  729. return;
  730. free(urls->controlURL);
  731. urls->controlURL = 0;
  732. free(urls->ipcondescURL);
  733. urls->ipcondescURL = 0;
  734. free(urls->controlURL_CIF);
  735. urls->controlURL_CIF = 0;
  736. free(urls->controlURL_6FC);
  737. urls->controlURL_6FC = 0;
  738. }
  739. int
  740. UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
  741. {
  742. char status[64];
  743. unsigned int uptime;
  744. status[0] = '\0';
  745. UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
  746. status, &uptime, NULL);
  747. if(0 == strcmp("Connected", status))
  748. {
  749. return 1;
  750. }
  751. else
  752. return 0;
  753. }
  754. /* UPNP_GetValidIGD() :
  755. * return values :
  756. * 0 = NO IGD found
  757. * 1 = A valid connected IGD has been found
  758. * 2 = A valid IGD has been found but it reported as
  759. * not connected
  760. * 3 = an UPnP device has been found but was not recognized as an IGD
  761. *
  762. * In any non zero return case, the urls and data structures
  763. * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
  764. * free allocated memory.
  765. */
  766. LIBSPEC int
  767. UPNP_GetValidIGD(struct UPNPDev * devlist,
  768. struct UPNPUrls * urls,
  769. struct IGDdatas * data,
  770. char * lanaddr, int lanaddrlen)
  771. {
  772. char * descXML;
  773. int descXMLsize = 0;
  774. struct UPNPDev * dev;
  775. int ndev = 0;
  776. int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
  777. if(!devlist)
  778. {
  779. #ifdef DEBUG
  780. printf("Empty devlist\n");
  781. #endif
  782. return 0;
  783. }
  784. for(state = 1; state <= 3; state++)
  785. {
  786. for(dev = devlist; dev; dev = dev->pNext)
  787. {
  788. /* we should choose an internet gateway device.
  789. * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
  790. descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
  791. lanaddr, lanaddrlen);
  792. if(descXML)
  793. {
  794. ndev++;
  795. memset(data, 0, sizeof(struct IGDdatas));
  796. memset(urls, 0, sizeof(struct UPNPUrls));
  797. parserootdesc(descXML, descXMLsize, data);
  798. free(descXML);
  799. descXML = NULL;
  800. if(0==strcmp(data->CIF.servicetype,
  801. "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
  802. || state >= 3 )
  803. {
  804. GetUPNPUrls(urls, data, dev->descURL);
  805. #ifdef DEBUG
  806. printf("UPNPIGD_IsConnected(%s) = %d\n",
  807. urls->controlURL,
  808. UPNPIGD_IsConnected(urls, data));
  809. #endif
  810. if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  811. return state;
  812. FreeUPNPUrls(urls);
  813. if(data->second.servicetype[0] != '\0') {
  814. #ifdef DEBUG
  815. printf("We tried %s, now we try %s !\n",
  816. data->first.servicetype, data->second.servicetype);
  817. #endif
  818. /* swaping WANPPPConnection and WANIPConnection ! */
  819. memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
  820. memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
  821. memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
  822. GetUPNPUrls(urls, data, dev->descURL);
  823. #ifdef DEBUG
  824. printf("UPNPIGD_IsConnected(%s) = %d\n",
  825. urls->controlURL,
  826. UPNPIGD_IsConnected(urls, data));
  827. #endif
  828. if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  829. return state;
  830. FreeUPNPUrls(urls);
  831. }
  832. }
  833. memset(data, 0, sizeof(struct IGDdatas));
  834. }
  835. #ifdef DEBUG
  836. else
  837. {
  838. printf("error getting XML description %s\n", dev->descURL);
  839. }
  840. #endif
  841. }
  842. }
  843. return 0;
  844. }
  845. /* UPNP_GetIGDFromUrl()
  846. * Used when skipping the discovery process.
  847. * return value :
  848. * 0 - Not ok
  849. * 1 - OK */
  850. int
  851. UPNP_GetIGDFromUrl(const char * rootdescurl,
  852. struct UPNPUrls * urls,
  853. struct IGDdatas * data,
  854. char * lanaddr, int lanaddrlen)
  855. {
  856. char * descXML;
  857. int descXMLsize = 0;
  858. descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
  859. lanaddr, lanaddrlen);
  860. if(descXML) {
  861. memset(data, 0, sizeof(struct IGDdatas));
  862. memset(urls, 0, sizeof(struct UPNPUrls));
  863. parserootdesc(descXML, descXMLsize, data);
  864. free(descXML);
  865. descXML = NULL;
  866. GetUPNPUrls(urls, data, rootdescurl);
  867. return 1;
  868. } else {
  869. return 0;
  870. }
  871. }