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