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

Last change on this file since 4251 was 4251, checked in by charles, 15 years ago

upgrade to miniupnp-20071213

File size: 17.5 KB
Line 
1/* $Id: miniupnpc.c,v 1.49 2007/12/19 14:58:54 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2007 Thomas Bernard
5 * This software is subjet to the conditions detailed in the
6 * provided LICENCE file. */
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#ifdef WIN32
11#include <winsock2.h>
12#include <Ws2tcpip.h>
13#include <io.h>
14#define snprintf _snprintf
15#define strncasecmp memicmp
16#define MAXHOSTNAMELEN 64
17#else
18#include <unistd.h>
19#include <sys/socket.h>
20#include <sys/types.h>
21#include <sys/param.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <poll.h>
25#include <netdb.h>
26#define closesocket close
27#endif
28#include "miniupnpc.h"
29#include "minissdpc.h"
30#include "miniwget.h"
31#include "minisoap.h"
32#include "minixml.h"
33#include "upnpcommands.h"
34
35/* Uncomment the following to transmit the msearch from the same port
36 * as the UPnP multicast port. With WinXP this seems to result in the
37 * responses to the msearch being lost, thus if things dont work then
38 * comment this out. */
39/* #define TX_FROM_UPNP_PORT */
40
41#ifdef WIN32
42#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
43#else
44#define PRINT_SOCKET_ERROR(x) perror(x)
45#endif
46
47/* root description parsing */
48void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
49{
50        struct xmlparser parser;
51        /* xmlparser object */
52        parser.xmlstart = buffer;
53        parser.xmlsize = bufsize;
54        parser.data = data;
55        parser.starteltfunc = IGDstartelt;
56        parser.endeltfunc = IGDendelt;
57        parser.datafunc = IGDdata;
58        parser.attfunc = 0;
59        parsexml(&parser);
60#ifndef NDEBUG
61        printIGD(data);
62#endif
63}
64
65/* Content-length: nnn */
66static int getcontentlenfromline(const char * p, int n)
67{
68        static const char contlenstr[] = "content-length";
69        const char * p2 = contlenstr;
70        int a = 0;
71        while(*p2)
72        {
73                if(n==0)
74                        return -1;
75                if(*p2 != *p && *p2 != (*p + 32))
76                        return -1;
77                p++; p2++; n--;
78        }
79        if(n==0)
80                return -1;
81        if(*p != ':')
82                return -1;
83        p++; n--;
84        while(*p == ' ')
85        {
86                if(n==0)
87                        return -1;
88                p++; n--;
89        }
90        while(*p >= '0' && *p <= '9')
91        {
92                if(n==0)
93                        return -1;
94                a = (a * 10) + (*p - '0');
95                p++; n--;
96        }
97        return a;
98}
99
100static void
101getContentLengthAndHeaderLength(char * p, int n,
102                                int * contentlen, int * headerlen)
103{
104        char * line;
105        int linelen;
106        int r;
107        line = p;
108        while(line < p + n)
109        {
110                linelen = 0;
111                while(line[linelen] != '\r' && line[linelen] != '\r')
112                {
113                        if(line+linelen >= p+n)
114                                return;
115                        linelen++;
116                }
117                r = getcontentlenfromline(line, linelen);
118                if(r>0)
119                        *contentlen = r;
120                line = line + linelen + 2;
121                if(line[0] == '\r' && line[1] == '\n')
122                {
123                        *headerlen = (line - p) + 2;
124                        return;
125                }
126        }
127}
128
129/* simpleUPnPcommand :
130 * not so simple !
131 * TODO: return some error codes */
132int simpleUPnPcommand(int s, const char * url, const char * service,
133                      const char * action, struct UPNParg * args,
134                      char * buffer, int * bufsize)
135{
136        struct sockaddr_in dest;
137        char hostname[MAXHOSTNAMELEN+1];
138        unsigned short port = 0;
139        char * path;
140        char soapact[128];
141        char soapbody[2048];
142        int soapbodylen;
143        char * buf;
144        int buffree;
145    int n;
146        int contentlen, headerlen;      /* for the response */
147        snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
148        if(args==NULL)
149        {
150                /*soapbodylen = snprintf(soapbody, sizeof(soapbody),
151                                                "<?xml version=\"1.0\"?>\r\n"
152                              "<SOAP-ENV:Envelope "
153                                                  "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
154                                                  "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
155                                                  "<SOAP-ENV:Body>"
156                                                  "<m:%s xmlns:m=\"%s\"/>"
157                                                  "</SOAP-ENV:Body></SOAP-ENV:Envelope>"
158                                                  "\r\n", action, service);*/
159                soapbodylen = snprintf(soapbody, sizeof(soapbody),
160                                                "<?xml version=\"1.0\"?>\r\n"
161                              "<s:Envelope "
162                                                  "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
163                                                  "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
164                                                  "<s:Body>"
165                                                  "<m:%s xmlns:m=\"%s\">"
166                                                  "</m:%s>"
167                                                  "</s:Body></s:Envelope>"
168                                                  "\r\n", action, service, action);
169        }
170        else
171        {
172                char * p;
173                const char * pe, * pv;
174                soapbodylen = snprintf(soapbody, sizeof(soapbody),
175                                                "<?xml version=\"1.0\"?>\r\n"
176                            "<SOAP-ENV:Envelope "
177                                                "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
178                                                "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
179                                                "<SOAP-ENV:Body>"
180                                                "<m:%s xmlns:m=\"%s\">",
181                                                action, service);
182                p = soapbody + soapbodylen;
183                while(args->elt)
184                {
185                        /* check that we are never overflowing the string... */
186                        if(soapbody + sizeof(soapbody) <= p + 100)
187                        {
188                                /* we keep a margin of at least 100 bytes */
189                                *bufsize = 0;
190                                return -1;
191                        }
192                        *(p++) = '<';
193                        pe = args->elt;
194                        while(*pe)
195                                *(p++) = *(pe++);
196                        *(p++) = '>';
197                        if((pv = args->val))
198                        {
199                                while(*pv)
200                                        *(p++) = *(pv++);
201                        }
202                        *(p++) = '<';
203                        *(p++) = '/';
204                        pe = args->elt;
205                        while(*pe)
206                                *(p++) = *(pe++);
207                        *(p++) = '>';
208                        args++;
209                }
210                *(p++) = '<';
211                *(p++) = '/';
212                *(p++) = 'm';
213                *(p++) = ':';
214                pe = action;
215                while(*pe)
216                        *(p++) = *(pe++);
217                strncpy(p, "></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n",
218                        soapbody + sizeof(soapbody) - p);
219        }
220        if(!parseURL(url, hostname, &port, &path)) return -1;
221        if(s<0)
222        {
223                s = socket(PF_INET, SOCK_STREAM, 0);
224                dest.sin_family = AF_INET;
225                dest.sin_port = htons(port);
226                dest.sin_addr.s_addr = inet_addr(hostname);
227                if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0)
228                {
229                        PRINT_SOCKET_ERROR("connect");
230                        *bufsize = 0;
231                        return -1;
232                }
233        }
234
235        n = soapPostSubmit(s, path, hostname, port, soapact, soapbody);
236
237        contentlen = -1;
238        headerlen = -1;
239        buf = buffer;
240        buffree = *bufsize;
241        *bufsize = 0;
242        while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) {
243                buffree -= n;
244                buf += n;
245                *bufsize += n;
246                getContentLengthAndHeaderLength(buffer, *bufsize,
247                                                &contentlen, &headerlen);
248#ifdef DEBUG
249                printf("n=%d bufsize=%d ContLen=%d HeadLen=%d\n",
250                       n, *bufsize, contentlen, headerlen);
251#endif
252                if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
253                        break;
254        }
255       
256        closesocket(s);
257        return -1;
258}
259
260/* parseMSEARCHReply()
261 * the last 4 arguments are filled during the parsing :
262 *    - location/locationsize : "location:" field of the SSDP reply packet
263 *    - st/stsize : "st:" field of the SSDP reply packet.
264 * The strings are NOT null terminated */
265static void
266parseMSEARCHReply(const char * reply, int size,
267                  const char * * location, int * locationsize,
268                              const char * * st, int * stsize)
269{
270        int a, b, i;
271        i = 0;
272        a = i;  /* start of the line */
273        b = 0;
274        while(i<size)
275        {
276                switch(reply[i])
277                {
278                case ':':
279                                if(b==0)
280                                {
281                                        b = i; /* end of the "header" */
282                                        /*for(j=a; j<b; j++)
283                                        {
284                                                putchar(reply[j]);
285                                        }
286                                        */
287                                }
288                                break;
289                case '\x0a':
290                case '\x0d':
291                                if(b!=0)
292                                {
293                                        /*for(j=b+1; j<i; j++)
294                                        {
295                                                putchar(reply[j]);
296                                        }
297                                        putchar('\n');*/
298                                        do { b++; } while(reply[b]==' ');
299                                        if(0==strncasecmp(reply+a, "location", 8))
300                                        {
301                                                *location = reply+b;
302                                                *locationsize = i-b;
303                                        }
304                                        else if(0==strncasecmp(reply+a, "st", 2))
305                                        {
306                                                *st = reply+b;
307                                                *stsize = i-b;
308                                        }
309                                        b = 0;
310                                }
311                                a = i+1;
312                                break;
313                default:
314                                break;
315                }
316                i++;
317        }
318}
319
320/* port upnp discover : SSDP protocol */
321#define PORT (1900)
322#define UPNP_MCAST_ADDR "239.255.255.250"
323
324/* upnpDiscover() :
325 * return a chained list of all devices found or NULL if
326 * no devices was found.
327 * It is up to the caller to free the chained list
328 * delay is in millisecond (poll) */
329struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
330                              const char * minissdpdsock)
331{
332        struct UPNPDev * tmp;
333        struct UPNPDev * devlist = 0;
334        int opt = 1;
335        static const char MSearchMsgFmt[] = 
336        "M-SEARCH * HTTP/1.1\r\n"
337        "HOST: " UPNP_MCAST_ADDR ":" "1900" "\r\n"
338        "ST: %s\r\n"
339        "MAN: \"ssdp:discover\"\r\n"
340        "MX: 3\r\n"
341        "\r\n";
342        static const char * const deviceList[] = {
343                "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
344                "urn:schemas-upnp-org:service:WANIPConnection:1",
345                "urn:schemas-upnp-org:service:WANPPPConnection:1",
346                "upnp:rootdevice",
347                0
348        };
349        int deviceIndex = 0;
350        char bufr[1536];        /* reception and emission buffer */
351        int sudp;
352        int n;
353        struct sockaddr_in sockudp_r, sockudp_w;
354
355#ifndef WIN32
356        /* first try to get infos from minissdpd ! */
357        if(!minissdpdsock)
358                minissdpdsock = "/var/run/minissdpd.sock";
359        while(!devlist && deviceList[deviceIndex]) {
360                devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
361                                                  minissdpdsock);
362                /* We return what we have found if it was not only a rootdevice */
363                if(devlist && !strstr(deviceList[deviceIndex], "rootdevice"))
364                        return devlist;
365                deviceIndex++;
366        }
367        deviceIndex = 0;
368#endif
369        /* fallback to direct discovery */
370#ifdef WIN32
371        sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
372#else
373        sudp = socket(PF_INET, SOCK_DGRAM, 0);
374#endif
375        if(sudp < 0)
376        {
377                PRINT_SOCKET_ERROR("socket");
378                return NULL;
379        }
380    /* reception */
381    memset(&sockudp_r, 0, sizeof(struct sockaddr_in));
382    sockudp_r.sin_family = AF_INET;
383#ifdef TX_FROM_UPNP_PORT
384    sockudp_r.sin_port = htons(PORT);
385#endif
386    sockudp_r.sin_addr.s_addr = INADDR_ANY;
387    /* emission */
388    memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
389    sockudp_w.sin_family = AF_INET;
390    sockudp_w.sin_port = htons(PORT);
391    sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
392
393#ifdef WIN32
394        if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
395#else
396        if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
397#endif
398        {
399                PRINT_SOCKET_ERROR("setsockopt");
400                return NULL;
401        }
402
403        if(multicastif)
404        {
405                struct in_addr mc_if;
406                mc_if.s_addr = inet_addr(multicastif);
407        sockudp_r.sin_addr.s_addr = mc_if.s_addr;
408                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
409                {
410                        PRINT_SOCKET_ERROR("setsockopt");
411                }
412        }
413
414        /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
415    if (bind(sudp, (struct sockaddr *)&sockudp_r, sizeof(struct sockaddr_in)) != 0)
416        {
417        PRINT_SOCKET_ERROR("bind");
418                closesocket(sudp);
419                return NULL;
420    }
421
422        /* receiving SSDP response packet */
423        for(n = 0;;)
424        {
425        if(n == 0)
426        {
427                /* sending the SSDP M-SEARCH packet */
428                n = snprintf(bufr, sizeof(bufr),
429                             MSearchMsgFmt, deviceList[deviceIndex++]);
430                /*printf("Sending %s", bufr);*/
431                n = sendto(sudp, bufr, n, 0,
432                           (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
433                if (n < 0) {
434                        PRINT_SOCKET_ERROR("sendto");
435                        closesocket(sudp);
436                        return devlist;
437                }
438        }
439        /* Waiting for SSDP REPLY packet to M-SEARCH */
440        n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
441        if (n < 0) {
442                /* error */
443                closesocket(sudp);
444                return devlist;
445        } else if (n == 0) {
446                /* no data or Time Out */
447                if (devlist || (deviceList[deviceIndex] == 0)) {
448                        /* no more device type to look for... */
449                        closesocket(sudp);
450                        return devlist;
451                }
452        } else {
453                const char * descURL=NULL;
454                int urlsize=0;
455                const char * st=NULL;
456                int stsize=0;
457        /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
458                parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
459                if(st&&descURL)
460                {
461                        /*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
462                               stsize, st, urlsize, descURL); */
463                        tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
464                        tmp->pNext = devlist;
465                        tmp->descURL = tmp->buffer;
466                        tmp->st = tmp->buffer + 1 + urlsize;
467                        memcpy(tmp->buffer, descURL, urlsize);
468                        tmp->buffer[urlsize] = '\0';
469                        memcpy(tmp->buffer + urlsize + 1, st, stsize);
470                        tmp->buffer[urlsize+1+stsize] = '\0';
471                        devlist = tmp;
472                }
473        }
474        }
475}
476
477/* freeUPNPDevlist() should be used to
478 * free the chained list returned by upnpDiscover() */
479void freeUPNPDevlist(struct UPNPDev * devlist)
480{
481        struct UPNPDev * next;
482        while(devlist)
483        {
484                next = devlist->pNext;
485                free(devlist);
486                devlist = next;
487        }
488}
489
490static void
491url_cpy_or_cat(char * dst, const char * src, int n)
492{
493        if(  (src[0] == 'h')
494           &&(src[1] == 't')
495           &&(src[2] == 't')
496           &&(src[3] == 'p')
497           &&(src[4] == ':')
498           &&(src[5] == '/')
499           &&(src[6] == '/'))
500        {
501                strncpy(dst, src, n);
502        }
503        else
504        {
505                int l = strlen(dst);
506                if(src[0] != '/')
507                        dst[l++] = '/';
508                if(l<=n)
509                        strncpy(dst + l, src, n - l);
510        }
511}
512
513/* Prepare the Urls for usage...
514 */
515void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
516                 const char * descURL)
517{
518        char * p;
519        int n1, n2, n3;
520        n1 = strlen(data->urlbase);
521        if(n1==0)
522                n1 = strlen(descURL);
523        n1 += 2;        /* 1 byte more for Null terminator, 1 byte for '/' if needed */
524        n2 = n1; n3 = n1;
525        n1 += strlen(data->scpdurl);
526        n2 += strlen(data->controlurl);
527        n3 += strlen(data->controlurl_CIF);
528
529        urls->ipcondescURL = (char *)malloc(n1);
530        urls->controlURL = (char *)malloc(n2);
531        urls->controlURL_CIF = (char *)malloc(n3);
532        /* maintenant on chope la desc du WANIPConnection */
533        if(data->urlbase[0] != '\0')
534                strncpy(urls->ipcondescURL, data->urlbase, n1);
535        else
536                strncpy(urls->ipcondescURL, descURL, n1);
537        p = strchr(urls->ipcondescURL+7, '/');
538        if(p) p[0] = '\0';
539        strncpy(urls->controlURL, urls->ipcondescURL, n2);
540        strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
541       
542        url_cpy_or_cat(urls->ipcondescURL, data->scpdurl, n1);
543
544        url_cpy_or_cat(urls->controlURL, data->controlurl, n2);
545
546        url_cpy_or_cat(urls->controlURL_CIF, data->controlurl_CIF, n3);
547
548#ifdef DEBUG
549        printf("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL,
550               strlen(urls->ipcondescURL), n1);
551        printf("urls->controlURL='%s' %d n2=%d\n", urls->controlURL,
552               strlen(urls->controlURL), n2);
553        printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF,
554               strlen(urls->controlURL_CIF), n3);
555#endif
556}
557
558void
559FreeUPNPUrls(struct UPNPUrls * urls)
560{
561        if(!urls)
562                return;
563        free(urls->controlURL);
564        urls->controlURL = 0;
565        free(urls->ipcondescURL);
566        urls->ipcondescURL = 0;
567        free(urls->controlURL_CIF);
568        urls->controlURL_CIF = 0;
569}
570
571
572int ReceiveData(int socket, char * data, int length, int timeout)
573{
574    int n;
575#ifndef WIN32
576    struct pollfd fds[1]; /* for the poll */
577    fds[0].fd = socket;
578    fds[0].events = POLLIN;
579    n = poll(fds, 1, timeout);
580    if(n < 0)
581    {
582        PRINT_SOCKET_ERROR("poll");
583        return -1;
584    }
585    else if(n == 0)
586    {
587        return 0;
588    }
589#else
590    fd_set socketSet;
591    TIMEVAL timeval;
592    FD_ZERO(&socketSet);
593    FD_SET(socket, &socketSet);
594    timeval.tv_sec = timeout / 1000;
595    timeval.tv_usec = (timeout % 1000) * 1000;
596    /*n = select(0, &socketSet, NULL, NULL, &timeval);*/
597    n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
598    if(n < 0)
599    {
600        PRINT_SOCKET_ERROR("select");
601        return -1;
602    }
603    else if(n == 0)
604    {
605        return 0;
606    }   
607#endif
608        n = recv(socket, data, length, 0);
609        if(n<0)
610        {
611                PRINT_SOCKET_ERROR("recv");
612        }
613        return n;
614}
615
616int
617UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
618{
619        char status[64];
620        unsigned int uptime;
621        status[0] = '\0';
622        UPNP_GetStatusInfo(urls->controlURL, data->servicetype, status, &uptime);
623        if(0 == strcmp("Connected", status))
624        {
625                return 1;
626        }
627        else
628                return 0;
629}
630
631
632/* UPNP_GetValidIGD() :
633 * return values :
634 *     0 = NO IGD found
635 *     1 = A valid connected IGD has been found
636 *     2 = A valid IGD has been found but it reported as
637 *         not connected
638 *     3 = an UPnP device has been found but was not recognized as an IGD
639 *
640 * In any non zero return case, the urls and data structures
641 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
642 * free allocated memory.
643 */
644int
645UPNP_GetValidIGD(struct UPNPDev * devlist,
646                 struct UPNPUrls * urls,
647                                 struct IGDdatas * data,
648                                 char * lanaddr, int lanaddrlen)
649{
650        char * descXML;
651        int descXMLsize = 0;
652        struct UPNPDev * dev;
653        int ndev = 0;
654        int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
655        if(!devlist)
656        {
657#ifdef DEBUG
658                printf("Empty devlist\n");
659#endif
660                return 0;
661        }
662        for(state = 1; state <= 3; state++)
663        {
664                for(dev = devlist; dev; dev = dev->pNext)
665                {
666                        /* we should choose an internet gateway device.
667                        * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
668                        if((state >= 3) || strstr(dev->st, "InternetGatewayDevice"))
669                        {
670                                descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
671                                                        lanaddr, lanaddrlen);
672                                if(descXML)
673                                {
674                                        ndev++;
675                                        memset(data, 0, sizeof(struct IGDdatas));
676                                        memset(urls, 0, sizeof(struct UPNPUrls));
677                                        parserootdesc(descXML, descXMLsize, data);
678                                        free(descXML);
679                                        descXML = NULL;
680                                        GetUPNPUrls(urls, data, dev->descURL);
681
682#ifdef DEBUG
683                                        printf("UPNPIGD_IsConnected(%s) = %d\n",
684                                           urls->controlURL,
685                                       UPNPIGD_IsConnected(urls, data));
686#endif
687                                        if((state >= 2) || UPNPIGD_IsConnected(urls, data))
688                                                return state;
689                                        FreeUPNPUrls(urls);
690                                        memset(data, 0, sizeof(struct IGDdatas));
691                                }
692#ifdef DEBUG
693                                else
694                                {
695                                        printf("error getting XML description %s\n", dev->descURL);
696                                }
697#endif
698                        }
699                }
700        }
701        return 0;
702}
703
704/* UPNP_GetIGDFromUrl()
705 * Used when skipping the discovery process.
706 * return value :
707 *   0 - Not ok
708 *   1 - OK */
709int
710UPNP_GetIGDFromUrl(const char * rootdescurl,
711                   struct UPNPUrls * urls,
712                   struct IGDdatas * data,
713                   char * lanaddr, int lanaddrlen)
714{
715        char * descXML;
716        int descXMLsize = 0;
717        descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
718                                       lanaddr, lanaddrlen);
719        if(descXML) {
720                memset(data, 0, sizeof(struct IGDdatas));
721                memset(urls, 0, sizeof(struct UPNPUrls));
722                parserootdesc(descXML, descXMLsize, data);
723                free(descXML);
724                descXML = NULL;
725                GetUPNPUrls(urls, data, rootdescurl);
726                return 1;
727        } else {
728                return 0;
729        }
730}
731
Note: See TracBrowser for help on using the repository browser.