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