source: trunk/third-party/miniupnp/miniupnpc.c @ 10739

Last change on this file since 10739 was 10739, checked in by livings124, 12 years ago

update miniupnpc to 20100609

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