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

Last change on this file since 12593 was 12593, checked in by livings124, 10 years ago

#4387 Update miniupnpc to 1.6

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