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

Last change on this file since 11577 was 11577, checked in by livings124, 11 years ago

update miniupnp to 1.4.20101221

File size: 24.6 KB
Line 
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
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#if 0
103/* getcontentlenfromline() : parse the Content-Length HTTP header line.
104 * Content-length: nnn */
105static int getcontentlenfromline(const char * p, int n)
106{
107        static const char contlenstr[] = "content-length";
108        const char * p2 = contlenstr;
109        int a = 0;
110        while(*p2)
111        {
112                if(n==0)
113                        return -1;
114                if(*p2 != *p && *p2 != (*p + 32))
115                        return -1;
116                p++; p2++; n--;
117        }
118        if(n==0)
119                return -1;
120        if(*p != ':')
121                return -1;
122        p++; n--;
123        while(*p == ' ')
124        {
125                if(n==0)
126                        return -1;
127                p++; n--;
128        }
129        while(*p >= '0' && *p <= '9')
130        {
131                if(n==0)
132                        return -1;
133                a = (a * 10) + (*p - '0');
134                p++; n--;
135        }
136        return a;
137}
138
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. */
143static void
144getContentLengthAndHeaderLength(char * p, int n,
145                                int * contentlen, int * headerlen)
146{
147        char * line;
148        int linelen;
149        int r;
150        line = p;
151        while(line < p + n)
152        {
153                linelen = 0;
154                while(line[linelen] != '\r' && line[linelen] != '\r')
155                {
156                        if(line+linelen >= p+n)
157                                return;
158                        linelen++;
159                }
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')
165                {
166                        *headerlen = (line - p) + 2;
167                        return;
168                }
169        }
170}
171#endif
172
173/* simpleUPnPcommand2 :
174 * not so simple !
175 * return values :
176 *   0 - OK
177 *  -1 - error */
178static 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)
181{
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 */
191
192        snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
193        if(args==NULL)
194        {
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);
205        }
206        else
207        {
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)
221                {
222                        /* check that we are never overflowing the string... */
223                        if(soapbody + sizeof(soapbody) <= p + 100)
224                        {
225                                /* we keep a margin of at least 100 bytes */
226                                *bufsize = 0;
227                                return -1;
228                        }
229                        *(p++) = '<';
230                        pe = args->elt;
231                        while(*pe)
232                                *(p++) = *(pe++);
233                        *(p++) = '>';
234                        if((pv = args->val))
235                        {
236                                while(*pv)
237                                        *(p++) = *(pv++);
238                        }
239                        *(p++) = '<';
240                        *(p++) = '/';
241                        pe = args->elt;
242                        while(*pe)
243                                *(p++) = *(pe++);
244                        *(p++) = '>';
245                        args++;
246                }
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);
256        }
257        if(!parseURL(url, hostname, &port, &path)) return -1;
258        if(s<0)
259        {
260                s = connecthostport(hostname, port);
261                if(s < 0)
262                {
263                        *bufsize = 0;
264                        return -1;
265                }
266        }
267
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;
275        }
276
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;
296        }
297#endif
298        buf = getHTTPResponse(s, &n);
299        if(n > 0 && buf)
300        {
301#ifdef DEBUG
302                printf("SOAP Response :\n%.*s\n", n, buf);
303#endif
304                if(*bufsize > n)
305                {
306                        memcpy(buffer, buf, n);
307                        *bufsize = n;
308                }
309                else
310                {
311                        memcpy(buffer, buf, *bufsize);
312                }
313                free(buf);
314                buf = 0;
315        }
316        closesocket(s);
317        return 0;
318}
319
320/* simpleUPnPcommand :
321 * not so simple !
322 * return values :
323 *   0 - OK
324 *  -1 - error */
325int simpleUPnPcommand(int s, const char * url, const char * service,
326                       const char * action, struct UPNParg * args,
327                       char * buffer, int * bufsize)
328{
329        int result;
330        /*int origbufsize = *bufsize;*/
331
332        result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
333/*
334        result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.0");
335        if (result < 0 || *bufsize == 0)
336        {
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");
342        }
343*/
344        return result;
345}
346
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 */
352static void
353parseMSEARCHReply(const char * reply, int size,
354                  const char * * location, int * locationsize,
355                              const char * * st, int * stsize)
356{
357        int a, b, i;
358        i = 0;
359        a = i;  /* start of the line */
360        b = 0;
361        while(i<size)
362        {
363                switch(reply[i])
364                {
365                case ':':
366                                if(b==0)
367                                {
368                                        b = i; /* end of the "header" */
369                                        /*for(j=a; j<b; j++)
370                                        {
371                                                putchar(reply[j]);
372                                        }
373                                        */
374                                }
375                                break;
376                case '\x0a':
377                case '\x0d':
378                                if(b!=0)
379                                {
380                                        /*for(j=b+1; j<i; j++)
381                                        {
382                                                putchar(reply[j]);
383                                        }
384                                        putchar('\n');*/
385                                        do { b++; } while(reply[b]==' ');
386                                        if(0==strncasecmp(reply+a, "location", 8))
387                                        {
388                                                *location = reply+b;
389                                                *locationsize = i-b;
390                                        }
391                                        else if(0==strncasecmp(reply+a, "st", 2))
392                                        {
393                                                *st = reply+b;
394                                                *stsize = i-b;
395                                        }
396                                        b = 0;
397                                }
398                                a = i+1;
399                                break;
400                default:
401                                break;
402                }
403                i++;
404        }
405}
406
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"
412
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) */
418LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
419                              const char * minissdpdsock, int sameport)
420{
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",
436                0
437        };
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
453
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++;
465        }
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)
475        {
476                PRINT_SOCKET_ERROR("socket");
477                return NULL;
478        }
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;
493        }
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);
514                }
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");
540                                        }
541                                        ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
542#ifndef DEBUG
543                                        break;
544#endif
545                                }
546                        }
547                        free(pIPAddrTable);
548                        pIPAddrTable = NULL;
549                }
550        }
551#endif
552
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
558        {
559                PRINT_SOCKET_ERROR("setsockopt");
560                return NULL;
561        }
562
563        if(multicastif)
564        {
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;
570                }
571                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
572                {
573                        PRINT_SOCKET_ERROR("setsockopt");
574                }
575        }
576
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)
579        {
580        PRINT_SOCKET_ERROR("bind");
581                closesocket(sudp);
582                return NULL;
583    }
584
585        /* Calculating maximum response time in seconds */
586        mx = ((unsigned int)delay) / 1000u;
587        /* receiving SSDP response packet */
588        for(n = 0;;)
589        {
590        if(n == 0)
591        {
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;
609                }
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;
622                }
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;
628                        }
629                }
630                freeaddrinfo(servinfo);
631                if(n < 0) {
632                        closesocket(sudp);
633                        return devlist;
634                }
635#endif /* #ifdef NO_GETADDRINFO */
636        }
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;
649                }
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)
658                {
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;
669                        }
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;
683                }
684        }
685        }
686}
687
688/* freeUPNPDevlist() should be used to
689 * free the chained list returned by upnpDiscover() */
690LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
691{
692        struct UPNPDev * next;
693        while(devlist)
694        {
695                next = devlist->pNext;
696                free(devlist);
697                devlist = next;
698        }
699}
700
701static void
702url_cpy_or_cat(char * dst, const char * src, int n)
703{
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] == '/'))
711        {
712                strncpy(dst, src, n);
713        }
714        else
715        {
716                int l = strlen(dst);
717                if(src[0] != '/')
718                        dst[l++] = '/';
719                if(l<=n)
720                        strncpy(dst + l, src, n - l);
721        }
722}
723
724/* Prepare the Urls for usage...
725 */
726LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
727                 const char * descURL)
728{
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);
739
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);
752       
753        url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
754
755        url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
756
757        url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
758
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
767}
768
769LIBSPEC void
770FreeUPNPUrls(struct UPNPUrls * urls)
771{
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;
780}
781
782
783int ReceiveData(int socket, char * data, int length, int timeout)
784{
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)
798    {
799        PRINT_SOCKET_ERROR("poll");
800        return -1;
801    }
802    else if(n == 0)
803    {
804        return 0;
805    }
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)
815    {
816        PRINT_SOCKET_ERROR("select");
817        return -1;
818    }
819    else if(n == 0)
820    {
821        return 0;
822    }   
823#endif
824        n = recv(socket, data, length, 0);
825        if(n<0)
826        {
827                PRINT_SOCKET_ERROR("recv");
828        }
829        return n;
830}
831
832int
833UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
834{
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))
841        {
842                return 1;
843        }
844        else
845                return 0;
846}
847
848
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
856 *
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.
860 */
861LIBSPEC int
862UPNP_GetValidIGD(struct UPNPDev * devlist,
863                 struct UPNPUrls * urls,
864                                 struct IGDdatas * data,
865                                 char * lanaddr, int lanaddrlen)
866{
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)
873        {
874#ifdef DEBUG
875                printf("Empty devlist\n");
876#endif
877                return 0;
878        }
879        for(state = 1; state <= 3; state++)
880        {
881                for(dev = devlist; dev; dev = dev->pNext)
882                {
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)
888                        {
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 )
898                                {
899                                  GetUPNPUrls(urls, data, dev->descURL);
900
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);
927                                  }
928                                }
929                                memset(data, 0, sizeof(struct IGDdatas));
930                        }
931#ifdef DEBUG
932                        else
933                        {
934                                printf("error getting XML description %s\n", dev->descURL);
935                        }
936#endif
937                }
938        }
939        return 0;
940}
941
942/* UPNP_GetIGDFromUrl()
943 * Used when skipping the discovery process.
944 * return value :
945 *   0 - Not ok
946 *   1 - OK */
947int
948UPNP_GetIGDFromUrl(const char * rootdescurl,
949                   struct UPNPUrls * urls,
950                   struct IGDdatas * data,
951                   char * lanaddr, int lanaddrlen)
952{
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;
967        }
968}
969
Note: See TracBrowser for help on using the repository browser.