PageRenderTime 232ms CodeModel.GetById 40ms app.highlight 145ms RepoModel.GetById 36ms app.codeStats 1ms

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

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