transmission 2.83
[tomato.git] / release / src / router / transmission / third-party / miniupnp / miniupnpc.c
blob85b7d7577d809da58110f008b1ecccc68c720245
1 /* $Id: miniupnpc.c,v 1.117 2014/01/31 14:19:13 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(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
20 #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun)
21 #define HAS_IP_MREQN
22 #endif
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
75 #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
76 /* Several versions of glibc don't define this structure, define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
77 struct ip_mreqn
79 struct in_addr imr_multiaddr; /* IP multicast address of group */
80 struct in_addr imr_address; /* local IP address of interface */
81 int imr_ifindex; /* Interface index */
83 #endif
85 #include "miniupnpc.h"
86 #include "minissdpc.h"
87 #include "miniwget.h"
88 #include "minisoap.h"
89 #include "minixml.h"
90 #include "upnpcommands.h"
91 #include "connecthostport.h"
92 #include "receivedata.h"
94 #ifdef _WIN32
95 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
96 #else
97 #define PRINT_SOCKET_ERROR(x) perror(x)
98 #endif
100 #ifndef MAXHOSTNAMELEN
101 #define MAXHOSTNAMELEN 64
102 #endif
104 #define SOAPPREFIX "s"
105 #define SERVICEPREFIX "u"
106 #define SERVICEPREFIX2 'u'
108 /* root description parsing */
109 LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
111 struct xmlparser parser;
112 /* xmlparser object */
113 parser.xmlstart = buffer;
114 parser.xmlsize = bufsize;
115 parser.data = data;
116 parser.starteltfunc = IGDstartelt;
117 parser.endeltfunc = IGDendelt;
118 parser.datafunc = IGDdata;
119 parser.attfunc = 0;
120 parsexml(&parser);
121 #ifdef DEBUG
122 printIGD(data);
123 #endif
126 /* simpleUPnPcommand2 :
127 * not so simple !
128 * return values :
129 * pointer - OK
130 * NULL - error */
131 static char * simpleUPnPcommand2(int s, const char * url, const char * service,
132 const char * action, struct UPNParg * args,
133 int * bufsize, const char * httpversion)
135 char hostname[MAXHOSTNAMELEN+1];
136 unsigned short port = 0;
137 char * path;
138 char soapact[128];
139 char soapbody[2048];
140 char * buf;
141 int n;
143 *bufsize = 0;
144 snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
145 if(args==NULL)
147 /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
148 "<?xml version=\"1.0\"?>\r\n"
149 "<" SOAPPREFIX ":Envelope "
150 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
151 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
152 "<" SOAPPREFIX ":Body>"
153 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
154 "</" SERVICEPREFIX ":%s>"
155 "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
156 "\r\n", action, service, action);
158 else
160 char * p;
161 const char * pe, * pv;
162 int soapbodylen;
163 soapbodylen = snprintf(soapbody, sizeof(soapbody),
164 "<?xml version=\"1.0\"?>\r\n"
165 "<" SOAPPREFIX ":Envelope "
166 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
167 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
168 "<" SOAPPREFIX ":Body>"
169 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
170 action, service);
171 p = soapbody + soapbodylen;
172 while(args->elt)
174 /* check that we are never overflowing the string... */
175 if(soapbody + sizeof(soapbody) <= p + 100)
177 /* we keep a margin of at least 100 bytes */
178 return NULL;
180 *(p++) = '<';
181 pe = args->elt;
182 while(*pe)
183 *(p++) = *(pe++);
184 *(p++) = '>';
185 if((pv = args->val))
187 while(*pv)
188 *(p++) = *(pv++);
190 *(p++) = '<';
191 *(p++) = '/';
192 pe = args->elt;
193 while(*pe)
194 *(p++) = *(pe++);
195 *(p++) = '>';
196 args++;
198 *(p++) = '<';
199 *(p++) = '/';
200 *(p++) = SERVICEPREFIX2;
201 *(p++) = ':';
202 pe = action;
203 while(*pe)
204 *(p++) = *(pe++);
205 strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
206 soapbody + sizeof(soapbody) - p);
208 if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
209 if(s < 0) {
210 s = connecthostport(hostname, port, 0);
211 if(s < 0) {
212 /* failed to connect */
213 return NULL;
217 n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
218 if(n<=0) {
219 #ifdef DEBUG
220 printf("Error sending SOAP request\n");
221 #endif
222 closesocket(s);
223 return NULL;
226 buf = getHTTPResponse(s, bufsize);
227 #ifdef DEBUG
228 if(*bufsize > 0 && buf)
230 printf("SOAP Response :\n%.*s\n", *bufsize, buf);
232 #endif
233 closesocket(s);
234 return buf;
237 /* simpleUPnPcommand :
238 * not so simple !
239 * return values :
240 * pointer - OK
241 * NULL - error */
242 char * simpleUPnPcommand(int s, const char * url, const char * service,
243 const char * action, struct UPNParg * args,
244 int * bufsize)
246 char * buf;
248 #if 1
249 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
250 #else
251 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
252 if (!buf || *bufsize == 0)
254 #if DEBUG
255 printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
256 #endif
257 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
259 #endif
260 return buf;
263 /* parseMSEARCHReply()
264 * the last 4 arguments are filled during the parsing :
265 * - location/locationsize : "location:" field of the SSDP reply packet
266 * - st/stsize : "st:" field of the SSDP reply packet.
267 * The strings are NOT null terminated */
268 static void
269 parseMSEARCHReply(const char * reply, int size,
270 const char * * location, int * locationsize,
271 const char * * st, int * stsize)
273 int a, b, i;
274 i = 0;
275 a = i; /* start of the line */
276 b = 0; /* end of the "header" (position of the colon) */
277 while(i<size)
279 switch(reply[i])
281 case ':':
282 if(b==0)
284 b = i; /* end of the "header" */
285 /*for(j=a; j<b; j++)
287 putchar(reply[j]);
291 break;
292 case '\x0a':
293 case '\x0d':
294 if(b!=0)
296 /*for(j=b+1; j<i; j++)
298 putchar(reply[j]);
300 putchar('\n');*/
301 /* skip the colon and white spaces */
302 do { b++; } while(reply[b]==' ');
303 if(0==strncasecmp(reply+a, "location", 8))
305 *location = reply+b;
306 *locationsize = i-b;
308 else if(0==strncasecmp(reply+a, "st", 2))
310 *st = reply+b;
311 *stsize = i-b;
313 b = 0;
315 a = i+1;
316 break;
317 default:
318 break;
320 i++;
324 /* port upnp discover : SSDP protocol */
325 #define PORT 1900
326 #define XSTR(s) STR(s)
327 #define STR(s) #s
328 #define UPNP_MCAST_ADDR "239.255.255.250"
329 /* for IPv6 */
330 #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
331 #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
333 /* upnpDiscover() :
334 * return a chained list of all devices found or NULL if
335 * no devices was found.
336 * It is up to the caller to free the chained list
337 * delay is in millisecond (poll) */
338 LIBSPEC struct UPNPDev *
339 upnpDiscover(int delay, const char * multicastif,
340 const char * minissdpdsock, int sameport,
341 int ipv6,
342 int * error)
344 struct UPNPDev * tmp;
345 struct UPNPDev * devlist = 0;
346 unsigned int scope_id = 0;
347 int opt = 1;
348 static const char MSearchMsgFmt[] =
349 "M-SEARCH * HTTP/1.1\r\n"
350 "HOST: %s:" XSTR(PORT) "\r\n"
351 "ST: %s\r\n"
352 "MAN: \"ssdp:discover\"\r\n"
353 "MX: %u\r\n"
354 "\r\n";
355 static const char * const deviceList[] = {
356 #if 0
357 "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
358 "urn:schemas-upnp-org:service:WANIPConnection:2",
359 #endif
360 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
361 "urn:schemas-upnp-org:service:WANIPConnection:1",
362 "urn:schemas-upnp-org:service:WANPPPConnection:1",
363 "upnp:rootdevice",
366 int deviceIndex = 0;
367 char bufr[1536]; /* reception and emission buffer */
368 int sudp;
369 int n;
370 struct sockaddr_storage sockudp_r;
371 unsigned int mx;
372 #ifdef NO_GETADDRINFO
373 struct sockaddr_storage sockudp_w;
374 #else
375 int rv;
376 struct addrinfo hints, *servinfo, *p;
377 #endif
378 #ifdef _WIN32
379 MIB_IPFORWARDROW ip_forward;
380 #endif
381 int linklocal = 1;
383 if(error)
384 *error = UPNPDISCOVER_UNKNOWN_ERROR;
385 #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
386 /* first try to get infos from minissdpd ! */
387 if(!minissdpdsock)
388 minissdpdsock = "/var/run/minissdpd.sock";
389 while(!devlist && deviceList[deviceIndex]) {
390 devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
391 minissdpdsock);
392 /* We return what we have found if it was not only a rootdevice */
393 if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
394 if(error)
395 *error = UPNPDISCOVER_SUCCESS;
396 return devlist;
398 deviceIndex++;
400 deviceIndex = 0;
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)
410 if(error)
411 *error = UPNPDISCOVER_SOCKET_ERROR;
412 PRINT_SOCKET_ERROR("socket");
413 return NULL;
415 /* reception */
416 memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
417 if(ipv6) {
418 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
419 p->sin6_family = AF_INET6;
420 if(sameport)
421 p->sin6_port = htons(PORT);
422 p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
423 } else {
424 struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
425 p->sin_family = AF_INET;
426 if(sameport)
427 p->sin_port = htons(PORT);
428 p->sin_addr.s_addr = INADDR_ANY;
430 #ifdef _WIN32
431 /* This code could help us to use the right Network interface for
432 * SSDP multicast traffic */
433 /* Get IP associated with the index given in the ip_forward struct
434 * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
435 if(!ipv6
436 && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
437 DWORD dwRetVal = 0;
438 PMIB_IPADDRTABLE pIPAddrTable;
439 DWORD dwSize = 0;
440 #ifdef DEBUG
441 IN_ADDR IPAddr;
442 #endif
443 int i;
444 #ifdef DEBUG
445 printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
446 #endif
447 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
448 if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
449 free(pIPAddrTable);
450 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
452 if(pIPAddrTable) {
453 dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
454 #ifdef DEBUG
455 printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
456 #endif
457 for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
458 #ifdef DEBUG
459 printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
460 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
461 printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
462 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
463 printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
464 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
465 printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
466 printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
467 printf("\tType and State[%d]:", i);
468 printf("\n");
469 #endif
470 if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
471 /* Set the address of this interface to be used */
472 struct in_addr mc_if;
473 memset(&mc_if, 0, sizeof(mc_if));
474 mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
475 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
476 PRINT_SOCKET_ERROR("setsockopt");
478 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
479 #ifndef DEBUG
480 break;
481 #endif
484 free(pIPAddrTable);
485 pIPAddrTable = NULL;
488 #endif
490 #ifdef _WIN32
491 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
492 #else
493 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
494 #endif
496 if(error)
497 *error = UPNPDISCOVER_SOCKET_ERROR;
498 PRINT_SOCKET_ERROR("setsockopt");
499 return NULL;
502 if(multicastif)
504 if(ipv6) {
505 #if !defined(_WIN32)
506 /* according to MSDN, if_nametoindex() is supported since
507 * MS Windows Vista and MS Windows Server 2008.
508 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
509 unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
510 if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
512 PRINT_SOCKET_ERROR("setsockopt");
514 #else
515 #ifdef DEBUG
516 printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
517 #endif
518 #endif
519 } else {
520 struct in_addr mc_if;
521 mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
522 if(mc_if.s_addr != INADDR_NONE)
524 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
525 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
527 PRINT_SOCKET_ERROR("setsockopt");
529 } else {
530 #ifdef HAS_IP_MREQN
531 /* was not an ip address, try with an interface name */
532 struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
533 memset(&reqn, 0, sizeof(struct ip_mreqn));
534 reqn.imr_ifindex = if_nametoindex(multicastif);
535 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
537 PRINT_SOCKET_ERROR("setsockopt");
539 #else
540 #ifdef DEBUG
541 printf("Setting of multicast interface not supported with interface name.\n");
542 #endif
543 #endif
548 /* Before sending the packed, we first "bind" in order to be able
549 * to receive the response */
550 if (bind(sudp, (const struct sockaddr *)&sockudp_r,
551 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
553 if(error)
554 *error = UPNPDISCOVER_SOCKET_ERROR;
555 PRINT_SOCKET_ERROR("bind");
556 closesocket(sudp);
557 return NULL;
560 if(error)
561 *error = UPNPDISCOVER_SUCCESS;
562 /* Calculating maximum response time in seconds */
563 mx = ((unsigned int)delay) / 1000u;
564 if(mx == 0) {
565 mx = 1;
566 delay = 1000;
568 /* receiving SSDP response packet */
569 for(n = 0; deviceList[deviceIndex]; deviceIndex++)
571 if(n == 0)
573 /* sending the SSDP M-SEARCH packet */
574 n = snprintf(bufr, sizeof(bufr),
575 MSearchMsgFmt,
576 ipv6 ?
577 (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
578 : UPNP_MCAST_ADDR,
579 deviceList[deviceIndex], mx);
580 #ifdef DEBUG
581 printf("Sending %s", bufr);
582 #endif
583 #ifdef NO_GETADDRINFO
584 /* the following code is not using getaddrinfo */
585 /* emission */
586 memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
587 if(ipv6) {
588 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
589 p->sin6_family = AF_INET6;
590 p->sin6_port = htons(PORT);
591 inet_pton(AF_INET6,
592 linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
593 &(p->sin6_addr));
594 } else {
595 struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
596 p->sin_family = AF_INET;
597 p->sin_port = htons(PORT);
598 p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
600 n = sendto(sudp, bufr, n, 0,
601 &sockudp_w,
602 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
603 if (n < 0) {
604 if(error)
605 *error = UPNPDISCOVER_SOCKET_ERROR;
606 PRINT_SOCKET_ERROR("sendto");
607 break;
609 #else /* #ifdef NO_GETADDRINFO */
610 memset(&hints, 0, sizeof(hints));
611 hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
612 hints.ai_socktype = SOCK_DGRAM;
613 /*hints.ai_flags = */
614 if ((rv = getaddrinfo(ipv6
615 ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
616 : UPNP_MCAST_ADDR,
617 XSTR(PORT), &hints, &servinfo)) != 0) {
618 if(error)
619 *error = UPNPDISCOVER_SOCKET_ERROR;
620 #ifdef _WIN32
621 fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
622 #else
623 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
624 #endif
625 break;
627 for(p = servinfo; p; p = p->ai_next) {
628 n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
629 if (n < 0) {
630 #ifdef DEBUG
631 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
632 if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
633 sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
634 fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
636 #endif
637 PRINT_SOCKET_ERROR("sendto");
638 continue;
641 freeaddrinfo(servinfo);
642 if(n < 0) {
643 if(error)
644 *error = UPNPDISCOVER_SOCKET_ERROR;
645 break;
647 #endif /* #ifdef NO_GETADDRINFO */
649 /* Waiting for SSDP REPLY packet to M-SEARCH */
650 n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
651 if (n < 0) {
652 /* error */
653 if(error)
654 *error = UPNPDISCOVER_SOCKET_ERROR;
655 break;
656 } else if (n == 0) {
657 /* no data or Time Out */
658 if (devlist) {
659 /* no more device type to look for... */
660 if(error)
661 *error = UPNPDISCOVER_SUCCESS;
662 break;
664 if(ipv6) {
665 if(linklocal) {
666 linklocal = 0;
667 --deviceIndex;
668 } else {
669 linklocal = 1;
672 } else {
673 const char * descURL=NULL;
674 int urlsize=0;
675 const char * st=NULL;
676 int stsize=0;
677 /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
678 parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
679 if(st&&descURL)
681 #ifdef DEBUG
682 printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
683 stsize, st, urlsize, descURL);
684 #endif
685 for(tmp=devlist; tmp; tmp = tmp->pNext) {
686 if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
687 tmp->descURL[urlsize] == '\0' &&
688 memcmp(tmp->st, st, stsize) == 0 &&
689 tmp->st[stsize] == '\0')
690 break;
692 /* at the exit of the loop above, tmp is null if
693 * no duplicate device was found */
694 if(tmp)
695 continue;
696 tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
697 if(!tmp) {
698 /* memory allocation error */
699 if(error)
700 *error = UPNPDISCOVER_MEMORY_ERROR;
701 break;
703 tmp->pNext = devlist;
704 tmp->descURL = tmp->buffer;
705 tmp->st = tmp->buffer + 1 + urlsize;
706 memcpy(tmp->buffer, descURL, urlsize);
707 tmp->buffer[urlsize] = '\0';
708 memcpy(tmp->buffer + urlsize + 1, st, stsize);
709 tmp->buffer[urlsize+1+stsize] = '\0';
710 tmp->scope_id = scope_id;
711 devlist = tmp;
715 closesocket(sudp);
716 return devlist;
719 /* freeUPNPDevlist() should be used to
720 * free the chained list returned by upnpDiscover() */
721 LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
723 struct UPNPDev * next;
724 while(devlist)
726 next = devlist->pNext;
727 free(devlist);
728 devlist = next;
732 static void
733 url_cpy_or_cat(char * dst, const char * src, int n)
735 if( (src[0] == 'h')
736 &&(src[1] == 't')
737 &&(src[2] == 't')
738 &&(src[3] == 'p')
739 &&(src[4] == ':')
740 &&(src[5] == '/')
741 &&(src[6] == '/'))
743 strncpy(dst, src, n);
745 else
747 int l = strlen(dst);
748 if(src[0] != '/')
749 dst[l++] = '/';
750 if(l<=n)
751 strncpy(dst + l, src, n - l);
755 /* Prepare the Urls for usage...
757 LIBSPEC void
758 GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
759 const char * descURL, unsigned int scope_id)
761 char * p;
762 int n1, n2, n3, n4;
763 #ifdef IF_NAMESIZE
764 char ifname[IF_NAMESIZE];
765 #else
766 char scope_str[8];
767 #endif
769 n1 = strlen(data->urlbase);
770 if(n1==0)
771 n1 = strlen(descURL);
772 if(scope_id != 0) {
773 #ifdef IF_NAMESIZE
774 if(if_indextoname(scope_id, ifname)) {
775 n1 += 3 + strlen(ifname); /* 3 == strlen(%25) */
777 #else
778 /* under windows, scope is numerical */
779 snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
780 #endif
782 n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
783 n2 = n1; n3 = n1; n4 = n1;
784 n1 += strlen(data->first.scpdurl);
785 n2 += strlen(data->first.controlurl);
786 n3 += strlen(data->CIF.controlurl);
787 n4 += strlen(data->IPv6FC.controlurl);
789 /* allocate memory to store URLs */
790 urls->ipcondescURL = (char *)malloc(n1);
791 urls->controlURL = (char *)malloc(n2);
792 urls->controlURL_CIF = (char *)malloc(n3);
793 urls->controlURL_6FC = (char *)malloc(n4);
795 /* strdup descURL */
796 urls->rootdescURL = strdup(descURL);
798 /* get description of WANIPConnection */
799 if(data->urlbase[0] != '\0')
800 strncpy(urls->ipcondescURL, data->urlbase, n1);
801 else
802 strncpy(urls->ipcondescURL, descURL, n1);
803 p = strchr(urls->ipcondescURL+7, '/');
804 if(p) p[0] = '\0';
805 if(scope_id != 0) {
806 if(0 == memcmp(urls->ipcondescURL, "http://[fe80:", 13)) {
807 /* this is a linklocal IPv6 address */
808 p = strchr(urls->ipcondescURL, ']');
809 if(p) {
810 /* insert %25<scope> into URL */
811 #ifdef IF_NAMESIZE
812 memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
813 memcpy(p, "%25", 3);
814 memcpy(p + 3, ifname, strlen(ifname));
815 #else
816 memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
817 memcpy(p, "%25", 3);
818 memcpy(p + 3, scope_str, strlen(scope_str));
819 #endif
823 strncpy(urls->controlURL, urls->ipcondescURL, n2);
824 strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
825 strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
827 url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
829 url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
831 url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
833 url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
835 #ifdef DEBUG
836 printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
837 (unsigned)strlen(urls->ipcondescURL), n1);
838 printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
839 (unsigned)strlen(urls->controlURL), n2);
840 printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
841 (unsigned)strlen(urls->controlURL_CIF), n3);
842 printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
843 (unsigned)strlen(urls->controlURL_6FC), n4);
844 #endif
847 LIBSPEC void
848 FreeUPNPUrls(struct UPNPUrls * urls)
850 if(!urls)
851 return;
852 free(urls->controlURL);
853 urls->controlURL = 0;
854 free(urls->ipcondescURL);
855 urls->ipcondescURL = 0;
856 free(urls->controlURL_CIF);
857 urls->controlURL_CIF = 0;
858 free(urls->controlURL_6FC);
859 urls->controlURL_6FC = 0;
860 free(urls->rootdescURL);
861 urls->rootdescURL = 0;
865 UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
867 char status[64];
868 unsigned int uptime;
869 status[0] = '\0';
870 UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
871 status, &uptime, NULL);
872 if(0 == strcmp("Connected", status))
874 return 1;
876 else
877 return 0;
881 /* UPNP_GetValidIGD() :
882 * return values :
883 * -1 = Internal error
884 * 0 = NO IGD found
885 * 1 = A valid connected IGD has been found
886 * 2 = A valid IGD has been found but it reported as
887 * not connected
888 * 3 = an UPnP device has been found but was not recognized as an IGD
890 * In any positive non zero return case, the urls and data structures
891 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
892 * free allocated memory.
894 LIBSPEC int
895 UPNP_GetValidIGD(struct UPNPDev * devlist,
896 struct UPNPUrls * urls,
897 struct IGDdatas * data,
898 char * lanaddr, int lanaddrlen)
900 struct xml_desc {
901 char * xml;
902 int size;
903 int is_igd;
904 } * desc = NULL;
905 struct UPNPDev * dev;
906 int ndev = 0;
907 int i;
908 int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
909 int n_igd = 0;
910 char extIpAddr[16];
911 if(!devlist)
913 #ifdef DEBUG
914 printf("Empty devlist\n");
915 #endif
916 return 0;
918 /* counting total number of devices in the list */
919 for(dev = devlist; dev; dev = dev->pNext)
920 ndev++;
921 if(ndev > 0)
923 desc = calloc(ndev, sizeof(struct xml_desc));
924 if(!desc)
925 return -1; /* memory allocation error */
927 /* Step 1 : downloading descriptions and testing type */
928 for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
930 /* we should choose an internet gateway device.
931 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
932 desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
933 lanaddr, lanaddrlen,
934 dev->scope_id);
935 #ifdef DEBUG
936 if(!desc[i].xml)
938 printf("error getting XML description %s\n", dev->descURL);
940 #endif
941 if(desc[i].xml)
943 memset(data, 0, sizeof(struct IGDdatas));
944 memset(urls, 0, sizeof(struct UPNPUrls));
945 parserootdesc(desc[i].xml, desc[i].size, data);
946 if(0==strcmp(data->CIF.servicetype,
947 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
949 desc[i].is_igd = 1;
950 n_igd++;
954 /* iterate the list to find a device depending on state */
955 for(state = 1; state <= 3; state++)
957 for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
959 if(desc[i].xml)
961 memset(data, 0, sizeof(struct IGDdatas));
962 memset(urls, 0, sizeof(struct UPNPUrls));
963 parserootdesc(desc[i].xml, desc[i].size, data);
964 if(desc[i].is_igd || state >= 3 )
966 GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
968 /* in state 2 and 3 we dont test if device is connected ! */
969 if(state >= 2)
970 goto free_and_return;
971 #ifdef DEBUG
972 printf("UPNPIGD_IsConnected(%s) = %d\n",
973 urls->controlURL,
974 UPNPIGD_IsConnected(urls, data));
975 #endif
976 /* checks that status is connected AND there is a external IP address assigned */
977 if(UPNPIGD_IsConnected(urls, data)
978 && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
979 goto free_and_return;
980 FreeUPNPUrls(urls);
981 if(data->second.servicetype[0] != '\0') {
982 #ifdef DEBUG
983 printf("We tried %s, now we try %s !\n",
984 data->first.servicetype, data->second.servicetype);
985 #endif
986 /* swaping WANPPPConnection and WANIPConnection ! */
987 memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
988 memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
989 memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
990 GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
991 #ifdef DEBUG
992 printf("UPNPIGD_IsConnected(%s) = %d\n",
993 urls->controlURL,
994 UPNPIGD_IsConnected(urls, data));
995 #endif
996 if(UPNPIGD_IsConnected(urls, data)
997 && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
998 goto free_and_return;
999 FreeUPNPUrls(urls);
1002 memset(data, 0, sizeof(struct IGDdatas));
1006 state = 0;
1007 free_and_return:
1008 if(desc) {
1009 for(i = 0; i < ndev; i++) {
1010 if(desc[i].xml) {
1011 free(desc[i].xml);
1014 free(desc);
1016 return state;
1019 /* UPNP_GetIGDFromUrl()
1020 * Used when skipping the discovery process.
1021 * return value :
1022 * 0 - Not ok
1023 * 1 - OK */
1025 UPNP_GetIGDFromUrl(const char * rootdescurl,
1026 struct UPNPUrls * urls,
1027 struct IGDdatas * data,
1028 char * lanaddr, int lanaddrlen)
1030 char * descXML;
1031 int descXMLsize = 0;
1032 descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
1033 lanaddr, lanaddrlen, 0);
1034 if(descXML) {
1035 memset(data, 0, sizeof(struct IGDdatas));
1036 memset(urls, 0, sizeof(struct UPNPUrls));
1037 parserootdesc(descXML, descXMLsize, data);
1038 free(descXML);
1039 descXML = NULL;
1040 GetUPNPUrls(urls, data, rootdescurl, 0);
1041 return 1;
1042 } else {
1043 return 0;