miniupnpc.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. /* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
  2. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  3. * Project : miniupnp
  4. * Web : http://miniupnp.free.fr/
  5. * Author : Thomas BERNARD
  6. * copyright (c) 2005-2018 Thomas Bernard
  7. * This software is subjet to the conditions detailed in the
  8. * provided LICENSE file. */
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #ifdef _WIN32
  13. /* Win32 Specific includes and defines */
  14. #include <winsock2.h>
  15. #include <ws2tcpip.h>
  16. #include <io.h>
  17. #include <iphlpapi.h>
  18. #define snprintf _snprintf
  19. #define strdup _strdup
  20. #ifndef strncasecmp
  21. #if defined(_MSC_VER) && (_MSC_VER >= 1400)
  22. #define strncasecmp _memicmp
  23. #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  24. #define strncasecmp memicmp
  25. #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  26. #endif /* #ifndef strncasecmp */
  27. #define MAXHOSTNAMELEN 64
  28. #else /* #ifdef _WIN32 */
  29. /* Standard POSIX includes */
  30. #include <unistd.h>
  31. #if defined(__amigaos__) && !defined(__amigaos4__)
  32. /* Amiga OS 3 specific stuff */
  33. #define socklen_t int
  34. #else
  35. #include <sys/select.h>
  36. #endif
  37. #include <sys/socket.h>
  38. #include <sys/types.h>
  39. #include <sys/param.h>
  40. #include <netinet/in.h>
  41. #include <arpa/inet.h>
  42. #include <netdb.h>
  43. #include <net/if.h>
  44. #if !defined(__amigaos__) && !defined(__amigaos4__)
  45. #include <poll.h>
  46. #endif
  47. #include <strings.h>
  48. #include <errno.h>
  49. #define closesocket close
  50. #endif /* #else _WIN32 */
  51. #ifdef __GNU__
  52. #define MAXHOSTNAMELEN 64
  53. #endif
  54. #include "miniupnpc.h"
  55. #include "minissdpc.h"
  56. #include "miniwget.h"
  57. #include "miniwget_private.h"
  58. #include "minisoap.h"
  59. #include "minixml.h"
  60. #include "upnpcommands.h"
  61. #include "connecthostport.h"
  62. /* compare the beginning of a string with a constant string */
  63. #define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
  64. #ifndef MAXHOSTNAMELEN
  65. #define MAXHOSTNAMELEN 64
  66. #endif
  67. #define SOAPPREFIX "s"
  68. #define SERVICEPREFIX "u"
  69. #define SERVICEPREFIX2 'u'
  70. /* check if an ip address is a private (LAN) address
  71. * see https://tools.ietf.org/html/rfc1918 */
  72. static int is_rfc1918addr(const char * addr)
  73. {
  74. /* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
  75. if(COMPARE(addr, "192.168."))
  76. return 1;
  77. /* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
  78. if(COMPARE(addr, "10."))
  79. return 1;
  80. /* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
  81. if(COMPARE(addr, "172.")) {
  82. int i = atoi(addr + 4);
  83. if((16 <= i) && (i <= 31))
  84. return 1;
  85. }
  86. return 0;
  87. }
  88. /* root description parsing */
  89. MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
  90. {
  91. struct xmlparser parser;
  92. /* xmlparser object */
  93. parser.xmlstart = buffer;
  94. parser.xmlsize = bufsize;
  95. parser.data = data;
  96. parser.starteltfunc = IGDstartelt;
  97. parser.endeltfunc = IGDendelt;
  98. parser.datafunc = IGDdata;
  99. parser.attfunc = 0;
  100. parsexml(&parser);
  101. #ifdef DEBUG
  102. printIGD(data);
  103. #endif
  104. }
  105. /* simpleUPnPcommand2 :
  106. * not so simple !
  107. * return values :
  108. * pointer - OK
  109. * NULL - error */
  110. static char *
  111. simpleUPnPcommand2(SOCKET s, const char * url, const char * service,
  112. const char * action, struct UPNParg * args,
  113. int * bufsize, const char * httpversion)
  114. {
  115. char hostname[MAXHOSTNAMELEN+1];
  116. unsigned short port = 0;
  117. char * path;
  118. char soapact[128];
  119. char soapbody[2048];
  120. int soapbodylen;
  121. char * buf;
  122. int n;
  123. int status_code;
  124. *bufsize = 0;
  125. snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
  126. if(args==NULL)
  127. {
  128. soapbodylen = snprintf(soapbody, sizeof(soapbody),
  129. "<?xml version=\"1.0\"?>\r\n"
  130. "<" SOAPPREFIX ":Envelope "
  131. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  132. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  133. "<" SOAPPREFIX ":Body>"
  134. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
  135. "</" SERVICEPREFIX ":%s>"
  136. "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
  137. "\r\n", action, service, action);
  138. if ((unsigned int)soapbodylen >= sizeof(soapbody))
  139. return NULL;
  140. }
  141. else
  142. {
  143. char * p;
  144. const char * pe, * pv;
  145. const char * const pend = soapbody + sizeof(soapbody);
  146. soapbodylen = snprintf(soapbody, sizeof(soapbody),
  147. "<?xml version=\"1.0\"?>\r\n"
  148. "<" SOAPPREFIX ":Envelope "
  149. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  150. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  151. "<" SOAPPREFIX ":Body>"
  152. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
  153. action, service);
  154. if ((unsigned int)soapbodylen >= sizeof(soapbody))
  155. return NULL;
  156. p = soapbody + soapbodylen;
  157. while(args->elt)
  158. {
  159. if(p >= pend) /* check for space to write next byte */
  160. return NULL;
  161. *(p++) = '<';
  162. pe = args->elt;
  163. while(p < pend && *pe)
  164. *(p++) = *(pe++);
  165. if(p >= pend) /* check for space to write next byte */
  166. return NULL;
  167. *(p++) = '>';
  168. if((pv = args->val))
  169. {
  170. while(p < pend && *pv)
  171. *(p++) = *(pv++);
  172. }
  173. if((p+2) > pend) /* check for space to write next 2 bytes */
  174. return NULL;
  175. *(p++) = '<';
  176. *(p++) = '/';
  177. pe = args->elt;
  178. while(p < pend && *pe)
  179. *(p++) = *(pe++);
  180. if(p >= pend) /* check for space to write next byte */
  181. return NULL;
  182. *(p++) = '>';
  183. args++;
  184. }
  185. if((p+4) > pend) /* check for space to write next 4 bytes */
  186. return NULL;
  187. *(p++) = '<';
  188. *(p++) = '/';
  189. *(p++) = SERVICEPREFIX2;
  190. *(p++) = ':';
  191. pe = action;
  192. while(p < pend && *pe)
  193. *(p++) = *(pe++);
  194. strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
  195. pend - p);
  196. if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
  197. return NULL;
  198. }
  199. if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
  200. if(ISINVALID(s)) {
  201. s = connecthostport(hostname, port, 0);
  202. if(ISINVALID(s)) {
  203. /* failed to connect */
  204. return NULL;
  205. }
  206. }
  207. n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
  208. if(n<=0) {
  209. #ifdef DEBUG
  210. printf("Error sending SOAP request\n");
  211. #endif
  212. closesocket(s);
  213. return NULL;
  214. }
  215. buf = getHTTPResponse(s, bufsize, &status_code);
  216. #ifdef DEBUG
  217. if(*bufsize > 0 && buf)
  218. {
  219. printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
  220. }
  221. else
  222. {
  223. printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
  224. }
  225. #endif
  226. closesocket(s);
  227. return buf;
  228. }
  229. /* simpleUPnPcommand :
  230. * not so simple !
  231. * return values :
  232. * pointer - OK
  233. * NULL - error */
  234. char *
  235. simpleUPnPcommand(int s, const char * url, const char * service,
  236. const char * action, struct UPNParg * args,
  237. int * bufsize)
  238. {
  239. char * buf;
  240. #if 1
  241. buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
  242. #else
  243. buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.0");
  244. if (!buf || *bufsize == 0)
  245. {
  246. #if DEBUG
  247. printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
  248. #endif
  249. buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
  250. }
  251. #endif
  252. return buf;
  253. }
  254. /* upnpDiscoverDevices() :
  255. * return a chained list of all devices found or NULL if
  256. * no devices was found.
  257. * It is up to the caller to free the chained list
  258. * delay is in millisecond (poll).
  259. * UDA v1.1 says :
  260. * The TTL for the IP packet SHOULD default to 2 and
  261. * SHOULD be configurable. */
  262. MINIUPNP_LIBSPEC struct UPNPDev *
  263. upnpDiscoverDevices(const char * const deviceTypes[],
  264. int delay, const char * multicastif,
  265. const char * minissdpdsock, int localport,
  266. int ipv6, unsigned char ttl,
  267. int * error,
  268. int searchalltypes)
  269. {
  270. struct UPNPDev * tmp;
  271. struct UPNPDev * devlist = 0;
  272. #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  273. int deviceIndex;
  274. #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
  275. if(error)
  276. *error = UPNPDISCOVER_UNKNOWN_ERROR;
  277. #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  278. /* first try to get infos from minissdpd ! */
  279. if(!minissdpdsock)
  280. minissdpdsock = "/var/run/minissdpd.sock";
  281. if(minissdpdsock[0] != '\0') {
  282. for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
  283. struct UPNPDev * minissdpd_devlist;
  284. int only_rootdevice = 1;
  285. minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
  286. minissdpdsock, 0);
  287. if(minissdpd_devlist) {
  288. #ifdef DEBUG
  289. printf("returned by MiniSSDPD: %s\t%s\n",
  290. minissdpd_devlist->st, minissdpd_devlist->descURL);
  291. #endif /* DEBUG */
  292. if(!strstr(minissdpd_devlist->st, "rootdevice"))
  293. only_rootdevice = 0;
  294. for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
  295. #ifdef DEBUG
  296. printf("returned by MiniSSDPD: %s\t%s\n",
  297. tmp->pNext->st, tmp->pNext->descURL);
  298. #endif /* DEBUG */
  299. if(!strstr(tmp->st, "rootdevice"))
  300. only_rootdevice = 0;
  301. }
  302. tmp->pNext = devlist;
  303. devlist = minissdpd_devlist;
  304. if(!searchalltypes && !only_rootdevice)
  305. break;
  306. }
  307. }
  308. }
  309. for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
  310. /* We return what we have found if it was not only a rootdevice */
  311. if(!strstr(tmp->st, "rootdevice")) {
  312. if(error)
  313. *error = UPNPDISCOVER_SUCCESS;
  314. return devlist;
  315. }
  316. }
  317. #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
  318. /* direct discovery if minissdpd responses are not sufficient */
  319. {
  320. struct UPNPDev * discovered_devlist;
  321. discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
  322. ipv6, ttl, error, searchalltypes);
  323. if(devlist == NULL)
  324. devlist = discovered_devlist;
  325. else {
  326. for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
  327. tmp->pNext = discovered_devlist;
  328. }
  329. }
  330. return devlist;
  331. }
  332. /* upnpDiscover() Discover IGD device */
  333. MINIUPNP_LIBSPEC struct UPNPDev *
  334. upnpDiscover(int delay, const char * multicastif,
  335. const char * minissdpdsock, int localport,
  336. int ipv6, unsigned char ttl,
  337. int * error)
  338. {
  339. static const char * const deviceList[] = {
  340. #if 0
  341. "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
  342. "urn:schemas-upnp-org:service:WANIPConnection:2",
  343. #endif
  344. "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
  345. "urn:schemas-upnp-org:service:WANIPConnection:1",
  346. "urn:schemas-upnp-org:service:WANPPPConnection:1",
  347. "upnp:rootdevice",
  348. /*"ssdp:all",*/
  349. 0
  350. };
  351. return upnpDiscoverDevices(deviceList,
  352. delay, multicastif, minissdpdsock, localport,
  353. ipv6, ttl, error, 0);
  354. }
  355. /* upnpDiscoverAll() Discover all UPnP devices */
  356. MINIUPNP_LIBSPEC struct UPNPDev *
  357. upnpDiscoverAll(int delay, const char * multicastif,
  358. const char * minissdpdsock, int localport,
  359. int ipv6, unsigned char ttl,
  360. int * error)
  361. {
  362. static const char * const deviceList[] = {
  363. /*"upnp:rootdevice",*/
  364. "ssdp:all",
  365. 0
  366. };
  367. return upnpDiscoverDevices(deviceList,
  368. delay, multicastif, minissdpdsock, localport,
  369. ipv6, ttl, error, 0);
  370. }
  371. /* upnpDiscoverDevice() Discover a specific device */
  372. MINIUPNP_LIBSPEC struct UPNPDev *
  373. upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
  374. const char * minissdpdsock, int localport,
  375. int ipv6, unsigned char ttl,
  376. int * error)
  377. {
  378. const char * const deviceList[] = {
  379. device,
  380. 0
  381. };
  382. return upnpDiscoverDevices(deviceList,
  383. delay, multicastif, minissdpdsock, localport,
  384. ipv6, ttl, error, 0);
  385. }
  386. static char *
  387. build_absolute_url(const char * baseurl, const char * descURL,
  388. const char * url, unsigned int scope_id)
  389. {
  390. int l, n;
  391. char * s;
  392. const char * base;
  393. char * p;
  394. #if defined(IF_NAMESIZE) && !defined(_WIN32)
  395. char ifname[IF_NAMESIZE];
  396. #else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  397. char scope_str[8];
  398. #endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  399. if( (url[0] == 'h')
  400. &&(url[1] == 't')
  401. &&(url[2] == 't')
  402. &&(url[3] == 'p')
  403. &&(url[4] == ':')
  404. &&(url[5] == '/')
  405. &&(url[6] == '/'))
  406. return strdup(url);
  407. base = (baseurl[0] == '\0') ? descURL : baseurl;
  408. n = strlen(base);
  409. if(n > 7) {
  410. p = strchr(base + 7, '/');
  411. if(p)
  412. n = p - base;
  413. }
  414. l = n + strlen(url) + 1;
  415. if(url[0] != '/')
  416. l++;
  417. if(scope_id != 0) {
  418. #if defined(IF_NAMESIZE) && !defined(_WIN32)
  419. if(if_indextoname(scope_id, ifname)) {
  420. l += 3 + strlen(ifname); /* 3 == strlen(%25) */
  421. }
  422. #else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  423. /* under windows, scope is numerical */
  424. l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
  425. #endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  426. }
  427. s = malloc(l);
  428. if(s == NULL) return NULL;
  429. memcpy(s, base, n);
  430. if(scope_id != 0) {
  431. s[n] = '\0';
  432. if(0 == memcmp(s, "http://[fe80:", 13)) {
  433. /* this is a linklocal IPv6 address */
  434. p = strchr(s, ']');
  435. if(p) {
  436. /* insert %25<scope> into URL */
  437. #if defined(IF_NAMESIZE) && !defined(_WIN32)
  438. memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
  439. memcpy(p, "%25", 3);
  440. memcpy(p + 3, ifname, strlen(ifname));
  441. n += 3 + strlen(ifname);
  442. #else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  443. memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
  444. memcpy(p, "%25", 3);
  445. memcpy(p + 3, scope_str, strlen(scope_str));
  446. n += 3 + strlen(scope_str);
  447. #endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
  448. }
  449. }
  450. }
  451. if(url[0] != '/')
  452. s[n++] = '/';
  453. memcpy(s + n, url, l - n);
  454. return s;
  455. }
  456. /* Prepare the Urls for usage...
  457. */
  458. MINIUPNP_LIBSPEC void
  459. GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
  460. const char * descURL, unsigned int scope_id)
  461. {
  462. /* strdup descURL */
  463. urls->rootdescURL = strdup(descURL);
  464. /* get description of WANIPConnection */
  465. urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
  466. data->first.scpdurl, scope_id);
  467. urls->controlURL = build_absolute_url(data->urlbase, descURL,
  468. data->first.controlurl, scope_id);
  469. urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
  470. data->CIF.controlurl, scope_id);
  471. urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
  472. data->IPv6FC.controlurl, scope_id);
  473. #ifdef DEBUG
  474. printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
  475. printf("urls->controlURL='%s'\n", urls->controlURL);
  476. printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
  477. printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
  478. #endif
  479. }
  480. MINIUPNP_LIBSPEC void
  481. FreeUPNPUrls(struct UPNPUrls * urls)
  482. {
  483. if(!urls)
  484. return;
  485. free(urls->controlURL);
  486. urls->controlURL = 0;
  487. free(urls->ipcondescURL);
  488. urls->ipcondescURL = 0;
  489. free(urls->controlURL_CIF);
  490. urls->controlURL_CIF = 0;
  491. free(urls->controlURL_6FC);
  492. urls->controlURL_6FC = 0;
  493. free(urls->rootdescURL);
  494. urls->rootdescURL = 0;
  495. }
  496. int
  497. UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
  498. {
  499. char status[64];
  500. unsigned int uptime;
  501. status[0] = '\0';
  502. UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
  503. status, &uptime, NULL);
  504. if(0 == strcmp("Connected", status))
  505. return 1;
  506. else if(0 == strcmp("Up", status)) /* Also accept "Up" */
  507. return 1;
  508. else
  509. return 0;
  510. }
  511. /* UPNP_GetValidIGD() :
  512. * return values :
  513. * -1 = Internal error
  514. * 0 = NO IGD found
  515. * 1 = A valid connected IGD has been found
  516. * 2 = A valid IGD has been found but it reported as
  517. * not connected
  518. * 3 = an UPnP device has been found but was not recognized as an IGD
  519. *
  520. * In any positive non zero return case, the urls and data structures
  521. * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to
  522. * free allocated memory.
  523. */
  524. MINIUPNP_LIBSPEC int
  525. UPNP_GetValidIGD(struct UPNPDev * devlist,
  526. struct UPNPUrls * urls,
  527. struct IGDdatas * data,
  528. char * lanaddr, int lanaddrlen)
  529. {
  530. struct xml_desc {
  531. char * xml;
  532. int size;
  533. int is_igd;
  534. } * desc = NULL;
  535. struct UPNPDev * dev;
  536. int ndev = 0;
  537. int i;
  538. int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
  539. int n_igd = 0;
  540. char extIpAddr[16];
  541. char myLanAddr[40];
  542. int status_code = -1;
  543. if(!devlist)
  544. {
  545. #ifdef DEBUG
  546. printf("Empty devlist\n");
  547. #endif
  548. return 0;
  549. }
  550. /* counting total number of devices in the list */
  551. for(dev = devlist; dev; dev = dev->pNext)
  552. ndev++;
  553. if(ndev > 0)
  554. {
  555. desc = calloc(ndev, sizeof(struct xml_desc));
  556. if(!desc)
  557. return -1; /* memory allocation error */
  558. }
  559. /* Step 1 : downloading descriptions and testing type */
  560. for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
  561. {
  562. /* we should choose an internet gateway device.
  563. * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
  564. desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
  565. myLanAddr, sizeof(myLanAddr),
  566. dev->scope_id, &status_code);
  567. #ifdef DEBUG
  568. if(!desc[i].xml)
  569. {
  570. printf("error getting XML description %s\n", dev->descURL);
  571. }
  572. #endif
  573. if(desc[i].xml)
  574. {
  575. memset(data, 0, sizeof(struct IGDdatas));
  576. memset(urls, 0, sizeof(struct UPNPUrls));
  577. parserootdesc(desc[i].xml, desc[i].size, data);
  578. if(COMPARE(data->CIF.servicetype,
  579. "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
  580. {
  581. desc[i].is_igd = 1;
  582. n_igd++;
  583. if(lanaddr)
  584. strncpy(lanaddr, myLanAddr, lanaddrlen);
  585. }
  586. }
  587. }
  588. /* iterate the list to find a device depending on state */
  589. for(state = 1; state <= 3; state++)
  590. {
  591. for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
  592. {
  593. if(desc[i].xml)
  594. {
  595. memset(data, 0, sizeof(struct IGDdatas));
  596. memset(urls, 0, sizeof(struct UPNPUrls));
  597. parserootdesc(desc[i].xml, desc[i].size, data);
  598. if(desc[i].is_igd || state >= 3 )
  599. {
  600. int is_connected;
  601. GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
  602. /* in state 2 and 3 we don't test if device is connected ! */
  603. if(state >= 2)
  604. goto free_and_return;
  605. is_connected = UPNPIGD_IsConnected(urls, data);
  606. #ifdef DEBUG
  607. printf("UPNPIGD_IsConnected(%s) = %d\n",
  608. urls->controlURL, is_connected);
  609. #endif
  610. /* checks that status is connected AND there is a external IP address assigned */
  611. if(is_connected &&
  612. (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
  613. if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
  614. && (0 != strcmp(extIpAddr, "0.0.0.0")))
  615. goto free_and_return;
  616. }
  617. FreeUPNPUrls(urls);
  618. if(data->second.servicetype[0] != '\0') {
  619. #ifdef DEBUG
  620. printf("We tried %s, now we try %s !\n",
  621. data->first.servicetype, data->second.servicetype);
  622. #endif
  623. /* swaping WANPPPConnection and WANIPConnection ! */
  624. memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
  625. memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
  626. memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
  627. GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
  628. is_connected = UPNPIGD_IsConnected(urls, data);
  629. #ifdef DEBUG
  630. printf("UPNPIGD_IsConnected(%s) = %d\n",
  631. urls->controlURL, is_connected);
  632. #endif
  633. if(is_connected &&
  634. (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
  635. if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
  636. && (0 != strcmp(extIpAddr, "0.0.0.0")))
  637. goto free_and_return;
  638. }
  639. FreeUPNPUrls(urls);
  640. }
  641. }
  642. memset(data, 0, sizeof(struct IGDdatas));
  643. }
  644. }
  645. }
  646. state = 0;
  647. free_and_return:
  648. if(desc) {
  649. for(i = 0; i < ndev; i++) {
  650. if(desc[i].xml) {
  651. free(desc[i].xml);
  652. }
  653. }
  654. free(desc);
  655. }
  656. return state;
  657. }
  658. /* UPNP_GetIGDFromUrl()
  659. * Used when skipping the discovery process.
  660. * return value :
  661. * 0 - Not ok
  662. * 1 - OK */
  663. int
  664. UPNP_GetIGDFromUrl(const char * rootdescurl,
  665. struct UPNPUrls * urls,
  666. struct IGDdatas * data,
  667. char * lanaddr, int lanaddrlen)
  668. {
  669. char * descXML;
  670. int descXMLsize = 0;
  671. descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
  672. lanaddr, lanaddrlen, 0, NULL);
  673. if(descXML) {
  674. memset(data, 0, sizeof(struct IGDdatas));
  675. memset(urls, 0, sizeof(struct UPNPUrls));
  676. parserootdesc(descXML, descXMLsize, data);
  677. free(descXML);
  678. descXML = NULL;
  679. GetUPNPUrls(urls, data, rootdescurl, 0);
  680. return 1;
  681. } else {
  682. return 0;
  683. }
  684. }