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 */
parserootdesc(const char * buffer,int bufsize,struct IGDdatas * data)78 MINIUPNP_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 */
100 static char *
simpleUPnPcommand2(SOCKET s,const char * url,const char * service,const char * action,struct UPNParg * args,int * bufsize,const char * httpversion)101 simpleUPnPcommand2(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 */
237 char *
simpleUPnPcommand(int s,const char * url,const char * service,const char * action,struct UPNParg * args,int * bufsize)238 simpleUPnPcommand(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. */
267 MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[],int delay,const char * multicastif,const char * minissdpdsock,int localport,int ipv6,unsigned char ttl,int * error,int searchalltypes)268 upnpDiscoverDevices(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 */
343 MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay,const char * multicastif,const char * minissdpdsock,int localport,int ipv6,unsigned char ttl,int * error)344 upnpDiscover(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 */
367 MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverAll(int delay,const char * multicastif,const char * minissdpdsock,int localport,int ipv6,unsigned char ttl,int * error)368 upnpDiscoverAll(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 */
384 MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevice(const char * device,int delay,const char * multicastif,const char * minissdpdsock,int localport,int ipv6,unsigned char ttl,int * error)385 upnpDiscoverDevice(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 
399 static char *
build_absolute_url(const char * baseurl,const char * descURL,const char * url,unsigned int scope_id)400 build_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  */
473 MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls,struct IGDdatas * data,const char * descURL,unsigned int scope_id)474 GetUPNPUrls(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 
498 MINIUPNP_LIBSPEC void
FreeUPNPUrls(struct UPNPUrls * urls)499 FreeUPNPUrls(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 
515 int
UPNPIGD_IsConnected(struct UPNPUrls * urls,struct IGDdatas * data)516 UPNPIGD_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  */
545 MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,struct UPNPUrls * urls,struct IGDdatas * data,char * lanaddr,int lanaddrlen)546 UPNP_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;
663 free_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 */
677 int
UPNP_GetIGDFromUrl(const char * rootdescurl,struct UPNPUrls * urls,struct IGDdatas * data,char * lanaddr,int lanaddrlen)678 UPNP_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