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