transmission: update from 2.13 to 2.22
[tomato.git] / release / src / router / transmission / third-party / miniupnp / miniupnpc.c
blobd91591c2cec80ad24f13d17137b1d8e43a1c00d7
1 /* $Id: miniupnpc.c,v 1.85 2010/12/21 16:13:14 nanard Exp $ */
2 /* Project : miniupnp
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2010 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
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 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
30 #define strncasecmp _memicmp
31 #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
32 #define strncasecmp memicmp
33 #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
34 #define MAXHOSTNAMELEN 64
35 #else /* #ifdef WIN32 */
36 /* Standard POSIX includes */
37 #include <unistd.h>
38 #if defined(__amigaos__) && !defined(__amigaos4__)
39 /* Amiga OS 3 specific stuff */
40 #define socklen_t int
41 #else
42 #include <sys/select.h>
43 #endif
44 #include <sys/socket.h>
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <netdb.h>
50 #if !defined(__amigaos__) && !defined(__amigaos4__)
51 #include <poll.h>
52 #endif
53 #include <strings.h>
54 #include <errno.h>
55 #define closesocket close
56 #define MINIUPNPC_IGNORE_EINTR
57 #endif /* #else WIN32 */
58 #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
59 #include <sys/time.h>
60 #endif
61 #if defined(__amigaos__) || defined(__amigaos4__)
62 /* Amiga OS specific stuff */
63 #define TIMEVAL struct timeval
64 #endif
66 #include "miniupnpc.h"
67 #include "minissdpc.h"
68 #include "miniwget.h"
69 #include "minisoap.h"
70 #include "minixml.h"
71 #include "upnpcommands.h"
72 #include "connecthostport.h"
74 #ifdef WIN32
75 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
76 #else
77 #define PRINT_SOCKET_ERROR(x) perror(x)
78 #endif
80 #define SOAPPREFIX "s"
81 #define SERVICEPREFIX "u"
82 #define SERVICEPREFIX2 'u'
84 /* root description parsing */
85 LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
87 struct xmlparser parser;
88 /* xmlparser object */
89 parser.xmlstart = buffer;
90 parser.xmlsize = bufsize;
91 parser.data = data;
92 parser.starteltfunc = IGDstartelt;
93 parser.endeltfunc = IGDendelt;
94 parser.datafunc = IGDdata;
95 parser.attfunc = 0;
96 parsexml(&parser);
97 #ifdef DEBUG
98 printIGD(data);
99 #endif
102 #if 0
103 /* getcontentlenfromline() : parse the Content-Length HTTP header line.
104 * Content-length: nnn */
105 static int getcontentlenfromline(const char * p, int n)
107 static const char contlenstr[] = "content-length";
108 const char * p2 = contlenstr;
109 int a = 0;
110 while(*p2)
112 if(n==0)
113 return -1;
114 if(*p2 != *p && *p2 != (*p + 32))
115 return -1;
116 p++; p2++; n--;
118 if(n==0)
119 return -1;
120 if(*p != ':')
121 return -1;
122 p++; n--;
123 while(*p == ' ')
125 if(n==0)
126 return -1;
127 p++; n--;
129 while(*p >= '0' && *p <= '9')
131 if(n==0)
132 return -1;
133 a = (a * 10) + (*p - '0');
134 p++; n--;
136 return a;
139 /* getContentLengthAndHeaderLength()
140 * retrieve header length and content length from an HTTP response
141 * TODO : retrieve Transfer-Encoding: header value, in order to support
142 * HTTP/1.1, chunked transfer encoding must be supported. */
143 static void
144 getContentLengthAndHeaderLength(char * p, int n,
145 int * contentlen, int * headerlen)
147 char * line;
148 int linelen;
149 int r;
150 line = p;
151 while(line < p + n)
153 linelen = 0;
154 while(line[linelen] != '\r' && line[linelen] != '\r')
156 if(line+linelen >= p+n)
157 return;
158 linelen++;
160 r = getcontentlenfromline(line, linelen);
161 if(r>0)
162 *contentlen = r;
163 line = line + linelen + 2;
164 if(line[0] == '\r' && line[1] == '\n')
166 *headerlen = (line - p) + 2;
167 return;
171 #endif
173 /* simpleUPnPcommand2 :
174 * not so simple !
175 * return values :
176 * 0 - OK
177 * -1 - error */
178 static int simpleUPnPcommand2(int s, const char * url, const char * service,
179 const char * action, struct UPNParg * args,
180 char * buffer, int * bufsize, const char * httpversion)
182 char hostname[MAXHOSTNAMELEN+1];
183 unsigned short port = 0;
184 char * path;
185 char soapact[128];
186 char soapbody[2048];
187 char * buf;
188 /*int buffree;*/
189 int n;
190 /*int contentlen, headerlen;*/ /* for the response */
192 snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
193 if(args==NULL)
195 /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
196 "<?xml version=\"1.0\"?>\r\n"
197 "<" SOAPPREFIX ":Envelope "
198 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
199 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
200 "<" SOAPPREFIX ":Body>"
201 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
202 "</" SERVICEPREFIX ":%s>"
203 "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
204 "\r\n", action, service, action);
206 else
208 char * p;
209 const char * pe, * pv;
210 int soapbodylen;
211 soapbodylen = snprintf(soapbody, sizeof(soapbody),
212 "<?xml version=\"1.0\"?>\r\n"
213 "<" SOAPPREFIX ":Envelope "
214 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
215 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
216 "<" SOAPPREFIX ":Body>"
217 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
218 action, service);
219 p = soapbody + soapbodylen;
220 while(args->elt)
222 /* check that we are never overflowing the string... */
223 if(soapbody + sizeof(soapbody) <= p + 100)
225 /* we keep a margin of at least 100 bytes */
226 *bufsize = 0;
227 return -1;
229 *(p++) = '<';
230 pe = args->elt;
231 while(*pe)
232 *(p++) = *(pe++);
233 *(p++) = '>';
234 if((pv = args->val))
236 while(*pv)
237 *(p++) = *(pv++);
239 *(p++) = '<';
240 *(p++) = '/';
241 pe = args->elt;
242 while(*pe)
243 *(p++) = *(pe++);
244 *(p++) = '>';
245 args++;
247 *(p++) = '<';
248 *(p++) = '/';
249 *(p++) = SERVICEPREFIX2;
250 *(p++) = ':';
251 pe = action;
252 while(*pe)
253 *(p++) = *(pe++);
254 strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
255 soapbody + sizeof(soapbody) - p);
257 if(!parseURL(url, hostname, &port, &path)) return -1;
258 if(s<0)
260 s = connecthostport(hostname, port);
261 if(s < 0)
263 *bufsize = 0;
264 return -1;
268 n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
269 if(n<=0) {
270 #ifdef DEBUG
271 printf("Error sending SOAP request\n");
272 #endif
273 closesocket(s);
274 return -1;
277 #if 0
278 contentlen = -1;
279 headerlen = -1;
280 buf = buffer;
281 buffree = *bufsize;
282 *bufsize = 0;
283 while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) {
284 buffree -= n;
285 buf += n;
286 *bufsize += n;
287 getContentLengthAndHeaderLength(buffer, *bufsize,
288 &contentlen, &headerlen);
289 #ifdef DEBUG
290 printf("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
291 n, *bufsize, contentlen, headerlen);
292 #endif
293 /* break if we received everything */
294 if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
295 break;
297 #endif
298 buf = getHTTPResponse(s, &n);
299 if(n > 0 && buf)
301 #ifdef DEBUG
302 printf("SOAP Response :\n%.*s\n", n, buf);
303 #endif
304 if(*bufsize > n)
306 memcpy(buffer, buf, n);
307 *bufsize = n;
309 else
311 memcpy(buffer, buf, *bufsize);
313 free(buf);
314 buf = 0;
316 closesocket(s);
317 return 0;
320 /* simpleUPnPcommand :
321 * not so simple !
322 * return values :
323 * 0 - OK
324 * -1 - error */
325 int simpleUPnPcommand(int s, const char * url, const char * service,
326 const char * action, struct UPNParg * args,
327 char * buffer, int * bufsize)
329 int result;
330 /*int origbufsize = *bufsize;*/
332 result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
334 result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.0");
335 if (result < 0 || *bufsize == 0)
337 #if DEBUG
338 printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
339 #endif
340 *bufsize = origbufsize;
341 result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
344 return result;
347 /* parseMSEARCHReply()
348 * the last 4 arguments are filled during the parsing :
349 * - location/locationsize : "location:" field of the SSDP reply packet
350 * - st/stsize : "st:" field of the SSDP reply packet.
351 * The strings are NOT null terminated */
352 static void
353 parseMSEARCHReply(const char * reply, int size,
354 const char * * location, int * locationsize,
355 const char * * st, int * stsize)
357 int a, b, i;
358 i = 0;
359 a = i; /* start of the line */
360 b = 0;
361 while(i<size)
363 switch(reply[i])
365 case ':':
366 if(b==0)
368 b = i; /* end of the "header" */
369 /*for(j=a; j<b; j++)
371 putchar(reply[j]);
375 break;
376 case '\x0a':
377 case '\x0d':
378 if(b!=0)
380 /*for(j=b+1; j<i; j++)
382 putchar(reply[j]);
384 putchar('\n');*/
385 do { b++; } while(reply[b]==' ');
386 if(0==strncasecmp(reply+a, "location", 8))
388 *location = reply+b;
389 *locationsize = i-b;
391 else if(0==strncasecmp(reply+a, "st", 2))
393 *st = reply+b;
394 *stsize = i-b;
396 b = 0;
398 a = i+1;
399 break;
400 default:
401 break;
403 i++;
407 /* port upnp discover : SSDP protocol */
408 #define PORT 1900
409 #define XSTR(s) STR(s)
410 #define STR(s) #s
411 #define UPNP_MCAST_ADDR "239.255.255.250"
413 /* upnpDiscover() :
414 * return a chained list of all devices found or NULL if
415 * no devices was found.
416 * It is up to the caller to free the chained list
417 * delay is in millisecond (poll) */
418 LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
419 const char * minissdpdsock, int sameport)
421 struct UPNPDev * tmp;
422 struct UPNPDev * devlist = 0;
423 int opt = 1;
424 static const char MSearchMsgFmt[] =
425 "M-SEARCH * HTTP/1.1\r\n"
426 "HOST: " UPNP_MCAST_ADDR ":" XSTR(PORT) "\r\n"
427 "ST: %s\r\n"
428 "MAN: \"ssdp:discover\"\r\n"
429 "MX: %u\r\n"
430 "\r\n";
431 static const char * const deviceList[] = {
432 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
433 "urn:schemas-upnp-org:service:WANIPConnection:1",
434 "urn:schemas-upnp-org:service:WANPPPConnection:1",
435 "upnp:rootdevice",
438 int deviceIndex = 0;
439 char bufr[1536]; /* reception and emission buffer */
440 int sudp;
441 int n;
442 struct sockaddr sockudp_r;
443 unsigned int mx;
444 #ifdef NO_GETADDRINFO
445 struct sockaddr_in sockudp_w;
446 #else
447 int rv;
448 struct addrinfo hints, *servinfo, *p;
449 #endif
450 #ifdef WIN32
451 MIB_IPFORWARDROW ip_forward;
452 #endif
454 #if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
455 /* first try to get infos from minissdpd ! */
456 if(!minissdpdsock)
457 minissdpdsock = "/var/run/minissdpd.sock";
458 while(!devlist && deviceList[deviceIndex]) {
459 devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
460 minissdpdsock);
461 /* We return what we have found if it was not only a rootdevice */
462 if(devlist && !strstr(deviceList[deviceIndex], "rootdevice"))
463 return devlist;
464 deviceIndex++;
466 deviceIndex = 0;
467 #endif
468 /* fallback to direct discovery */
469 #ifdef WIN32
470 sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
471 #else
472 sudp = socket(PF_INET, SOCK_DGRAM, 0);
473 #endif
474 if(sudp < 0)
476 PRINT_SOCKET_ERROR("socket");
477 return NULL;
479 /* reception */
480 memset(&sockudp_r, 0, sizeof(struct sockaddr));
481 if(0/*ipv6*/) {
482 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
483 p->sin6_family = AF_INET6;
484 if(sameport)
485 p->sin6_port = htons(PORT);
486 p->sin6_addr = in6addr_any;//IN6ADDR_ANY_INIT;/*INADDR_ANY;*/
487 } else {
488 struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
489 p->sin_family = AF_INET;
490 if(sameport)
491 p->sin_port = htons(PORT);
492 p->sin_addr.s_addr = INADDR_ANY;
494 #ifdef WIN32
495 /* This code could help us to use the right Network interface for
496 * SSDP multicast traffic */
497 /* Get IP associated with the index given in the ip_forward struct
498 * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
499 if(GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR) {
500 DWORD dwRetVal = 0;
501 PMIB_IPADDRTABLE pIPAddrTable;
502 DWORD dwSize = 0;
503 #ifdef DEBUG
504 IN_ADDR IPAddr;
505 #endif
506 int i;
507 #ifdef DEBUG
508 printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
509 #endif
510 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
511 if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
512 free(pIPAddrTable);
513 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
515 if(pIPAddrTable) {
516 dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
517 #ifdef DEBUG
518 printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
519 #endif
520 for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
521 #ifdef DEBUG
522 printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
523 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
524 printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
525 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
526 printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
527 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
528 printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
529 printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
530 printf("\tType and State[%d]:", i);
531 printf("\n");
532 #endif
533 if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
534 /* Set the address of this interface to be used */
535 struct in_addr mc_if;
536 memset(&mc_if, 0, sizeof(mc_if));
537 mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
538 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
539 PRINT_SOCKET_ERROR("setsockopt");
541 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
542 #ifndef DEBUG
543 break;
544 #endif
547 free(pIPAddrTable);
548 pIPAddrTable = NULL;
551 #endif
553 #ifdef WIN32
554 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
555 #else
556 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
557 #endif
559 PRINT_SOCKET_ERROR("setsockopt");
560 return NULL;
563 if(multicastif)
565 struct in_addr mc_if;
566 mc_if.s_addr = inet_addr(multicastif);
567 if(0/*ipv6*/) {
568 } else {
569 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
571 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
573 PRINT_SOCKET_ERROR("setsockopt");
577 /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
578 if (bind(sudp, &sockudp_r, 0/*ipv6*/?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) != 0)
580 PRINT_SOCKET_ERROR("bind");
581 closesocket(sudp);
582 return NULL;
585 /* Calculating maximum response time in seconds */
586 mx = ((unsigned int)delay) / 1000u;
587 /* receiving SSDP response packet */
588 for(n = 0;;)
590 if(n == 0)
592 /* sending the SSDP M-SEARCH packet */
593 n = snprintf(bufr, sizeof(bufr),
594 MSearchMsgFmt, deviceList[deviceIndex++], mx);
595 /*printf("Sending %s", bufr);*/
596 #ifdef NO_GETADDRINFO
597 /* the following code is not using getaddrinfo */
598 /* emission */
599 memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
600 sockudp_w.sin_family = AF_INET;
601 sockudp_w.sin_port = htons(PORT);
602 sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
603 n = sendto(sudp, bufr, n, 0,
604 (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
605 if (n < 0) {
606 PRINT_SOCKET_ERROR("sendto");
607 closesocket(sudp);
608 return devlist;
610 #else /* #ifdef NO_GETADDRINFO */
611 memset(&hints, 0, sizeof(hints));
612 hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
613 hints.ai_socktype = SOCK_DGRAM;
614 /*hints.ai_flags = */
615 if ((rv = getaddrinfo(UPNP_MCAST_ADDR, XSTR(PORT), &hints, &servinfo)) != 0) {
616 #ifdef WIN32
617 fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
618 #else
619 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
620 #endif
621 return devlist;
623 for(p = servinfo; p; p = p->ai_next) {
624 n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
625 if (n < 0) {
626 PRINT_SOCKET_ERROR("sendto");
627 continue;
630 freeaddrinfo(servinfo);
631 if(n < 0) {
632 closesocket(sudp);
633 return devlist;
635 #endif /* #ifdef NO_GETADDRINFO */
637 /* Waiting for SSDP REPLY packet to M-SEARCH */
638 n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
639 if (n < 0) {
640 /* error */
641 closesocket(sudp);
642 return devlist;
643 } else if (n == 0) {
644 /* no data or Time Out */
645 if (devlist || (deviceList[deviceIndex] == 0)) {
646 /* no more device type to look for... */
647 closesocket(sudp);
648 return devlist;
650 } else {
651 const char * descURL=NULL;
652 int urlsize=0;
653 const char * st=NULL;
654 int stsize=0;
655 /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
656 parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
657 if(st&&descURL)
659 #ifdef DEBUG
660 printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
661 stsize, st, urlsize, descURL);
662 #endif
663 for(tmp=devlist; tmp; tmp = tmp->pNext) {
664 if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
665 tmp->descURL[urlsize] == '\0' &&
666 memcmp(tmp->st, st, stsize) == 0 &&
667 tmp->st[stsize] == '\0')
668 break;
670 /* at the exit of the loop above, tmp is null if
671 * no duplicate device was found */
672 if(tmp)
673 continue;
674 tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
675 tmp->pNext = devlist;
676 tmp->descURL = tmp->buffer;
677 tmp->st = tmp->buffer + 1 + urlsize;
678 memcpy(tmp->buffer, descURL, urlsize);
679 tmp->buffer[urlsize] = '\0';
680 memcpy(tmp->buffer + urlsize + 1, st, stsize);
681 tmp->buffer[urlsize+1+stsize] = '\0';
682 devlist = tmp;
688 /* freeUPNPDevlist() should be used to
689 * free the chained list returned by upnpDiscover() */
690 LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
692 struct UPNPDev * next;
693 while(devlist)
695 next = devlist->pNext;
696 free(devlist);
697 devlist = next;
701 static void
702 url_cpy_or_cat(char * dst, const char * src, int n)
704 if( (src[0] == 'h')
705 &&(src[1] == 't')
706 &&(src[2] == 't')
707 &&(src[3] == 'p')
708 &&(src[4] == ':')
709 &&(src[5] == '/')
710 &&(src[6] == '/'))
712 strncpy(dst, src, n);
714 else
716 int l = strlen(dst);
717 if(src[0] != '/')
718 dst[l++] = '/';
719 if(l<=n)
720 strncpy(dst + l, src, n - l);
724 /* Prepare the Urls for usage...
726 LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
727 const char * descURL)
729 char * p;
730 int n1, n2, n3;
731 n1 = strlen(data->urlbase);
732 if(n1==0)
733 n1 = strlen(descURL);
734 n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
735 n2 = n1; n3 = n1;
736 n1 += strlen(data->first.scpdurl);
737 n2 += strlen(data->first.controlurl);
738 n3 += strlen(data->CIF.controlurl);
740 urls->ipcondescURL = (char *)malloc(n1);
741 urls->controlURL = (char *)malloc(n2);
742 urls->controlURL_CIF = (char *)malloc(n3);
743 /* maintenant on chope la desc du WANIPConnection */
744 if(data->urlbase[0] != '\0')
745 strncpy(urls->ipcondescURL, data->urlbase, n1);
746 else
747 strncpy(urls->ipcondescURL, descURL, n1);
748 p = strchr(urls->ipcondescURL+7, '/');
749 if(p) p[0] = '\0';
750 strncpy(urls->controlURL, urls->ipcondescURL, n2);
751 strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
753 url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
755 url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
757 url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
759 #ifdef DEBUG
760 printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
761 (unsigned)strlen(urls->ipcondescURL), n1);
762 printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
763 (unsigned)strlen(urls->controlURL), n2);
764 printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
765 (unsigned)strlen(urls->controlURL_CIF), n3);
766 #endif
769 LIBSPEC void
770 FreeUPNPUrls(struct UPNPUrls * urls)
772 if(!urls)
773 return;
774 free(urls->controlURL);
775 urls->controlURL = 0;
776 free(urls->ipcondescURL);
777 urls->ipcondescURL = 0;
778 free(urls->controlURL_CIF);
779 urls->controlURL_CIF = 0;
783 int ReceiveData(int socket, char * data, int length, int timeout)
785 int n;
786 #if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
787 struct pollfd fds[1]; /* for the poll */
788 #ifdef MINIUPNPC_IGNORE_EINTR
789 do {
790 #endif
791 fds[0].fd = socket;
792 fds[0].events = POLLIN;
793 n = poll(fds, 1, timeout);
794 #ifdef MINIUPNPC_IGNORE_EINTR
795 } while(n < 0 && errno == EINTR);
796 #endif
797 if(n < 0)
799 PRINT_SOCKET_ERROR("poll");
800 return -1;
802 else if(n == 0)
804 return 0;
806 #else
807 fd_set socketSet;
808 TIMEVAL timeval;
809 FD_ZERO(&socketSet);
810 FD_SET(socket, &socketSet);
811 timeval.tv_sec = timeout / 1000;
812 timeval.tv_usec = (timeout % 1000) * 1000;
813 n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
814 if(n < 0)
816 PRINT_SOCKET_ERROR("select");
817 return -1;
819 else if(n == 0)
821 return 0;
823 #endif
824 n = recv(socket, data, length, 0);
825 if(n<0)
827 PRINT_SOCKET_ERROR("recv");
829 return n;
833 UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
835 char status[64];
836 unsigned int uptime;
837 status[0] = '\0';
838 UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
839 status, &uptime, NULL);
840 if(0 == strcmp("Connected", status))
842 return 1;
844 else
845 return 0;
849 /* UPNP_GetValidIGD() :
850 * return values :
851 * 0 = NO IGD found
852 * 1 = A valid connected IGD has been found
853 * 2 = A valid IGD has been found but it reported as
854 * not connected
855 * 3 = an UPnP device has been found but was not recognized as an IGD
857 * In any non zero return case, the urls and data structures
858 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
859 * free allocated memory.
861 LIBSPEC int
862 UPNP_GetValidIGD(struct UPNPDev * devlist,
863 struct UPNPUrls * urls,
864 struct IGDdatas * data,
865 char * lanaddr, int lanaddrlen)
867 char * descXML;
868 int descXMLsize = 0;
869 struct UPNPDev * dev;
870 int ndev = 0;
871 int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
872 if(!devlist)
874 #ifdef DEBUG
875 printf("Empty devlist\n");
876 #endif
877 return 0;
879 for(state = 1; state <= 3; state++)
881 for(dev = devlist; dev; dev = dev->pNext)
883 /* we should choose an internet gateway device.
884 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
885 descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
886 lanaddr, lanaddrlen);
887 if(descXML)
889 ndev++;
890 memset(data, 0, sizeof(struct IGDdatas));
891 memset(urls, 0, sizeof(struct UPNPUrls));
892 parserootdesc(descXML, descXMLsize, data);
893 free(descXML);
894 descXML = NULL;
895 if(0==strcmp(data->CIF.servicetype,
896 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
897 || state >= 3 )
899 GetUPNPUrls(urls, data, dev->descURL);
901 #ifdef DEBUG
902 printf("UPNPIGD_IsConnected(%s) = %d\n",
903 urls->controlURL,
904 UPNPIGD_IsConnected(urls, data));
905 #endif
906 if((state >= 2) || UPNPIGD_IsConnected(urls, data))
907 return state;
908 FreeUPNPUrls(urls);
909 if(data->second.servicetype[0] != '\0') {
910 #ifdef DEBUG
911 printf("We tried %s, now we try %s !\n",
912 data->first.servicetype, data->second.servicetype);
913 #endif
914 /* swaping WANPPPConnection and WANIPConnection ! */
915 memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
916 memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
917 memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
918 GetUPNPUrls(urls, data, dev->descURL);
919 #ifdef DEBUG
920 printf("UPNPIGD_IsConnected(%s) = %d\n",
921 urls->controlURL,
922 UPNPIGD_IsConnected(urls, data));
923 #endif
924 if((state >= 2) || UPNPIGD_IsConnected(urls, data))
925 return state;
926 FreeUPNPUrls(urls);
929 memset(data, 0, sizeof(struct IGDdatas));
931 #ifdef DEBUG
932 else
934 printf("error getting XML description %s\n", dev->descURL);
936 #endif
939 return 0;
942 /* UPNP_GetIGDFromUrl()
943 * Used when skipping the discovery process.
944 * return value :
945 * 0 - Not ok
946 * 1 - OK */
948 UPNP_GetIGDFromUrl(const char * rootdescurl,
949 struct UPNPUrls * urls,
950 struct IGDdatas * data,
951 char * lanaddr, int lanaddrlen)
953 char * descXML;
954 int descXMLsize = 0;
955 descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
956 lanaddr, lanaddrlen);
957 if(descXML) {
958 memset(data, 0, sizeof(struct IGDdatas));
959 memset(urls, 0, sizeof(struct UPNPUrls));
960 parserootdesc(descXML, descXMLsize, data);
961 free(descXML);
962 descXML = NULL;
963 GetUPNPUrls(urls, data, rootdescurl);
964 return 1;
965 } else {
966 return 0;