1/* $Id: miniupnpc.c,v 1.123 2014/11/27 12:02:25 nanard Exp $ */
2/* Project : miniupnp
3 * Web : http://miniupnp.free.fr/
4 * Author : Thomas BERNARD
5 * copyright (c) 2005-2014 Thomas Bernard
6 * This software is subjet to the conditions detailed in the
7 * provided LICENSE file. */
8#define __EXTENSIONS__ 1
9#if !defined(__APPLE__) && !defined(__sun)
10#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
11#ifndef __cplusplus
12#define _XOPEN_SOURCE 600
13#endif
14#endif
15#ifndef __BSD_VISIBLE
16#define __BSD_VISIBLE 1
17#endif
18#endif
19
20#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
21#define HAS_IP_MREQN
22#endif
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#ifdef _WIN32
28/* Win32 Specific includes and defines */
29#include <winsock2.h>
30#include <ws2tcpip.h>
31#include <io.h>
32#include <iphlpapi.h>
33#define snprintf _snprintf
34#define strdup _strdup
35#ifndef strncasecmp
36#if defined(_MSC_VER) && (_MSC_VER >= 1400)
37#define strncasecmp _memicmp
38#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
39#define strncasecmp memicmp
40#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
41#endif /* #ifndef strncasecmp */
42#define MAXHOSTNAMELEN 64
43#else /* #ifdef _WIN32 */
44/* Standard POSIX includes */
45#include <unistd.h>
46#if defined(__amigaos__) && !defined(__amigaos4__)
47/* Amiga OS 3 specific stuff */
48#define socklen_t int
49#else
50#include <sys/select.h>
51#endif
52#include <syslog.h>
53#include <sys/ioctl.h>
54#include <sys/socket.h>
55#include <sys/types.h>
56#include <sys/param.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include <netdb.h>
60#include <net/if.h>
61#if !defined(__amigaos__) && !defined(__amigaos4__)
62#include <poll.h>
63#endif
64#include <strings.h>
65#include <errno.h>
66#define closesocket close
67#endif /* #else _WIN32 */
68#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
69#include <sys/time.h>
70#endif
71#if defined(__amigaos__) || defined(__amigaos4__)
72/* Amiga OS specific stuff */
73#define TIMEVAL struct timeval
74#endif
75#ifdef __GNU__
76#define MAXHOSTNAMELEN 64
77#endif
78
79#include <shared.h>
80
81#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
82/* Several versions of glibc don't define this structure, define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
83struct ip_mreqn
84{
85	struct in_addr	imr_multiaddr;		/* IP multicast address of group */
86	struct in_addr	imr_address;		/* local IP address of interface */
87	int		imr_ifindex;		/* Interface index */
88};
89#endif
90
91#include "miniupnpc.h"
92#include "minissdpc.h"
93#include "miniwget.h"
94#include "minisoap.h"
95#include "minixml.h"
96#include "upnpcommands.h"
97#include "connecthostport.h"
98#include "receivedata.h"
99
100/* compare the begining of a string with a constant string */
101#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
102
103#ifdef _WIN32
104#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
105#else
106#define PRINT_SOCKET_ERROR(x) perror(x)
107#endif
108
109#ifndef MAXHOSTNAMELEN
110#define MAXHOSTNAMELEN 64
111#endif
112
113#define SOAPPREFIX "s"
114#define SERVICEPREFIX "u"
115#define SERVICEPREFIX2 'u'
116
117
118/* root description parsing */
119MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
120{
121	struct xmlparser parser;
122	/* xmlparser object */
123	parser.xmlstart = buffer;
124	parser.xmlsize = bufsize;
125	parser.data = data;
126	parser.starteltfunc = IGDstartelt;
127	parser.endeltfunc = IGDendelt;
128	parser.datafunc = IGDdata;
129	parser.attfunc = 0;
130	parsexml(&parser);
131#ifdef DEBUG
132/*
133	printIGD(data);
134*/
135#endif
136}
137
138/* simpleUPnPcommand2 :
139 * not so simple !
140 * return values :
141 *   pointer - OK
142 *   NULL - error */
143char * simpleUPnPcommand2(int s, const char * url, const char * service,
144		       const char * action, struct UPNParg * args,
145		       int * bufsize, const char * httpversion)
146{
147	char hostname[MAXHOSTNAMELEN+1];
148	unsigned short port = 0;
149	char * path;
150	char soapact[128];
151	char soapbody[2048];
152	char * buf;
153    int n;
154
155	*bufsize = 0;
156	snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
157	if(args==NULL)
158	{
159		/*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
160						"<?xml version=\"1.0\"?>\r\n"
161	    	              "<" SOAPPREFIX ":Envelope "
162						  "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
163						  SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
164						  "<" SOAPPREFIX ":Body>"
165						  "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
166						  "</" SERVICEPREFIX ":%s>"
167						  "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
168					 	  "\r\n", action, service, action);
169	}
170	else
171	{
172		char * p;
173		const char * pe, * pv;
174		int soapbodylen;
175		soapbodylen = snprintf(soapbody, sizeof(soapbody),
176						"<?xml version=\"1.0\"?>\r\n"
177	    	            "<" SOAPPREFIX ":Envelope "
178						"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
179						SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
180						"<" SOAPPREFIX ":Body>"
181						"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
182						action, service);
183		p = soapbody + soapbodylen;
184		while(args->elt)
185		{
186			/* check that we are never overflowing the string... */
187			if(soapbody + sizeof(soapbody) <= p + 100)
188			{
189				/* we keep a margin of at least 100 bytes */
190				return NULL;
191			}
192			*(p++) = '<';
193			pe = args->elt;
194			while(*pe)
195				*(p++) = *(pe++);
196			*(p++) = '>';
197			if((pv = args->val))
198			{
199				while(*pv)
200					*(p++) = *(pv++);
201			}
202			*(p++) = '<';
203			*(p++) = '/';
204			pe = args->elt;
205			while(*pe)
206				*(p++) = *(pe++);
207			*(p++) = '>';
208			args++;
209		}
210		*(p++) = '<';
211		*(p++) = '/';
212		*(p++) = SERVICEPREFIX2;
213		*(p++) = ':';
214		pe = action;
215		while(*pe)
216			*(p++) = *(pe++);
217		strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
218		        soapbody + sizeof(soapbody) - p);
219	}
220	if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
221	if(s < 0) {
222		s = connecthostport(hostname, port, 0);
223		if(s < 0) {
224			/* failed to connect */
225			return NULL;
226		}
227	}
228
229	n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
230	if(n<=0) {
231#ifdef DEBUG
232		printf("Error sending SOAP request\n");
233#endif
234		closesocket(s);
235		return NULL;
236	}
237
238	buf = getHTTPResponse(s, bufsize, NULL);
239#ifdef DEBUG
240	if(*bufsize > 0 && buf)
241	{
242		printf("SOAP Response :\n%.*s\n", *bufsize, buf);
243	}
244#endif
245	closesocket(s);
246	return buf;
247}
248
249/* simpleUPnPcommand :
250 * not so simple !
251 * return values :
252 *   pointer - OK
253 *   NULL    - error */
254char * simpleUPnPcommand(int s, const char * url, const char * service,
255		       const char * action, struct UPNParg * args,
256		       int * bufsize)
257{
258	char * buf;
259
260#if 1
261	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
262#else
263	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
264	if (!buf || *bufsize == 0)
265	{
266#if DEBUG
267	    printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
268#endif
269		buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
270	}
271#endif
272	return buf;
273}
274
275/* parseMSEARCHReply()
276 * the last 4 arguments are filled during the parsing :
277 *    - location/locationsize : "location:" field of the SSDP reply packet
278 *    - st/stsize : "st:" field of the SSDP reply packet.
279 * The strings are NOT null terminated */
280static void
281parseMSEARCHReply(const char * reply, int size,
282                  const char * * location, int * locationsize,
283			      const char * * st, int * stsize)
284{
285	int a, b, i;
286	i = 0;
287	a = i;	/* start of the line */
288	b = 0;	/* end of the "header" (position of the colon) */
289	while(i<size)
290	{
291		switch(reply[i])
292		{
293		case ':':
294				if(b==0)
295				{
296					b = i; /* end of the "header" */
297					/*for(j=a; j<b; j++)
298					{
299						putchar(reply[j]);
300					}
301					*/
302				}
303				break;
304		case '\x0a':
305		case '\x0d':
306				if(b!=0)
307				{
308					/*for(j=b+1; j<i; j++)
309					{
310						putchar(reply[j]);
311					}
312					putchar('\n');*/
313					/* skip the colon and white spaces */
314					do { b++; } while(reply[b]==' ');
315					if(0==strncasecmp(reply+a, "location", 8))
316					{
317						*location = reply+b;
318						*locationsize = i-b;
319					}
320					else if(0==strncasecmp(reply+a, "st", 2))
321					{
322						*st = reply+b;
323						*stsize = i-b;
324					}
325					b = 0;
326				}
327				a = i+1;
328				break;
329		default:
330				break;
331		}
332		i++;
333	}
334}
335
336/* port upnp discover : SSDP protocol */
337#define PORT 1900
338#define XSTR(s) STR(s)
339#define STR(s) #s
340#define UPNP_MCAST_ADDR "239.255.255.250"
341/* for IPv6 */
342#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
343#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
344
345/* upnpDiscoverDevices() :
346 * return a chained list of all devices found or NULL if
347 * no devices was found.
348 * It is up to the caller to free the chained list
349 * delay is in millisecond (poll) */
350MINIUPNP_LIBSPEC struct UPNPDev *
351upnpDiscoverDevices(const char * const deviceTypes[],
352                    int delay, const char * multicastif,
353                    const char * minissdpdsock, int sameport,
354                    int ipv6,
355                    int * error)
356{
357	struct UPNPDev * tmp;
358	struct UPNPDev * devlist = 0;
359	unsigned int scope_id = 0;
360	int opt = 1;
361	static const char MSearchMsgFmt[] =
362	"M-SEARCH * HTTP/1.1\r\n"
363	"HOST: %s:" XSTR(PORT) "\r\n"
364	"ST: %s\r\n"
365	"MAN: \"ssdp:discover\"\r\n"
366	"MX: %u\r\n"
367	"\r\n";
368	int deviceIndex;
369	char bufr[1536];	/* reception and emission buffer */
370	int sudp;
371	int n;
372	struct sockaddr_storage sockudp_r;
373	unsigned int mx;
374#ifdef NO_GETADDRINFO
375	struct sockaddr_storage sockudp_w;
376#else
377	int rv;
378	struct addrinfo hints, *servinfo, *p;
379#endif
380#ifdef _WIN32
381	MIB_IPFORWARDROW ip_forward;
382#endif
383	int linklocal = 1;
384
385	if(error)
386		*error = UPNPDISCOVER_UNKNOWN_ERROR;
387#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
388	/* first try to get infos from minissdpd ! */
389	if(!minissdpdsock)
390		minissdpdsock = "/var/run/minissdpd.sock";
391	for(deviceIndex = 0; !devlist && deviceTypes[deviceIndex]; deviceIndex++) {
392		devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
393		                                  minissdpdsock);
394		/* We return what we have found if it was not only a rootdevice */
395		if(devlist && !strstr(deviceTypes[deviceIndex], "rootdevice")) {
396			if(error)
397				*error = UPNPDISCOVER_SUCCESS;
398			return devlist;
399		}
400	}
401#endif
402	/* fallback to direct discovery */
403#ifdef _WIN32
404	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
405#else
406	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
407#endif
408	if(sudp < 0)
409	{
410		if(error)
411			*error = UPNPDISCOVER_SOCKET_ERROR;
412		PRINT_SOCKET_ERROR("socket");
413		return NULL;
414	}
415
416	/* reception */
417	memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
418	if(ipv6) {
419		struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
420		p->sin6_family = AF_INET6;
421		if(sameport)
422			p->sin6_port = htons(PORT);
423		p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
424	} else {
425		struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
426		p->sin_family = AF_INET;
427		if(sameport)
428			p->sin_port = htons(PORT);
429		p->sin_addr.s_addr = INADDR_ANY;
430	}
431#ifdef _WIN32
432/* This code could help us to use the right Network interface for
433 * SSDP multicast traffic */
434/* Get IP associated with the index given in the ip_forward struct
435 * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
436	if(!ipv6
437	   && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
438		DWORD dwRetVal = 0;
439		PMIB_IPADDRTABLE pIPAddrTable;
440		DWORD dwSize = 0;
441#ifdef DEBUG
442		IN_ADDR IPAddr;
443#endif
444		int i;
445#ifdef DEBUG
446		printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
447#endif
448		pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
449		if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
450			free(pIPAddrTable);
451			pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
452		}
453		if(pIPAddrTable) {
454			dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
455#ifdef DEBUG
456			printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
457#endif
458			for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
459#ifdef DEBUG
460				printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
461				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
462				printf("\tIP Address[%d]:     \t%s\n", i, inet_ntoa(IPAddr) );
463				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
464				printf("\tSubnet Mask[%d]:    \t%s\n", i, inet_ntoa(IPAddr) );
465				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
466				printf("\tBroadCast[%d]:      \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
467				printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
468				printf("\tType and State[%d]:", i);
469				printf("\n");
470#endif
471				if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
472					/* Set the address of this interface to be used */
473					struct in_addr mc_if;
474					memset(&mc_if, 0, sizeof(mc_if));
475					mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
476					if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
477						PRINT_SOCKET_ERROR("setsockopt");
478					}
479					((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
480#ifndef DEBUG
481					break;
482#endif
483				}
484			}
485			free(pIPAddrTable);
486			pIPAddrTable = NULL;
487		}
488	}
489#endif
490
491#ifdef _WIN32
492	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
493#else
494	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
495#endif
496	{
497		if(error)
498			*error = UPNPDISCOVER_SOCKET_ERROR;
499		PRINT_SOCKET_ERROR("setsockopt");
500		return NULL;
501	}
502
503	if(multicastif)
504	{
505		if(ipv6) {
506#if !defined(_WIN32)
507			/* according to MSDN, if_nametoindex() is supported since
508			 * MS Windows Vista and MS Windows Server 2008.
509			 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
510			unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
511			if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
512			{
513				PRINT_SOCKET_ERROR("setsockopt");
514			}
515#else
516#ifdef DEBUG
517			printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
518#endif
519#endif
520		} else {
521			struct in_addr mc_if;
522			mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
523			if(mc_if.s_addr != INADDR_NONE)
524			{
525				((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
526				if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
527				{
528					PRINT_SOCKET_ERROR("setsockopt");
529				}
530			} else {
531#ifdef HAS_IP_MREQN
532				/* was not an ip address, try with an interface name */
533				struct ip_mreqn reqn;	/* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
534				memset(&reqn, 0, sizeof(struct ip_mreqn));
535				reqn.imr_ifindex = if_nametoindex(multicastif);
536				if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
537				{
538					PRINT_SOCKET_ERROR("setsockopt");
539				}
540#else
541#ifdef DEBUG
542				printf("Setting of multicast interface not supported with interface name.\n");
543#endif
544#endif
545			}
546		}
547	}
548
549	/* Before sending the packed, we first "bind" in order to be able
550	 * to receive the response */
551	if (bind(sudp, (const struct sockaddr *)&sockudp_r,
552	         ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
553	{
554		if(error)
555			*error = UPNPDISCOVER_SOCKET_ERROR;
556		PRINT_SOCKET_ERROR("bind");
557		closesocket(sudp);
558		return NULL;
559	}
560
561	if(error)
562		*error = UPNPDISCOVER_SUCCESS;
563	/* Calculating maximum response time in seconds */
564	mx = ((unsigned int)delay) / 1000u;
565	if(mx == 0) {
566		mx = 1;
567		delay = 1000;
568	}
569	/* receiving SSDP response packet */
570	for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
571		/* sending the SSDP M-SEARCH packet */
572		n = snprintf(bufr, sizeof(bufr),
573		             MSearchMsgFmt,
574		             ipv6 ?
575		             (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
576		             : UPNP_MCAST_ADDR,
577		             deviceTypes[deviceIndex], mx);
578#ifdef DEBUG
579		/*printf("Sending %s", bufr);*/
580		printf("Sending M-SEARCH request to %s with ST: %s\n",
581		       ipv6 ?
582		       (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
583		       : UPNP_MCAST_ADDR,
584		       deviceTypes[deviceIndex]);
585#endif
586#ifdef NO_GETADDRINFO
587		/* the following code is not using getaddrinfo */
588		/* emission */
589		memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
590		if(ipv6) {
591			struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
592			p->sin6_family = AF_INET6;
593			p->sin6_port = htons(PORT);
594			inet_pton(AF_INET6,
595			          linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
596			          &(p->sin6_addr));
597		} else {
598			struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
599			p->sin_family = AF_INET;
600			p->sin_port = htons(PORT);
601			p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
602		}
603		n = sendto(sudp, bufr, n, 0, &sockudp_w,
604		           ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
605		if (n < 0) {
606			if(error)
607				*error = UPNPDISCOVER_SOCKET_ERROR;
608			PRINT_SOCKET_ERROR("sendto");
609			break;
610		}
611#else /* #ifdef NO_GETADDRINFO */
612		memset(&hints, 0, sizeof(hints));
613		hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
614		hints.ai_socktype = SOCK_DGRAM;
615		/*hints.ai_flags = */
616		if ((rv = getaddrinfo(ipv6
617		                      ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
618		                      : UPNP_MCAST_ADDR,
619		                      XSTR(PORT), &hints, &servinfo)) != 0) {
620			if(error)
621				*error = UPNPDISCOVER_SOCKET_ERROR;
622#ifdef _WIN32
623			fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
624#else
625			fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
626#endif
627			break;
628		}
629		for(p = servinfo; p; p = p->ai_next) {
630			n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
631			if (n < 0) {
632#ifdef DEBUG
633				char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
634				if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
635				                sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
636					fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
637				}
638#endif
639				PRINT_SOCKET_ERROR("sendto");
640				continue;
641			}
642		}
643		freeaddrinfo(servinfo);
644		if(n < 0) {
645			if(error)
646				*error = UPNPDISCOVER_SOCKET_ERROR;
647			break;
648		}
649#endif /* #ifdef NO_GETADDRINFO */
650		/* Waiting for SSDP REPLY packet to M-SEARCH */
651		do {
652			n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
653			if (n < 0) {
654				/* error */
655				if(error)
656					*error = UPNPDISCOVER_SOCKET_ERROR;
657				goto error;
658			} else if (n == 0) {
659				/* no data or Time Out */
660				if (devlist) {
661					/* found some devices, stop now*/
662					if(error)
663						*error = UPNPDISCOVER_SUCCESS;
664					goto error;
665				}
666				if(ipv6) {
667					/* switch linklocal flag */
668					if(linklocal) {
669						linklocal = 0;
670						--deviceIndex;
671					} else {
672						linklocal = 1;
673					}
674				}
675			} else {
676				const char * descURL=NULL;
677				int urlsize=0;
678				const char * st=NULL;
679				int stsize=0;
680				parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
681				if(st&&descURL) {
682#ifdef DEBUG
683					printf("M-SEARCH Reply:\n  ST: %.*s\n  Location: %.*s\n",
684					       stsize, st, urlsize, descURL);
685#endif
686					for(tmp=devlist; tmp; tmp = tmp->pNext) {
687						if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
688						   tmp->descURL[urlsize] == '\0' &&
689						   memcmp(tmp->st, st, stsize) == 0 &&
690						   tmp->st[stsize] == '\0')
691							break;
692					}
693					/* at the exit of the loop above, tmp is null if
694					 * no duplicate device was found */
695					if(tmp)
696						continue;
697					tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
698					if(!tmp) {
699						/* memory allocation error */
700						if(error)
701							*error = UPNPDISCOVER_MEMORY_ERROR;
702						goto error;
703					}
704					tmp->pNext = devlist;
705					tmp->descURL = tmp->buffer;
706					tmp->st = tmp->buffer + 1 + urlsize;
707					memcpy(tmp->buffer, descURL, urlsize);
708					tmp->buffer[urlsize] = '\0';
709					memcpy(tmp->buffer + urlsize + 1, st, stsize);
710					tmp->buffer[urlsize+1+stsize] = '\0';
711					tmp->scope_id = scope_id;
712					devlist = tmp;
713				}
714			}
715		} while(n > 0);
716	}
717error:
718	closesocket(sudp);
719	return devlist;
720}
721
722/* upnpDiscover() Discover IGD device */
723MINIUPNP_LIBSPEC struct UPNPDev *
724upnpDiscover(int delay, const char * multicastif,
725             const char * minissdpdsock, int sameport,
726             int ipv6,
727             int * error)
728{
729	static const char * const deviceList[] = {
730#if 0
731		"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
732		"urn:schemas-upnp-org:service:WANIPConnection:2",
733#endif
734		"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
735		//"urn:schemas-upnp-org:service:WANIPConnection:1",
736		//"urn:schemas-upnp-org:service:WANPPPConnection:1",
737		"upnp:rootdevice",
738		/*"ssdp:all",*/
739		0
740	};
741	return upnpDiscoverDevices(deviceList,
742	                           delay, multicastif, minissdpdsock, sameport,
743	                           ipv6, error);
744}
745
746/* upnpDiscoverAll() Discover all UPnP devices */
747MINIUPNP_LIBSPEC struct UPNPDev *
748upnpDiscoverAll(int delay, const char * multicastif,
749                const char * minissdpdsock, int sameport,
750                int ipv6,
751                int * error)
752{
753	static const char * const deviceList[] = {
754		/*"upnp:rootdevice",*/
755		"ssdp:all",
756		0
757	};
758	return upnpDiscoverDevices(deviceList,
759	                           delay, multicastif, minissdpdsock, sameport,
760	                           ipv6, error);
761}
762
763/* upnpDiscoverDevice() Discover a specific device */
764MINIUPNP_LIBSPEC struct UPNPDev *
765upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
766                const char * minissdpdsock, int sameport,
767                int ipv6,
768                int * error)
769{
770	const char * const deviceList[] = {
771		device,
772		0
773	};
774	return upnpDiscoverDevices(deviceList,
775	                           delay, multicastif, minissdpdsock, sameport,
776	                           ipv6, error);
777}
778
779/* freeUPNPDevlist() should be used to
780 * free the chained list returned by upnpDiscover() */
781MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
782{
783	struct UPNPDev * next;
784	while(devlist)
785	{
786		next = devlist->pNext;
787		free(devlist);
788		devlist = next;
789	}
790}
791
792static char *
793build_absolute_url(const char * baseurl, const char * descURL,
794                   const char * url, unsigned int scope_id)
795{
796	int l, n;
797	char * s;
798	const char * base;
799	char * p;
800#if defined(IF_NAMESIZE) && !defined(_WIN32)
801	char ifname[IF_NAMESIZE];
802#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
803	char scope_str[8];
804#endif	/* defined(IF_NAMESIZE) && !defined(_WIN32) */
805
806	if(  (url[0] == 'h')
807	   &&(url[1] == 't')
808	   &&(url[2] == 't')
809	   &&(url[3] == 'p')
810	   &&(url[4] == ':')
811	   &&(url[5] == '/')
812	   &&(url[6] == '/'))
813		return strdup(url);
814	base = (baseurl[0] == '\0') ? descURL : baseurl;
815	n = strlen(base);
816	if(n > 7) {
817		p = strchr(base + 7, '/');
818		if(p)
819			n = p - base;
820	}
821	l = n + strlen(url) + 1;
822	if(url[0] != '/')
823		l++;
824	if(scope_id != 0) {
825#if defined(IF_NAMESIZE) && !defined(_WIN32)
826		if(if_indextoname(scope_id, ifname)) {
827			l += 3 + strlen(ifname);	/* 3 == strlen(%25) */
828		}
829#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
830		/* under windows, scope is numerical */
831		l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
832#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
833	}
834	s = malloc(l);
835	if(s == NULL) return NULL;
836	memcpy(s, base, n);
837	if(scope_id != 0) {
838		s[n] = '\0';
839		if(0 == memcmp(s, "http://[fe80:", 13)) {
840			/* this is a linklocal IPv6 address */
841			p = strchr(s, ']');
842			if(p) {
843				/* insert %25<scope> into URL */
844#ifdef IF_NAMESIZE
845				memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
846				memcpy(p, "%25", 3);
847				memcpy(p + 3, ifname, strlen(ifname));
848				n += 3 + strlen(ifname);
849#else
850				memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
851				memcpy(p, "%25", 3);
852				memcpy(p + 3, scope_str, strlen(scope_str));
853				n += 3 + strlen(scope_str);
854#endif
855			}
856		}
857	}
858	if(url[0] != '/')
859		s[n++] = '/';
860	memcpy(s + n, url, l - n);
861	return s;
862}
863
864/* Prepare the Urls for usage...
865 */
866MINIUPNP_LIBSPEC void
867GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
868            const char * descURL, unsigned int scope_id)
869{
870	/* strdup descURL */
871	urls->rootdescURL = strdup(descURL);
872
873	/* get description of WANIPConnection */
874	urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
875	                                        data->first.scpdurl, scope_id);
876	urls->controlURL = build_absolute_url(data->urlbase, descURL,
877	                                      data->first.controlurl, scope_id);
878	urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
879	                                          data->CIF.controlurl, scope_id);
880	urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
881	                                          data->IPv6FC.controlurl, scope_id);
882
883#ifdef DEBUG
884	printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
885	printf("urls->controlURL='%s'\n", urls->controlURL);
886	printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
887	printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
888#endif
889}
890
891MINIUPNP_LIBSPEC void
892FreeUPNPUrls(struct UPNPUrls * urls)
893{
894	if(!urls)
895		return;
896	free(urls->controlURL);
897	urls->controlURL = 0;
898	free(urls->ipcondescURL);
899	urls->ipcondescURL = 0;
900	free(urls->controlURL_CIF);
901	urls->controlURL_CIF = 0;
902	free(urls->controlURL_6FC);
903	urls->controlURL_6FC = 0;
904	free(urls->rootdescURL);
905	urls->rootdescURL = 0;
906}
907
908int
909UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
910{
911	char status[64];
912	unsigned int uptime;
913	status[0] = '\0';
914	UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
915	                   status, &uptime, NULL);
916	if(0 == strcmp("Connected", status))
917	{
918		return 1;
919	}
920	else
921		return 0;
922}
923
924void parsedescxml(char *msg, char *friendly_name, char *icon_url)
925{
926        char line[200], *body, *p, *mxend;
927        char tmp[200];
928        int i, j;
929        int s_num = 0;
930        int type = 0;
931	char *head, *end;
932        char *opts[] = {"<friendlyName>","<manufacturer>", "<presentationURL>",
933                        "<modelDescription>", "<modelName>", "<modelNumber>",
934                        "<serviceType>", "<SCPDURL>", "<icon>"
935                        };
936
937	char *icon_desc_end, *url_ptr;
938	int get_icon_url = 0;
939
940        /* pointer to the head of msg */
941        p = strstr(msg, "<?xml");
942        /* pointer to the end of msg */
943        body = strstr(msg, "</root>");
944
945        while( p!= NULL && p < body)
946        {
947                /* get rid of 'TAB' or 'Space' in the start of a line. */
948                while((*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ') && p < body)
949                        p ++;
950
951                /* get a line. */
952                i = 0;
953                while(*p != '>' && p < body)
954                {
955                    if(i<199)
956                        line[i++] = *p++;
957                    else
958                        *p++;
959                }
960                if(p == body)
961                {
962                        break;
963                }
964
965                line[i++] = *p++;
966                line[i] ='\0';
967
968                if(p == body)
969                        break;
970
971                /* judge whether this line is useful. */
972                for(type = 0; type< sizeof(opts)/sizeof(*opts); type++)
973                        if(strncmp(line, opts[type], strlen(opts[type])) == 0)
974                                break;
975
976                if(type == sizeof(opts)/sizeof(*opts))
977                        continue;
978
979
980                /* get the information.
981                 * eg. <manufacturer> information </manufacturer>
982		 */
983
984	if((type == 8) && (!get_icon_url)) {
985		icon_desc_end = strstr(p, "</icon>");
986
987		i = 0;
988		while(p < icon_desc_end) {
989	        	if(i<199)
990        	                line[i++] = *p++;
991                    	else
992                        	*p++;
993		}
994                line[i++] = *p++;
995                line[i] ='\0';
996		p = icon_desc_end + 7;
997	}
998	else {
999
1000                i = 0;
1001                while(*p != '>' && p < body)
1002                {
1003                    if(i<199)
1004                        line[i++] = *p++;
1005                    else
1006                        *p++;
1007                }
1008                line[i++] = *p++;
1009                line[i] ='\0';
1010                for(j =0; line[j] != '<'; j++)
1011                        tmp[j] = line[j];
1012                tmp[j] = '\0';
1013 }
1014
1015                switch(type)
1016                {
1017                case 0:
1018#ifdef DEBUG
1019			printf( "friendlyname = %s\n", tmp);
1020#endif
1021			if(strstr(tmp,"WPS Router") || strstr(tmp,"Device"))
1022				break;
1023
1024			if( (end = strchr(tmp, '(')) != NULL )
1025                        {
1026                                *end = '\0';
1027                                strcpy(friendly_name, tmp);
1028                        }
1029                        else if( (end = strchr(tmp, ':')) != NULL )
1030			{
1031				*end = '\0';
1032				strcpy(friendly_name, tmp);
1033			}
1034			else if( (head = strchr(tmp,'['))!=NULL && (end = strchr(tmp,']'))!=NULL )
1035			{
1036				if( head < end )
1037				{
1038					*end = '\0';
1039					strcpy(friendly_name, head+1);
1040				}
1041			}
1042			else
1043			{
1044				if(tmp!=NULL)
1045					strcpy(friendly_name, tmp);
1046			}
1047                        break;
1048#ifdef DEBUG
1049                case 1:
1050                        printf("manufacturer = %s\n", tmp);
1051                        break;
1052                case 2:
1053                        printf("presentation = %s\n", tmp);
1054                        break;
1055                case 3:
1056                        printf("description = %s\n", tmp);
1057
1058                        break;
1059                case 4:
1060                        printf("modelname = %s\n", tmp);
1061                        break;
1062                case 5:
1063                        printf("modelnumber = %s\n", tmp);
1064                        break;
1065#endif
1066                case 6: /* tmp="urn:schemas-upnp-org:service:serviceType:v" */
1067                        mxend = tmp;
1068                        i = 0; j = 0;
1069                        while(i != 4)
1070                        {
1071                                if(i == 3)
1072                                        tmp[j++] = *mxend;
1073                                if(*mxend == ':')
1074                                        i++;
1075                                mxend++;
1076                        }
1077                        tmp[j-1] = '\0';
1078#ifdef DEBUG
1079                        printf("service name = %s\n", tmp);
1080#endif
1081                        break;
1082                case 7:
1083#ifdef DEBUG
1084                        printf("service url = %s\n", tmp);
1085#endif
1086                        break;
1087                case 8:
1088	                if(strstr(line, "jpeg")||strstr(line, "png")) {
1089        	                if( url_ptr = strstr(line,"<url>")) {
1090                	                url_ptr = url_ptr + 5;
1091                        	        i = 0;
1092                                	while(*url_ptr != '<')
1093                                        	tmp[i++] = *url_ptr++;
1094                                	tmp[i] = '\0';
1095					strcpy(icon_url, tmp);
1096#ifdef DEBUG
1097					printf("icon url =%s\n", tmp);
1098#endif
1099					get_icon_url = 1;
1100				}
1101                        }
1102                        break;
1103                }
1104        }
1105
1106}
1107
1108char *
1109get_lan_ipaddr()
1110{
1111        int s;
1112        struct ifreq ifr;
1113        struct sockaddr_in *inaddr;
1114        struct in_addr ip_addr;
1115
1116        /* Retrieve IP info */
1117        if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
1118#if 0
1119                return strdup("0.0.0.0");
1120#else
1121        {
1122                memset(&ip_addr, 0x0, sizeof(ip_addr));
1123                return inet_ntoa(ip_addr);
1124        }
1125#endif
1126
1127        strncpy(ifr.ifr_name, "br0", IFNAMSIZ);
1128        inaddr = (struct sockaddr_in *)&ifr.ifr_addr;
1129        inet_aton("0.0.0.0", &inaddr->sin_addr);
1130
1131        /* Get IP address */
1132        ioctl(s, SIOCGIFADDR, &ifr);
1133        close(s);
1134
1135        ip_addr = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr;
1136        return inet_ntoa(ip_addr);
1137}
1138
1139/* UPNP_GetValidIGDe) :
1140 * return values :
1141 *    -1 = Internal error
1142 *     0 = NO IGD found
1143 *     1 = A valid connected IGD has been found
1144 *     2 = A valid IGD has been found but it reported as
1145 *         not connected
1146 *     3 = an UPnP device has been found but was not recognized as an IGD
1147 *
1148 * In any positive non zero return case, the urls and data structures
1149 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
1150 * free allocated memory.
1151 */
1152MINIUPNP_LIBSPEC int
1153UPNP_GetValidIGD(struct UPNPDev * devlist,
1154                 struct UPNPUrls * urls,
1155				 struct IGDdatas * data,
1156				 char * lanaddr, int lanaddrlen)
1157{
1158	struct xml_desc {
1159		char * xml;
1160		int size;
1161		int is_igd;
1162	} * desc = NULL;
1163	struct UPNPDev * dev;
1164	int ndev = 0;
1165	int i;
1166	int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
1167	int n_igd = 0;
1168	char extIpAddr[16];
1169	struct UPNPDevInfo IGDInfo;
1170	char *dut_lanaddr;
1171	char IGDDescURL[256];
1172
1173	if(!devlist)
1174	{
1175#ifdef DEBUG
1176		printf("Empty devlist\n");
1177#endif
1178		return 0;
1179	}
1180	/* counting total number of devices in the list */
1181	for(dev = devlist; dev; dev = dev->pNext)
1182		ndev++;
1183	if(ndev > 0)
1184	{
1185		desc = calloc(ndev, sizeof(struct xml_desc));
1186		if(!desc)
1187			return -1; /* memory allocation error */
1188	}
1189
1190	dut_lanaddr = get_lan_ipaddr();
1191
1192	/* Step 1 : downloading descriptions and testing type */
1193	for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
1194	{
1195		memset(&IGDInfo, 0, sizeof(struct UPNPDevInfo));
1196		/* we should choose an internet gateway device.
1197		 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
1198		strcpy(IGDDescURL, dev->descURL);
1199		desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
1200		                               lanaddr, lanaddrlen,
1201		                               dev->scope_id, &IGDInfo, dut_lanaddr);
1202
1203#ifdef DEBUG
1204		if(!desc[i].xml)
1205		{
1206			printf("error getting XML description %s\n", dev->descURL);
1207		}
1208#endif
1209		if(desc[i].xml)
1210		{
1211			memset(data, 0, sizeof(struct IGDdatas));
1212			memset(urls, 0, sizeof(struct UPNPUrls));
1213			/* parserootdesc(desc[i].xml, desc[i].size, data); */
1214
1215#ifdef DEBUG
1216			FILE *xml_fd;
1217			xml_fd = fopen("/tmp/upnpc_xml.log", "a");
1218			fprintf(xml_fd, "============= XML ==============\n");
1219			fprintf(xml_fd, "%s\n", desc[i].xml);
1220			parsedescxml(desc[i].xml, &IGDInfo.friendlyName, &IGDInfo.iconUrl);
1221	                fprintf(xml_fd, "    HostName: %s\n", IGDInfo.hostname);
1222        	        fprintf(xml_fd, "    type:     %s\n", IGDInfo.type);
1223                	fprintf(xml_fd, "    F Name:   %s\n", IGDInfo.friendlyName);
1224			fprintf(xml_fd, "    Icon URL: %s\n", IGDInfo.iconUrl);
1225			fprintf(xml_fd, "================================\n\n");
1226			syslog(LOG_NOTICE, "parse icon url: %s", IGDInfo.iconUrl);
1227#endif
1228
1229			strcpy(dev->DevInfo.hostname, IGDInfo.hostname);
1230			strcpy(dev->DevInfo.type, IGDInfo.type);
1231			strcpy(dev->DevInfo.friendlyName, IGDInfo.friendlyName);
1232			strcpy(dev->DevInfo.iconUrl, IGDInfo.iconUrl);
1233
1234#ifdef RTCONFIG_JFFS2USERICON
1235			if(strcmp(IGDInfo.iconUrl, "") != NULL) {
1236				char realIconUrl[158], iconFile[32], iconBuf[512];
1237				char *ico_head, *ico_end;
1238				char *icon, *p, *q;
1239				int  iconSize, i = 0, iconCheck = 0;
1240				FILE *ico_fd;
1241
1242				memset(realIconUrl, 0, 158);
1243				if(strstr(IGDInfo.iconUrl, "http://")) {
1244					strcpy(realIconUrl, IGDInfo.iconUrl);
1245				}
1246				else {
1247				    q = IGDDescURL;
1248				    while(q = strchr(q, '/')) {
1249					i++;
1250					if(i == 3) {
1251						p = IGDDescURL;
1252						i = 0;
1253						while ( p < q ) {
1254							realIconUrl[i++] = *p++;
1255						}
1256						strcat(realIconUrl, IGDInfo.iconUrl);
1257					#ifdef DEBUG
1258						printf("\n*** Real URL=%s=\n\n", realIconUrl);
1259					#endif
1260						break;
1261					}
1262					q++;
1263				    }
1264				}
1265			get_icon:
1266#ifdef DEBUG
1267				syslog(LOG_NOTICE, "Real icon url: %s", realIconUrl);
1268                                fprintf(xml_fd, "Real icon url: %s\n", realIconUrl);
1269#endif
1270                                sprintf(iconFile, "/tmp/upnpicon/%s.ico", IGDInfo.hostname);
1271
1272				icon = miniwget_getaddr(realIconUrl, &iconSize,
1273                                               lanaddr, lanaddrlen,
1274                                               0, &IGDInfo, dut_lanaddr);
1275
1276				if( iconSize > 512 ) {
1277					ico_fd = fopen(iconFile, "w");
1278					if(ico_fd) {
1279					fwrite(icon, sizeof(char), iconSize, ico_fd);
1280					fclose(ico_fd);
1281					}
1282				}
1283				else if(iconSize > 0) {
1284					ico_head = strstr(icon, "<a href=");
1285					if(ico_head) {
1286						ico_head = ico_head+9;
1287						ico_end = strstr(icon, "\">");
1288						if(ico_end) {
1289							*ico_end = '\0';
1290							strcpy(realIconUrl, ico_head);
1291							goto get_icon;
1292						}
1293					}
1294                                }
1295			}
1296#endif
1297
1298			if(COMPARE(data->CIF.servicetype,
1299			           "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
1300			{
1301				desc[i].is_igd = 1;
1302				n_igd++;
1303			}
1304#ifdef DEBUG
1305			fclose(xml_fd);
1306#endif
1307		}
1308		else
1309			memset(dev->DevInfo.hostname, 0, 65);
1310	}
1311
1312	/* iterate the list to find a device depending on state */
1313	for(state = 1; state <= 3; state++)
1314	/*if(0)*/
1315	{
1316		for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
1317		{
1318			if(desc[i].xml)
1319			{
1320				memset(data, 0, sizeof(struct IGDdatas));
1321				memset(urls, 0, sizeof(struct UPNPUrls));
1322				parserootdesc(desc[i].xml, desc[i].size, data);
1323				if(desc[i].is_igd || state >= 3 )
1324				{
1325				  GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
1326
1327				  /* in state 2 and 3 we dont test if device is connected ! */
1328				  if(state >= 2)
1329				    goto free_and_return;
1330#ifdef DEBUG
1331				  printf("UPNPIGD_IsConnected(%s) = %d\n",
1332				     urls->controlURL,
1333			         UPNPIGD_IsConnected(urls, data));
1334#endif
1335				  /* checks that status is connected AND there is a external IP address assigned */
1336				  if(UPNPIGD_IsConnected(urls, data)
1337				     && (UPNP_GetExternalIPAddress(urls->controlURL,  data->first.servicetype, extIpAddr) == 0))
1338					goto free_and_return;
1339				  FreeUPNPUrls(urls);
1340				  if(data->second.servicetype[0] != '\0') {
1341#ifdef DEBUG
1342				    printf("We tried %s, now we try %s !\n",
1343				           data->first.servicetype, data->second.servicetype);
1344#endif
1345				    /* swaping WANPPPConnection and WANIPConnection ! */
1346				    memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
1347				    memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
1348				    memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
1349				    GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
1350#ifdef DEBUG
1351				    printf("UPNPIGD_IsConnected(%s) = %d\n",
1352				       urls->controlURL,
1353			           UPNPIGD_IsConnected(urls, data));
1354#endif
1355				    if(UPNPIGD_IsConnected(urls, data)
1356				       && (UPNP_GetExternalIPAddress(urls->controlURL,  data->first.servicetype, extIpAddr) == 0))
1357					  goto free_and_return;
1358				    FreeUPNPUrls(urls);
1359				  }
1360				}
1361				memset(data, 0, sizeof(struct IGDdatas));
1362			}
1363		}
1364	}
1365	state = 0;
1366free_and_return:
1367	if(desc) {
1368		for(i = 0; i < ndev; i++) {
1369			if(desc[i].xml) {
1370				free(desc[i].xml);
1371			}
1372		}
1373		free(desc);
1374	}
1375	return state;
1376}
1377
1378/* UPNP_GetIGDFromUrl()
1379 * Used when skipping the discovery process.
1380 * return value :
1381 *   0 - Not ok
1382 *   1 - OK */
1383int
1384UPNP_GetIGDFromUrl(const char * rootdescurl,
1385                   struct UPNPUrls * urls,
1386                   struct IGDdatas * data,
1387                   char * lanaddr, int lanaddrlen)
1388{
1389	char * descXML;
1390	int descXMLsize = 0;
1391
1392	descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
1393	   	                       lanaddr, lanaddrlen, 0, NULL, NULL);
1394	if(descXML) {
1395		memset(data, 0, sizeof(struct IGDdatas));
1396		memset(urls, 0, sizeof(struct UPNPUrls));
1397		parserootdesc(descXML, descXMLsize, data);
1398		free(descXML);
1399		descXML = NULL;
1400		GetUPNPUrls(urls, data, rootdescurl, 0);
1401		return 1;
1402	} else {
1403		return 0;
1404	}
1405}
1406
1407