source: branches/1.9x/third-party/miniupnp/miniupnpc.c @ 10578

Last change on this file since 10578 was 10578, checked in by charles, 11 years ago

(1.9x third-party) upgrade our miniupnp snapshot for #3125 "UPnP fails on Linksys AM300 modem/router"

File size: 23.5 KB
Line 
1/* $Id: miniupnpc.c,v 1.80 2010/04/12 20:39:41 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#ifdef 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/* TODO : 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 /*
479        if(GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR) {
480                DWORD dwRetVal = 0;
481                PMIB_IPADDRTABLE pIPAddrTable;
482    DWORD dwSize = 0;
483    IN_ADDR IPAddr;
484    int i;
485    printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
486    pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
487    if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
488            free(pIPAddrTable);
489      pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
490    }
491    if(pIPAddrTable) {
492        dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
493        printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
494        for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
495        printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
496        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
497        printf("\tIP Address[%d]:     \t%s\n", i, inet_ntoa(IPAddr) );
498        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
499        printf("\tSubnet Mask[%d]:    \t%s\n", i, inet_ntoa(IPAddr) );
500        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
501        printf("\tBroadCast[%d]:      \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
502        printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
503        printf("\tType and State[%d]:", i);
504        printf("\n");
505        }
506                        free(pIPAddrTable);
507        pIPAddrTable = NULL;
508    }
509        }
510*/
511#endif
512
513#ifdef WIN32
514        if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
515#else
516        if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
517#endif
518        {
519                PRINT_SOCKET_ERROR("setsockopt");
520                return NULL;
521        }
522
523        if(multicastif)
524        {
525                struct in_addr mc_if;
526                mc_if.s_addr = inet_addr(multicastif);
527                if(0/*ipv6*/) {
528                } else {
529                        ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
530                }
531                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
532                {
533                        PRINT_SOCKET_ERROR("setsockopt");
534                }
535        }
536
537        /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
538    if (bind(sudp, &sockudp_r, 0/*ipv6*/?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) != 0)
539        {
540        PRINT_SOCKET_ERROR("bind");
541                closesocket(sudp);
542                return NULL;
543    }
544
545        /* Calculating maximum response time in seconds */
546        mx = ((unsigned int)delay) / 1000u;
547        /* receiving SSDP response packet */
548        for(n = 0;;)
549        {
550        if(n == 0)
551        {
552                /* sending the SSDP M-SEARCH packet */
553                n = snprintf(bufr, sizeof(bufr),
554                             MSearchMsgFmt, deviceList[deviceIndex++], mx);
555                /*printf("Sending %s", bufr);*/
556#if 0
557                n = sendto(sudp, bufr, n, 0,
558                           (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
559                if (n < 0) {
560                        PRINT_SOCKET_ERROR("sendto");
561                        closesocket(sudp);
562                        return devlist;
563                }
564#endif
565                memset(&hints, 0, sizeof(hints));
566                hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
567                hints.ai_socktype = SOCK_DGRAM;
568                /*hints.ai_flags = */
569                if ((rv = getaddrinfo(UPNP_MCAST_ADDR, XSTR(PORT), &hints, &servinfo)) != 0) {
570#ifdef WIN32
571                    fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
572#else
573                    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
574#endif
575                    return devlist;
576                }
577                for(p = servinfo; p; p = p->ai_next) {
578                        n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
579                        if (n < 0) {
580                                PRINT_SOCKET_ERROR("sendto");
581                                continue;
582                        }
583                }
584                freeaddrinfo(servinfo);
585                if(n < 0) {
586                        closesocket(sudp);
587                        return devlist;
588                }
589        }
590        /* Waiting for SSDP REPLY packet to M-SEARCH */
591        n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
592        if (n < 0) {
593                /* error */
594                closesocket(sudp);
595                return devlist;
596        } else if (n == 0) {
597                /* no data or Time Out */
598                if (devlist || (deviceList[deviceIndex] == 0)) {
599                        /* no more device type to look for... */
600                        closesocket(sudp);
601                        return devlist;
602                }
603        } else {
604                const char * descURL=NULL;
605                int urlsize=0;
606                const char * st=NULL;
607                int stsize=0;
608        /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
609                parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
610                if(st&&descURL)
611                {
612#ifdef DEBUG
613                        printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
614                               stsize, st, urlsize, descURL);
615#endif
616                        for(tmp=devlist; tmp; tmp = tmp->pNext) {
617                                if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
618                                   tmp->descURL[urlsize] == '\0' &&
619                                   memcmp(tmp->st, st, stsize) == 0 &&
620                                   tmp->st[stsize] == '\0')
621                                        break;
622                        }
623                        /* at the exit of the loop above, tmp is null if
624                         * no duplicate device was found */
625                        if(tmp)
626                                continue;
627                        tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
628                        tmp->pNext = devlist;
629                        tmp->descURL = tmp->buffer;
630                        tmp->st = tmp->buffer + 1 + urlsize;
631                        memcpy(tmp->buffer, descURL, urlsize);
632                        tmp->buffer[urlsize] = '\0';
633                        memcpy(tmp->buffer + urlsize + 1, st, stsize);
634                        tmp->buffer[urlsize+1+stsize] = '\0';
635                        devlist = tmp;
636                }
637        }
638        }
639}
640
641/* freeUPNPDevlist() should be used to
642 * free the chained list returned by upnpDiscover() */
643LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
644{
645        struct UPNPDev * next;
646        while(devlist)
647        {
648                next = devlist->pNext;
649                free(devlist);
650                devlist = next;
651        }
652}
653
654static void
655url_cpy_or_cat(char * dst, const char * src, int n)
656{
657        if(  (src[0] == 'h')
658           &&(src[1] == 't')
659           &&(src[2] == 't')
660           &&(src[3] == 'p')
661           &&(src[4] == ':')
662           &&(src[5] == '/')
663           &&(src[6] == '/'))
664        {
665                strncpy(dst, src, n);
666        }
667        else
668        {
669                int l = strlen(dst);
670                if(src[0] != '/')
671                        dst[l++] = '/';
672                if(l<=n)
673                        strncpy(dst + l, src, n - l);
674        }
675}
676
677/* Prepare the Urls for usage...
678 */
679LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
680                 const char * descURL)
681{
682        char * p;
683        int n1, n2, n3;
684        n1 = strlen(data->urlbase);
685        if(n1==0)
686                n1 = strlen(descURL);
687        n1 += 2;        /* 1 byte more for Null terminator, 1 byte for '/' if needed */
688        n2 = n1; n3 = n1;
689        n1 += strlen(data->first.scpdurl);
690        n2 += strlen(data->first.controlurl);
691        n3 += strlen(data->CIF.controlurl);
692
693        urls->ipcondescURL = (char *)malloc(n1);
694        urls->controlURL = (char *)malloc(n2);
695        urls->controlURL_CIF = (char *)malloc(n3);
696        /* maintenant on chope la desc du WANIPConnection */
697        if(data->urlbase[0] != '\0')
698                strncpy(urls->ipcondescURL, data->urlbase, n1);
699        else
700                strncpy(urls->ipcondescURL, descURL, n1);
701        p = strchr(urls->ipcondescURL+7, '/');
702        if(p) p[0] = '\0';
703        strncpy(urls->controlURL, urls->ipcondescURL, n2);
704        strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
705       
706        url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
707
708        url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
709
710        url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
711
712#ifdef DEBUG
713        printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
714               (unsigned)strlen(urls->ipcondescURL), n1);
715        printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
716               (unsigned)strlen(urls->controlURL), n2);
717        printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
718               (unsigned)strlen(urls->controlURL_CIF), n3);
719#endif
720}
721
722LIBSPEC void
723FreeUPNPUrls(struct UPNPUrls * urls)
724{
725        if(!urls)
726                return;
727        free(urls->controlURL);
728        urls->controlURL = 0;
729        free(urls->ipcondescURL);
730        urls->ipcondescURL = 0;
731        free(urls->controlURL_CIF);
732        urls->controlURL_CIF = 0;
733}
734
735
736int ReceiveData(int socket, char * data, int length, int timeout)
737{
738    int n;
739#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
740    struct pollfd fds[1]; /* for the poll */
741#ifdef MINIUPNPC_IGNORE_EINTR
742    do {
743#endif
744        fds[0].fd = socket;
745        fds[0].events = POLLIN;
746        n = poll(fds, 1, timeout);
747#ifdef MINIUPNPC_IGNORE_EINTR
748    } while(n < 0 && errno == EINTR);
749#endif
750    if(n < 0)
751    {
752        PRINT_SOCKET_ERROR("poll");
753        return -1;
754    }
755    else if(n == 0)
756    {
757        return 0;
758    }
759#else
760    fd_set socketSet;
761    TIMEVAL timeval;
762    FD_ZERO(&socketSet);
763    FD_SET(socket, &socketSet);
764    timeval.tv_sec = timeout / 1000;
765    timeval.tv_usec = (timeout % 1000) * 1000;
766    n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
767    if(n < 0)
768    {
769        PRINT_SOCKET_ERROR("select");
770        return -1;
771    }
772    else if(n == 0)
773    {
774        return 0;
775    }   
776#endif
777        n = recv(socket, data, length, 0);
778        if(n<0)
779        {
780                PRINT_SOCKET_ERROR("recv");
781        }
782        return n;
783}
784
785int
786UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
787{
788        char status[64];
789        unsigned int uptime;
790        status[0] = '\0';
791        UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
792                           status, &uptime, NULL);
793        if(0 == strcmp("Connected", status))
794        {
795                return 1;
796        }
797        else
798                return 0;
799}
800
801
802/* UPNP_GetValidIGD() :
803 * return values :
804 *     0 = NO IGD found
805 *     1 = A valid connected IGD has been found
806 *     2 = A valid IGD has been found but it reported as
807 *         not connected
808 *     3 = an UPnP device has been found but was not recognized as an IGD
809 *
810 * In any non zero return case, the urls and data structures
811 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
812 * free allocated memory.
813 */
814LIBSPEC int
815UPNP_GetValidIGD(struct UPNPDev * devlist,
816                 struct UPNPUrls * urls,
817                                 struct IGDdatas * data,
818                                 char * lanaddr, int lanaddrlen)
819{
820        char * descXML;
821        int descXMLsize = 0;
822        struct UPNPDev * dev;
823        int ndev = 0;
824        int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
825        if(!devlist)
826        {
827#ifdef DEBUG
828                printf("Empty devlist\n");
829#endif
830                return 0;
831        }
832        for(state = 1; state <= 3; state++)
833        {
834                for(dev = devlist; dev; dev = dev->pNext)
835                {
836                        /* we should choose an internet gateway device.
837                        * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
838                        descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
839                                                        lanaddr, lanaddrlen);
840                        if(descXML)
841                        {
842                                ndev++;
843                                memset(data, 0, sizeof(struct IGDdatas));
844                                memset(urls, 0, sizeof(struct UPNPUrls));
845                                parserootdesc(descXML, descXMLsize, data);
846                                free(descXML);
847                                descXML = NULL;
848                                if(0==strcmp(data->CIF.servicetype,
849                                   "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
850                                   || state >= 3 )
851                                {
852                                  GetUPNPUrls(urls, data, dev->descURL);
853
854#ifdef DEBUG
855                                  printf("UPNPIGD_IsConnected(%s) = %d\n",
856                                     urls->controlURL,
857                                 UPNPIGD_IsConnected(urls, data));
858#endif
859                                  if((state >= 2) || UPNPIGD_IsConnected(urls, data))
860                                        return state;
861                                  FreeUPNPUrls(urls);
862                                  if(data->second.servicetype[0] != '\0') {
863#ifdef DEBUG
864                                    printf("We tried %s, now we try %s !\n",
865                                           data->first.servicetype, data->second.servicetype);
866#endif
867                                    /* swaping WANPPPConnection and WANIPConnection ! */
868                                    memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
869                                    memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
870                                    memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
871                                    GetUPNPUrls(urls, data, dev->descURL);
872#ifdef DEBUG
873                                    printf("UPNPIGD_IsConnected(%s) = %d\n",
874                                       urls->controlURL,
875                                   UPNPIGD_IsConnected(urls, data));
876#endif
877                                    if((state >= 2) || UPNPIGD_IsConnected(urls, data))
878                                          return state;
879                                    FreeUPNPUrls(urls);
880                                  }
881                                }
882                                memset(data, 0, sizeof(struct IGDdatas));
883                        }
884#ifdef DEBUG
885                        else
886                        {
887                                printf("error getting XML description %s\n", dev->descURL);
888                        }
889#endif
890                }
891        }
892        return 0;
893}
894
895/* UPNP_GetIGDFromUrl()
896 * Used when skipping the discovery process.
897 * return value :
898 *   0 - Not ok
899 *   1 - OK */
900int
901UPNP_GetIGDFromUrl(const char * rootdescurl,
902                   struct UPNPUrls * urls,
903                   struct IGDdatas * data,
904                   char * lanaddr, int lanaddrlen)
905{
906        char * descXML;
907        int descXMLsize = 0;
908        descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
909                                       lanaddr, lanaddrlen);
910        if(descXML) {
911                memset(data, 0, sizeof(struct IGDdatas));
912                memset(urls, 0, sizeof(struct UPNPUrls));
913                parserootdesc(descXML, descXMLsize, data);
914                free(descXML);
915                descXML = NULL;
916                GetUPNPUrls(urls, data, rootdescurl);
917                return 1;
918        } else {
919                return 0;
920        }
921}
922
Note: See TracBrowser for help on using the repository browser.