Annotation of embedaddon/miniupnpd/miniupnpc-async/miniupnpc-async.c, revision 1.1

1.1     ! misho       1: /* $Id: miniupnpc-async.c,v 1.19 2014/11/07 12:05:40 nanard Exp $ */
        !             2: /* miniupnpc-async
        !             3:  * Copyright (c) 2008-2017, Thomas BERNARD <miniupnp@free.fr>
        !             4:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
        !             5:  *
        !             6:  * Permission to use, copy, modify, and/or distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
        !            17: #include <stdlib.h>
        !            18: #include <string.h>
        !            19: #include <sys/types.h>
        !            20: #include <sys/socket.h>
        !            21: #include <netinet/in.h>
        !            22: #include <arpa/inet.h>
        !            23: #include <net/if.h>
        !            24: #include <stdio.h>
        !            25: #ifdef WIN32
        !            26: #include <winsock2.h>
        !            27: #include <ws2tcpip.h>
        !            28: #include <io.h>
        !            29: #define PRINT_SOCKET_ERROR printf
        !            30: #define SOCKET_ERROR GetWSALastError()
        !            31: #define WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
        !            32: #else
        !            33: #include <unistd.h>
        !            34: #include <errno.h>
        !            35: #define closesocket close
        !            36: #define PRINT_SOCKET_ERROR perror
        !            37: #define SOCKET_ERROR errno
        !            38: #define WOULDBLOCK(err) (err == EAGAIN || err == EWOULDBLOCK)
        !            39: #endif
        !            40: #include "miniupnpc-async.h"
        !            41: #include "parsessdpreply.h"
        !            42: #include "upnputils.h"
        !            43: #include "minixml.h"
        !            44: #include "igd_desc_parse.h"
        !            45: #include "upnpreplyparse.h"
        !            46: 
        !            47: #ifndef MIN
        !            48: #define MIN(x,y) (((x)<(y))?(x):(y))
        !            49: #endif /* MIN */
        !            50: 
        !            51: #ifndef MAXHOSTNAMELEN
        !            52: #define MAXHOSTNAMELEN 64
        !            53: #endif /* MAXHOSTNAMELEN */
        !            54: 
        !            55: #define SSDP_PORT 1900
        !            56: #define SSDP_MCAST_ADDR "239.255.255.250"
        !            57: #define XSTR(s) STR(s)
        !            58: #define STR(s) #s
        !            59: 
        !            60: #ifdef DEBUG
        !            61: #define debug_printf(...) fprintf(stderr, __VA_ARGS__)
        !            62: #else
        !            63: #define debug_printf(...)
        !            64: #endif
        !            65: 
        !            66: /* stuctures */
        !            67: 
        !            68: struct upnp_args {
        !            69:        const char * elt;
        !            70:        const char * val;
        !            71: };
        !            72: 
        !            73: /* private functions */
        !            74: 
        !            75: static int upnpc_connect(upnpc_device_t * p, const char * url);
        !            76: static int upnpc_send_request(upnpc_device_t * p);
        !            77: 
        !            78: 
        !            79: /* parse_msearch_reply()
        !            80:  * the last 4 arguments are filled during the parsing :
        !            81:  *    - location/locationsize : "location:" field of the SSDP reply packet
        !            82:  *    - st/stsize : "st:" field of the SSDP reply packet.
        !            83:  * The strings are NOT null terminated */
        !            84: static void
        !            85: parse_msearch_reply(const char * reply, int size,
        !            86:                     const char * * location, unsigned int * locationsize,
        !            87:                     const char * * st, unsigned int * stsize)
        !            88: {
        !            89:        int a, b, i;
        !            90:        i = 0;  /* current character index */
        !            91:        a = i;  /* start of the line */
        !            92:        b = 0;  /* end of the "header" (position of the colon) */
        !            93:        while(i<size) {
        !            94:                switch(reply[i]) {
        !            95:                case ':':
        !            96:                        if(b==0) {
        !            97:                                b = i; /* end of the "header" */
        !            98:                        }
        !            99:                        break;
        !           100:                case '\x0a':
        !           101:                case '\x0d':
        !           102:                        if(b!=0) {
        !           103:                                /* skip the colon and white spaces */
        !           104:                                do { b++; } while(reply[b]==' ' && b<size);
        !           105:                                if(0==strncasecmp(reply+a, "location:", 9)) {
        !           106:                                        *location = reply+b;
        !           107:                                        *locationsize = i-b;
        !           108:                                } else if(0==strncasecmp(reply+a, "st:", 3)) {
        !           109:                                        *st = reply+b;
        !           110:                                        *stsize = i-b;
        !           111:                                }
        !           112:                                b = 0;
        !           113:                        }
        !           114:                        a = i+1;
        !           115:                        break;
        !           116:                default:
        !           117:                        break;
        !           118:                }
        !           119:                i++;
        !           120:        }
        !           121: }
        !           122: 
        !           123: static int upnpc_send_ssdp_msearch(upnpc_t * p, const char * device, unsigned int mx)
        !           124: {
        !           125:        /* envoyer les packets de M-SEARCH discovery sur le socket ssdp */
        !           126:        int n;
        !           127:        char bufr[1024];
        !           128:        struct sockaddr_in addr;
        !           129:        static const char MSearchMsgFmt[] = 
        !           130:        "M-SEARCH * HTTP/1.1\r\n"
        !           131:        "HOST: " SSDP_MCAST_ADDR ":" XSTR(SSDP_PORT) "\r\n"
        !           132:        "ST: %s\r\n"
        !           133:        "MAN: \"ssdp:discover\"\r\n"
        !           134:        "MX: %u\r\n"
        !           135:        "\r\n";
        !           136: 
        !           137:        memset(&addr, 0, sizeof(struct sockaddr_in));
        !           138:        addr.sin_family = AF_INET;
        !           139:     addr.sin_port = htons(SSDP_PORT);
        !           140:     addr.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
        !           141:        n = snprintf(bufr, sizeof(bufr),
        !           142:                     MSearchMsgFmt, device, mx);
        !           143:        debug_printf("upnpc_send_ssdp_msearch: %s", bufr);
        !           144:        n = sendto(p->ssdp_socket, bufr, n, 0,
        !           145:                   (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
        !           146:        if (n < 0) {
        !           147:                int err = SOCKET_ERROR;
        !           148:                if(err == EINTR || WOULDBLOCK(err)) {
        !           149:                        debug_printf("upnpc_send_ssdp_msearch: should try again");
        !           150:                        p->state = EUPnPSendSSDP;
        !           151:                        return 0;
        !           152:                }
        !           153:                PRINT_SOCKET_ERROR("sendto");
        !           154:                return -1;
        !           155:        }
        !           156:        p->state = EUPnPReceiveSSDP;
        !           157:        return 0;
        !           158: }
        !           159: 
        !           160: static int upnpc_set_root_desc_location(upnpc_device_t * d, const char * location, int locationsize)
        !           161: {
        !           162:        char * tmp;
        !           163:        tmp = realloc(d->root_desc_location, locationsize + 1);
        !           164:        if(tmp == 0) {
        !           165:                return -1;
        !           166:        }
        !           167:        memcpy(tmp, location, locationsize);
        !           168:        tmp[locationsize] = '\0';
        !           169:        d->root_desc_location = tmp;
        !           170:        return 0;
        !           171: }
        !           172: 
        !           173: static int upnpc_receive_and_parse_ssdp(upnpc_t * p)
        !           174: {
        !           175:        int n;
        !           176:        char bufr[1024];
        !           177:        n = recv(p->ssdp_socket, bufr, sizeof(bufr), 0);
        !           178:        if (n<0) {
        !           179:                PRINT_SOCKET_ERROR("recv");
        !           180:        } else if (n==0) {
        !           181:                debug_printf("empty packet received\n");
        !           182:        } else {
        !           183:                const char * location = NULL;
        !           184:                unsigned int locationsize;
        !           185:                const char * st = NULL;
        !           186:                unsigned int stsize;
        !           187:                debug_printf("%.*s", n, bufr);
        !           188:                parse_msearch_reply(bufr, n, &location, &locationsize, &st, &stsize);
        !           189:                debug_printf("location = '%.*s'\n", locationsize, location);
        !           190:                debug_printf("st = '%.*s'\n", stsize, st);
        !           191:                if(location != NULL) {
        !           192:                        upnpc_device_t * dev = p->device_list;
        !           193:                        while(dev != NULL) {
        !           194:                                if(dev->root_desc_location != NULL
        !           195:                                   && strlen(dev->root_desc_location) == locationsize
        !           196:                               && memcmp(dev->root_desc_location, location, locationsize) == 0) {
        !           197:                                        debug_printf("device already in list (location='%s')\n", dev->root_desc_location);
        !           198:                                        return -1;
        !           199:                                }
        !           200:                                dev = dev->next;
        !           201:                        }
        !           202:                        dev = calloc(1, sizeof(upnpc_device_t));
        !           203:                        if(dev == NULL) {
        !           204:                                p->state = EUPnPError;
        !           205:                                return -1;
        !           206:                        }
        !           207:                        if(upnpc_set_root_desc_location(dev, location, locationsize) < 0) {
        !           208:                                free(dev);
        !           209:                                p->state = EUPnPError;
        !           210:                                return -1;
        !           211:                        }
        !           212:                        dev->next = p->device_list;
        !           213:                        p->device_list = dev;
        !           214:                        dev->state = EDevGetDescConnect;
        !           215:                        upnpc_connect(dev, dev->root_desc_location);
        !           216:                } else {
        !           217:                        /* or do nothing ? */
        !           218:                        p->state = EUPnPError;
        !           219:                }
        !           220:        }
        !           221:        return 0;
        !           222: }
        !           223: 
        !           224: static int
        !           225: parseURL(const char * url,
        !           226:          char * hostname, unsigned short * port,
        !           227:          char * * path, unsigned int * scope_id)
        !           228: {
        !           229:        char * p1, *p2, *p3;
        !           230:        if(!url)
        !           231:                return 0;
        !           232:        p1 = strstr(url, "://");
        !           233:        if(!p1)
        !           234:                return 0;
        !           235:        p1 += 3;
        !           236:        if(  (url[0]!='h') || (url[1]!='t')
        !           237:           ||(url[2]!='t') || (url[3]!='p'))
        !           238:                return 0;
        !           239:        memset(hostname, 0, MAXHOSTNAMELEN + 1);
        !           240:        if(*p1 == '[') {
        !           241:                /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
        !           242:                char * scope;
        !           243:                scope = strchr(p1, '%');
        !           244:                p2 = strchr(p1, ']');
        !           245:                if(p2 && scope && scope < p2 && scope_id) {
        !           246:                        /* parse scope */
        !           247: #ifdef IF_NAMESIZE
        !           248:                        char tmp[IF_NAMESIZE];
        !           249:                        int l;
        !           250:                        scope++;
        !           251:                        /* "%25" is just '%' in URL encoding */
        !           252:                        if(scope[0] == '2' && scope[1] == '5')
        !           253:                                scope += 2;     /* skip "25" */
        !           254:                        l = p2 - scope;
        !           255:                        if(l >= IF_NAMESIZE)
        !           256:                                l = IF_NAMESIZE - 1;
        !           257:                        memcpy(tmp, scope, l);
        !           258:                        tmp[l] = '\0';
        !           259:                        *scope_id = if_nametoindex(tmp);
        !           260:                        if(*scope_id == 0) {
        !           261:                                *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
        !           262:                        }
        !           263: #else
        !           264:                        /* under windows, scope is numerical */
        !           265:                        char tmp[8];
        !           266:                        int l;
        !           267:                        scope++;
        !           268:                        /* "%25" is just '%' in URL encoding */
        !           269:                        if(scope[0] == '2' && scope[1] == '5')
        !           270:                                scope += 2;     /* skip "25" */
        !           271:                        l = p2 - scope;
        !           272:                        if(l >= (int)sizeof(tmp))
        !           273:                                l = sizeof(tmp) - 1;
        !           274:                        memcpy(tmp, scope, l);
        !           275:                        tmp[l] = '\0';
        !           276:                        *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
        !           277: #endif
        !           278:                }
        !           279:                p3 = strchr(p1, '/');
        !           280:                if(p2 && p3) {
        !           281:                        p2++;
        !           282:                        strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
        !           283:                        if(*p2 == ':') {
        !           284:                                *port = 0;
        !           285:                                p2++;
        !           286:                                while( (*p2 >= '0') && (*p2 <= '9')) {
        !           287:                                        *port *= 10;
        !           288:                                        *port += (unsigned short)(*p2 - '0');
        !           289:                                        p2++;
        !           290:                                }
        !           291:                        } else {
        !           292:                                *port = 80;
        !           293:                        }
        !           294:                        *path = p3;
        !           295:                        return 1;
        !           296:                }
        !           297:        }
        !           298:        p2 = strchr(p1, ':');
        !           299:        p3 = strchr(p1, '/');
        !           300:        if(!p3)
        !           301:                return 0;
        !           302:        if(!p2 || (p2>p3)) {
        !           303:                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
        !           304:                *port = 80;
        !           305:        } else {
        !           306:                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
        !           307:                *port = 0;
        !           308:                p2++;
        !           309:                while( (*p2 >= '0') && (*p2 <= '9')) {
        !           310:                        *port *= 10;
        !           311:                        *port += (unsigned short)(*p2 - '0');
        !           312:                        p2++;
        !           313:                }
        !           314:        }
        !           315:        *path = p3;
        !           316:        return 1;
        !           317: }
        !           318: 
        !           319: static int upnpc_connect(upnpc_device_t * p, const char * url)
        !           320: {
        !           321:        int r;
        !           322:        char hostname[MAXHOSTNAMELEN+1];
        !           323:        unsigned short port;
        !           324:        char * path;
        !           325:        unsigned int scope_id;
        !           326:        struct sockaddr_in addr;
        !           327:        socklen_t addrlen;
        !           328: 
        !           329:        /*if(p->root_desc_location == 0) {
        !           330:                p->state = EError;
        !           331:                return -1;
        !           332:        }*/
        !           333:        if(!parseURL(url/*p->root_desc_location*/, hostname, &port,
        !           334:                     &path, &scope_id)) {
        !           335:                p->state = EDevError;
        !           336:                return -1;
        !           337:        }
        !           338:        p->http_socket = socket(PF_INET, SOCK_STREAM, 0);
        !           339:        if(p->http_socket < 0) {
        !           340:                PRINT_SOCKET_ERROR("socket");
        !           341:                p->state = EDevError;
        !           342:                return -1;
        !           343:        }
        !           344:        if(!set_non_blocking(p->http_socket)) {
        !           345:                /* TODO : ERROR */
        !           346:        }
        !           347:        memset(&addr, 0, sizeof(struct sockaddr_in));
        !           348:        addr.sin_family = AF_INET;
        !           349:        inet_pton(AF_INET, hostname, &(addr.sin_addr));
        !           350:        addr.sin_port = htons(port);
        !           351:        addrlen = sizeof(struct sockaddr_in);
        !           352:        do {
        !           353:                r = connect(p->http_socket, (struct sockaddr *)&addr, addrlen);
        !           354:                if(r < 0) {
        !           355:                        if(errno == EINPROGRESS) {
        !           356:                                /*p->state = EGetDescConnect;*/
        !           357:                                return 0;
        !           358:                        } else if(errno != EINTR) {
        !           359:                                PRINT_SOCKET_ERROR("connect");
        !           360:                                p->state = EDevError;
        !           361:                                return -1;
        !           362:                        }
        !           363:                }
        !           364:        } while(r < 0 && errno == EINTR);
        !           365:        if(p->state == EDevGetDescConnect) {
        !           366:                p->state = EDevGetDescRequest;
        !           367:        } else {
        !           368:                p->state = EDevSoapRequest;
        !           369:        }
        !           370:        upnpc_send_request(p);
        !           371:        return 0;
        !           372: }
        !           373: 
        !           374: static int upnpc_complete_connect(upnpc_device_t * p)
        !           375: {
        !           376:        socklen_t len;
        !           377:        int err;
        !           378:        len = sizeof(err);
        !           379:        if(getsockopt(p->http_socket, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
        !           380:                PRINT_SOCKET_ERROR("getsockopt");
        !           381:                p->state = EDevError;
        !           382:                return -1;
        !           383:        }
        !           384:        if(err != 0) {
        !           385:                debug_printf("connect failed %d\n", err);
        !           386:                p->state = EDevError;
        !           387:                return -1;
        !           388:        }
        !           389:        if(p->state == EDevGetDescConnect)
        !           390:                p->state = EDevGetDescRequest;
        !           391:        else
        !           392:                p->state = EDevSoapRequest;
        !           393:        upnpc_send_request(p);
        !           394:        return 0;
        !           395: }
        !           396: 
        !           397: static int upnpc_send_request(upnpc_device_t * p)
        !           398: {
        !           399:        ssize_t n;
        !           400:        static const char reqfmt[] = "GET %s HTTP/1.1\r\n"
        !           401:                "Host: %s:%hu\r\n"
        !           402:                "Connection: Close\r\n"
        !           403:                "User-Agent: MiniUPnPc-async\r\n"
        !           404:                "\r\n";
        !           405: 
        !           406:        /* retrieve "our" IP address used to connect to the UPnP device */
        !           407:        p->selfaddrlen = sizeof(struct sockaddr_storage);
        !           408:        if(getsockname(p->http_socket, (struct sockaddr *)&p->selfaddr, &p->selfaddrlen) < 0) {
        !           409:                PRINT_SOCKET_ERROR("getsockname()");
        !           410:        }
        !           411: 
        !           412:        if(p->http_request == NULL) {
        !           413:                char hostname[MAXHOSTNAMELEN+1];
        !           414:                unsigned short port;
        !           415:                char * path;
        !           416:                unsigned int scope_id;
        !           417:                int len;
        !           418:                if(!parseURL(p->root_desc_location, hostname, &port,
        !           419:                         &path, &scope_id)) {
        !           420:                        p->state = EDevError;
        !           421:                        return -1;
        !           422:                }
        !           423:                len = snprintf(NULL, 0, reqfmt, path, hostname, port);
        !           424:                p->http_request = malloc(len + 1);
        !           425:                if(p->http_request == NULL) {
        !           426:                        p->state = EDevError;
        !           427:                        return -1;
        !           428:                }
        !           429:                p->http_request_len = snprintf(p->http_request, len + 1,
        !           430:                                               reqfmt, path, hostname, port);
        !           431:                p->http_request_sent = 0;
        !           432:        }
        !           433:        n = send(p->http_socket, p->http_request + p->http_request_sent,
        !           434:                 p->http_request_len - p->http_request_sent, 0/* flags */);
        !           435:        if(n < 0) {
        !           436:                PRINT_SOCKET_ERROR("send");
        !           437:                p->state = EDevError;
        !           438:                return -1;
        !           439:        } else {
        !           440:                debug_printf("sent %d bytes\n", (int)n);
        !           441:                /*if(n == 0) {
        !           442:                        p->state = EError;
        !           443:                        return -1;
        !           444:                }*/
        !           445:                p->http_request_sent += n;
        !           446:                if(p->http_request_sent >= p->http_request_len) {
        !           447:                        /* all bytes sent */
        !           448: #if 0
        !           449:                        shutdown(p->http_socket, SHUT_WR);      /* some routers don't like that */
        !           450: #endif
        !           451:                        free(p->http_request);
        !           452:                        p->http_request = NULL;
        !           453:                        p->http_request_len = 0;
        !           454:                        if(p->state == EDevGetDescRequest)
        !           455:                                p->state = EDevGetDescResponse;
        !           456:                        else
        !           457:                                p->state = EDevSoapResponse;
        !           458:                        free(p->http_response);
        !           459:                        p->http_response = NULL;
        !           460:                        p->http_response_received = 0;
        !           461:                        p->http_response_end_of_headers = 0;
        !           462:                        /* get response */
        !           463:                }
        !           464:        }
        !           465:        return 0;
        !           466: }
        !           467: 
        !           468: static int upnpc_parse_headers(upnpc_device_t * p)
        !           469: {
        !           470:        /* search for CR LF CR LF (end of headers)
        !           471:         * recognize also LF LF */
        !           472:        int i = 0;
        !           473:        while(i < (p->http_response_received-1) &&
        !           474:              p->http_response_end_of_headers == 0) {
        !           475:                if(p->http_response[i] == '\r') {
        !           476:                        i++;
        !           477:                        if(p->http_response[i] == '\n') {
        !           478:                                i++;
        !           479:                                if(i < p->http_response_received && p->http_response[i] == '\r') {
        !           480:                                        i++;
        !           481:                                        if(i < p->http_response_received && p->http_response[i] == '\n') {
        !           482:                                                p->http_response_end_of_headers = i + 1;
        !           483:                                        }
        !           484:                                }
        !           485:                        }
        !           486:                } else if(p->http_response[i] == '\n') {
        !           487:                        i++;
        !           488:                        if(p->http_response[i] == '\n') {
        !           489:                                p->http_response_end_of_headers = i + 1;
        !           490:                        }
        !           491:                }
        !           492:                i++;
        !           493:        }
        !           494:        if(p->http_response_end_of_headers != 0) {
        !           495:                int colon = 0;
        !           496:                int linestart = 0;
        !           497:                int valuestart = 0;
        !           498:                p->http_response_code = -1;
        !           499:                for(i = 0; i < p->http_response_end_of_headers - 1; i++) {
        !           500:                        if(linestart == 0) {
        !           501:                                /* reading HTTP response code on the 1st line */
        !           502:                                if(p->http_response[i] == ' ' && p->http_response_code < 0)
        !           503:                                        p->http_response_code = 0;
        !           504:                                else if(p->http_response[i] >= '0' && p->http_response[i] <= '9') {
        !           505:                                        p->http_response_code = p->http_response_code * 10 + (p->http_response[i] - '0');
        !           506:                                } else if(p->http_response[i] == ' ')
        !           507:                                        linestart = 1;
        !           508:                        }
        !           509:                        if(colon <= linestart && p->http_response[i] == ':') {
        !           510:                                colon = i;
        !           511:                                while(i < p->http_response_end_of_headers - 1 &&
        !           512:                                      (p->http_response[i+1] == ' ' || p->http_response[i+1] == '\t'))
        !           513:                                        i++;
        !           514:                                valuestart = i + 1;
        !           515:                        } else if(p->http_response[i + 1] == '\r' ||
        !           516:                                  p->http_response[i + 1] == '\n') {
        !           517:                                if(colon > linestart && valuestart > colon) {
        !           518:                                        debug_printf("header='%.*s', value='%.*s'\n",
        !           519:                                               colon-linestart, p->http_response+linestart,
        !           520:                                               i+1-valuestart, p->http_response+valuestart);
        !           521:                                        if(0==strncasecmp(p->http_response+linestart, "content-length", colon-linestart)) {
        !           522:                                                p->http_response_content_length = atoi(p->http_response + valuestart);
        !           523:                                                debug_printf("Content-Length: %d\n", p->http_response_content_length);
        !           524:                                                if(p->http_response_content_length < 0) {
        !           525:                                                        debug_printf("Content-Length overflow ? setting to 0\n");
        !           526:                                                        p->http_response_content_length = 0;
        !           527:                                                }
        !           528:                                        } else if(0==strncasecmp(p->http_response+linestart, "transfer-encoding", colon-linestart)
        !           529:                                                   && 0==strncasecmp(p->http_response+valuestart, "chunked", 7)) {
        !           530:                                                debug_printf("Chunked transfer-encoding !\n");
        !           531:                                                p->http_response_chunked = 1;
        !           532:                                        }
        !           533:                                }
        !           534:                                /* find next line */
        !           535:                                while((i < p->http_response_received) &&
        !           536:                                      (p->http_response[i]=='\r' || p->http_response[i] == '\n'))
        !           537:                                        i++;
        !           538:                                linestart = i;
        !           539:                                colon = linestart;
        !           540:                                valuestart = 0;
        !           541:                        }
        !           542:                }
        !           543:        }
        !           544:        return 0;
        !           545: }
        !           546: 
        !           547: static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl)
        !           548: {
        !           549:        int l, n;
        !           550:        char * s;
        !           551:        const char * base;
        !           552:        char * p;
        !           553:        /* if controlurl is an absolute url, return it */
        !           554:        if(0 == memcmp("http://", controlurl, 7))
        !           555:                return strdup(controlurl);
        !           556:        base = (urlbase[0] == '\0') ? root_desc_url : urlbase;
        !           557:        n = strlen(base);
        !           558:        if(n > 7) {
        !           559:                p = strchr(base + 7, '/');
        !           560:                if(p)
        !           561:                        n = p - base;
        !           562:        }
        !           563:        l = n + strlen(controlurl) + 1;
        !           564:        if(controlurl[0] != '/')
        !           565:                l++;
        !           566:        s = malloc(l);
        !           567:        if(s == NULL) return NULL;
        !           568:        memcpy(s, base, n);
        !           569:        if(controlurl[0] != '/')
        !           570:                s[n++] = '/';
        !           571:        memcpy(s + n, controlurl, l - n);
        !           572:        return s;
        !           573: }
        !           574: 
        !           575: static int upnpc_get_response(upnpc_device_t * p)
        !           576: {
        !           577:        ssize_t n;
        !           578:        ssize_t count;
        !           579:        char buffer[2048];
        !           580:        if(p->http_response_content_length > 0) {
        !           581:                count = p->http_response_content_length
        !           582:                      + p->http_response_end_of_headers
        !           583:                      - p->http_response_received;
        !           584:                if(count > (ssize_t)sizeof(buffer)) count = sizeof(buffer);
        !           585:        } else {
        !           586:                count = sizeof(buffer);
        !           587:        }
        !           588:        debug_printf("recv(..., %d)\n", (int)count);
        !           589:        n = recv(p->http_socket, buffer, count, 0/* flags */);
        !           590:        if(n < 0) {
        !           591:                if(errno == EINTR || WOULDBLOCK(errno))
        !           592:                        return 0;       /* try again later */
        !           593:                PRINT_SOCKET_ERROR("read");
        !           594:                p->state = EDevError;
        !           595:                return -1;
        !           596:        } else if(n == 0) {
        !           597:                /* receiving finished */
        !           598:                debug_printf("%.*s\n", p->http_response_received, p->http_response);
        !           599:                close(p->http_socket);
        !           600:                p->http_socket = -1;
        !           601:                /* parse */
        !           602:                if(p->http_response_end_of_headers == 0) {
        !           603:                        upnpc_parse_headers(p);
        !           604:                }
        !           605:                /* TODO : decode chunked transfer-encoding */
        !           606:                /* parse xml */
        !           607:                if(p->state == EDevGetDescResponse) {
        !           608:                        struct IGDdatas igd;
        !           609:                        struct xmlparser parser;
        !           610:                        memset(&igd, 0, sizeof(struct IGDdatas));
        !           611:                        memset(&parser, 0, sizeof(struct xmlparser));
        !           612:                        parser.xmlstart = p->http_response + p->http_response_end_of_headers;
        !           613:                        parser.xmlsize = p->http_response_received - p->http_response_end_of_headers;
        !           614:                        parser.data = &igd;
        !           615:                        parser.starteltfunc = IGDstartelt;
        !           616:                        parser.endeltfunc = IGDendelt;
        !           617:                        parser.datafunc = IGDdata;
        !           618:                        parsexml(&parser);
        !           619: #ifdef DEBUG
        !           620:                        printIGD(&igd);
        !           621: #endif /* DEBUG */
        !           622:                        p->control_conn_url = build_url_string(igd.urlbase, p->root_desc_location, igd.first.controlurl);
        !           623:                        p->control_cif_url = build_url_string(igd.urlbase, p->root_desc_location, igd.CIF.controlurl);
        !           624:                        debug_printf("control_conn_url='%s'\n", p->control_conn_url);
        !           625:                        debug_printf("control_cif_url='%s'\n", p->control_cif_url);
        !           626:                } else {
        !           627:                        ClearNameValueList(&p->soap_response_data);
        !           628:                        ParseNameValue(p->http_response + p->http_response_end_of_headers,
        !           629:                                       p->http_response_received - p->http_response_end_of_headers,
        !           630:                                       &p->soap_response_data);
        !           631:                }
        !           632:                free(p->http_response);
        !           633:                p->http_response = NULL;
        !           634:                p->http_response_received = 0;
        !           635:                p->http_response_end_of_headers = 0;
        !           636:                p->state = EDevReady;
        !           637:        } else {
        !           638:                /* receiving in progress */
        !           639:                debug_printf("received %d bytes:\n%.*s\n", (int)n, (int)n, buffer);
        !           640:                if(p->http_response == NULL) {
        !           641:                        p->http_response = malloc(n);
        !           642:                        if(p->http_response == NULL) {
        !           643:                                debug_printf("failed to malloc %d bytes\n", (int)n);
        !           644:                                p->state = EDevError;
        !           645:                                return -1;
        !           646:                        }
        !           647:                        p->http_response_received = n;
        !           648:                        memcpy(p->http_response, buffer, n);
        !           649:                } else {
        !           650:                        char * tmp = realloc(p->http_response, p->http_response_received + n);
        !           651:                        if(tmp == NULL) {
        !           652:                                debug_printf("failed to realloc %d bytes\n", (int)(p->http_response_received + n));
        !           653:                                p->state = EDevError;
        !           654:                                return -1;
        !           655:                        }
        !           656:                        p->http_response = tmp;
        !           657:                        memcpy(p->http_response + p->http_response_received, buffer, n);
        !           658:                        p->http_response_received += n;
        !           659:                }
        !           660:                if(p->http_response_end_of_headers == 0) {
        !           661:                        upnpc_parse_headers(p);
        !           662:                }
        !           663:        }
        !           664:        return 0;
        !           665: }
        !           666: 
        !           667: #define SOAPPREFIX "s"
        !           668: #define SERVICEPREFIX "u"
        !           669: #define SERVICEPREFIX2 'u'
        !           670: 
        !           671: static int upnpc_build_soap_request(upnpc_device_t * p, const char * url,
        !           672:                                     const char * service,
        !           673:                                     const char * action,
        !           674:                                     const struct upnp_args * args, int arg_count)
        !           675: {
        !           676:        char * body;
        !           677:        const char fmt_soap[] = 
        !           678:                "<?xml version=\"1.0\"?>\r\n"
        !           679:                "<" SOAPPREFIX ":Envelope "
        !           680:                "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
        !           681:                SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
        !           682:                "<" SOAPPREFIX ":Body>"
        !           683:                "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
        !           684:                "%s"
        !           685:                "</" SERVICEPREFIX ":%s>"
        !           686:                "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
        !           687:                "\r\n";
        !           688:        int body_len;
        !           689:        const char fmt_http[] =
        !           690:                "POST %s HTTP/1.1\r\n"
        !           691:                "Host: %s%s\r\n"
        !           692:                "User-Agent: MiniUPnPc-async\r\n"
        !           693:                "Content-Length: %d\r\n"
        !           694:                "Content-Type: text/xml\r\n"
        !           695:                "SOAPAction: \"%s#%s\"\r\n"
        !           696:                "Connection: Close\r\n"
        !           697:                "Cache-Control: no-cache\r\n"   /* ??? */
        !           698:                "Pragma: no-cache\r\n"
        !           699:                "\r\n"
        !           700:                "%s";
        !           701:        char hostname[MAXHOSTNAMELEN+1];
        !           702:        unsigned short port;
        !           703:        char * path;
        !           704:        unsigned int scope_id;
        !           705:        char portstr[8];
        !           706:        char * args_xml = NULL;
        !           707: 
        !           708:        if(arg_count > 0) {
        !           709:                int i;
        !           710:                size_t l, n;
        !           711:                for(i = 0, l = 0; i < arg_count; i++) {
        !           712:                        /* <ELT>VAL</ELT> */
        !           713:                        l += strlen(args[i].elt) * 2 + strlen(args[i].val) + 5;
        !           714:                }
        !           715:                args_xml = malloc(++l);
        !           716:                if(args_xml == NULL) {
        !           717:                        p->state = EDevError;
        !           718:                        return -1;
        !           719:                }
        !           720:                for(i = 0, n = 0; i < arg_count && n < l; i++) {
        !           721:                        /* <ELT>VAL</ELT> */
        !           722:                        n += snprintf(args_xml + n, l - n, "<%s>%s</%s>",
        !           723:                                      args[i].elt, args[i].val, args[i].elt);
        !           724:                }
        !           725:        }
        !           726: 
        !           727:        body_len = snprintf(NULL, 0, fmt_soap, action, service, args_xml?args_xml:"", action);
        !           728:        body = malloc(body_len + 1);
        !           729:        if(body == NULL) {
        !           730:                p->state = EDevError;
        !           731:                free(args_xml);
        !           732:                return -1;
        !           733:        }
        !           734:        if(snprintf(body, body_len + 1, fmt_soap, action, service, args_xml?args_xml:"", action) != body_len) {
        !           735:                debug_printf("snprintf() returned strange value...\n");
        !           736:        }
        !           737:        free(args_xml);
        !           738:        args_xml = NULL;
        !           739:        if(!parseURL(url, hostname, &port, &path, &scope_id)) {
        !           740:                p->state = EDevError;
        !           741:                free(body);
        !           742:                return -1;
        !           743:        }
        !           744:        if(port != 80)
        !           745:                snprintf(portstr, sizeof(portstr), ":%hu", port);
        !           746:        else
        !           747:                portstr[0] = '\0';
        !           748:        p->http_request_len = snprintf(NULL, 0, fmt_http,
        !           749:                                       path/*url*/, hostname, portstr, body_len, service, action, body);
        !           750:        free(p->http_request);
        !           751:        p->http_request = malloc(p->http_request_len + 1);
        !           752:        if(snprintf(p->http_request, p->http_request_len + 1, fmt_http,
        !           753:                    path/*url*/, hostname, portstr, body_len, service, action, body) != p->http_request_len) {
        !           754:                debug_printf("snprintf() returned strange value...\n");
        !           755:        }
        !           756:        free(body);
        !           757:        debug_printf("%s", p->http_request);
        !           758:        p->http_request_sent = 0;
        !           759:        return 0;
        !           760: }
        !           761: 
        !           762: /* public functions */
        !           763: int upnpc_init(upnpc_t * p, const char * multicastif)
        !           764: {
        !           765:        int opt = 1;
        !           766:        struct sockaddr_in addr;
        !           767:        if(!p)
        !           768:                return UPNPC_ERR_INVALID_ARGS;
        !           769:        p->state = EUPnPError;
        !           770:        memset(p, 0, sizeof(upnpc_t)); /* clean everything */
        !           771:        /* open the socket for SSDP */
        !           772:        p->ssdp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        !           773:        if(p->ssdp_socket < 0) {
        !           774:                return UPNPC_ERR_SOCKET_FAILED;
        !           775:        }
        !           776:        /* set REUSEADDR */
        !           777: #ifdef WIN32
        !           778:        if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) < 0) {
        !           779: #else
        !           780:        if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        !           781: #endif
        !           782:                /* non fatal error ! */
        !           783:        }
        !           784:        if(!set_non_blocking(p->ssdp_socket)) {
        !           785:                /* TODO log error */
        !           786:        }
        !           787: 
        !           788:        /* receive address */
        !           789:        memset(&addr, 0, sizeof(struct sockaddr_in));
        !           790:        addr.sin_family = AF_INET;
        !           791:        addr.sin_addr.s_addr = INADDR_ANY;
        !           792:        /*addr.sin_port = htons(SSDP_PORT);*/
        !           793: 
        !           794:        if(multicastif) {
        !           795:                struct in_addr mc_if;
        !           796:                mc_if.s_addr = inet_addr(multicastif);
        !           797:        addr.sin_addr.s_addr = mc_if.s_addr;
        !           798:                if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
        !           799:                        PRINT_SOCKET_ERROR("setsockopt");
        !           800:                        /* non fatal error ! */
        !           801:                }
        !           802:        }
        !           803: 
        !           804:        /* bind the socket to the ssdp address in order to receive responses */
        !           805:        if(bind(p->ssdp_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
        !           806:                close(p->ssdp_socket);
        !           807:                return UPNPC_ERR_BIND_FAILED;
        !           808:        }
        !           809: 
        !           810:        p->state = EUPnPInit;
        !           811:        return UPNPC_OK;
        !           812: }
        !           813: 
        !           814: int upnpc_finalize(upnpc_t * p)
        !           815: {
        !           816:        if(!p) return UPNPC_ERR_INVALID_ARGS;
        !           817:        if(p->ssdp_socket >= 0) {
        !           818:                close(p->ssdp_socket);
        !           819:                p->ssdp_socket = -1;
        !           820:        }
        !           821:        while(p->device_list) {
        !           822:                upnpc_device_t * next = p->device_list->next;
        !           823:                free(p->device_list->root_desc_location);
        !           824:                p->device_list->root_desc_location = NULL;
        !           825:                free(p->device_list->http_request);
        !           826:                p->device_list->http_request = NULL;
        !           827:                free(p->device_list->http_response);
        !           828:                p->device_list->http_response = NULL;
        !           829:                free(p->device_list->control_cif_url);
        !           830:                p->device_list->control_cif_url = NULL;
        !           831:                free(p->device_list->control_conn_url);
        !           832:                p->device_list->control_conn_url = NULL;
        !           833:                if(p->device_list->http_socket >= 0) {
        !           834:                        close(p->device_list->http_socket);
        !           835:                        p->device_list->http_socket = -1;
        !           836:                }
        !           837:                ClearNameValueList(&p->device_list->soap_response_data);
        !           838:                free(p->device_list);
        !           839:                p->device_list = next;
        !           840:        }
        !           841:        p->state = EUPnPFinalized;
        !           842:        return UPNPC_OK;
        !           843: }
        !           844: 
        !           845: int upnpc_get_external_ip_address(upnpc_device_t * p)
        !           846: {
        !           847:        upnpc_build_soap_request(p, p->control_conn_url,
        !           848:                                 "urn:schemas-upnp-org:service:WANIPConnection:1",
        !           849:                                 "GetExternalIPAddress", NULL, 0);
        !           850:        p->state = EDevSoapConnect;
        !           851:        upnpc_connect(p, p->control_conn_url);
        !           852:        return 0;
        !           853: }
        !           854: 
        !           855: int upnpc_get_link_layer_max_rate(upnpc_device_t * p)
        !           856: {
        !           857:        upnpc_build_soap_request(p, p->control_cif_url,
        !           858:                                 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
        !           859:                                 "GetCommonLinkProperties", NULL, 0);
        !           860:        p->state = EDevSoapConnect;
        !           861:        upnpc_connect(p, p->control_conn_url);
        !           862:        return 0;
        !           863: }
        !           864: 
        !           865: int upnpc_add_port_mapping(upnpc_device_t * p,
        !           866:                            const char * remote_host, unsigned short ext_port,
        !           867:                            unsigned short int_port, const char * int_client,
        !           868:                            const char * proto, const char * description,
        !           869:                            unsigned int lease_duration)
        !           870: {
        !           871:        struct upnp_args args[8];
        !           872:        char lease_duration_str[16];
        !           873:        char int_port_str[8];
        !           874:        char ext_port_str[8];
        !           875: 
        !           876:        if(int_client == NULL || int_port == 0 || ext_port == 0 || proto == NULL)
        !           877:                return UPNPC_ERR_INVALID_ARGS;
        !           878:        snprintf(lease_duration_str, sizeof(lease_duration_str), "%u", lease_duration);
        !           879:        snprintf(int_port_str, sizeof(int_port_str), "%hu", int_port);
        !           880:        snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
        !           881:        args[0].elt = "NewRemoteHost";
        !           882:        args[0].val = remote_host?remote_host:"";
        !           883:        args[1].elt = "NewExternalPort";
        !           884:        args[1].val = ext_port_str;
        !           885:        args[2].elt = "NewProtocol";
        !           886:        args[2].val = proto;
        !           887:        args[3].elt = "NewInternalPort";
        !           888:        args[3].val = int_port_str;
        !           889:        args[4].elt = "NewInternalClient";
        !           890:        args[4].val = int_client;
        !           891:        args[5].elt = "NewEnabled";
        !           892:        args[5].val = "1";
        !           893:        args[6].elt = "NewPortMappingDescription";
        !           894:        args[6].val = description?description:"miniupnpc-async";
        !           895:        args[7].elt = "NewLeaseDuration";
        !           896:        args[7].val = lease_duration_str;
        !           897:        upnpc_build_soap_request(p, p->control_conn_url,
        !           898:                                 "urn:schemas-upnp-org:service:WANIPConnection:1",
        !           899:                                 "AddPortMapping",
        !           900:                                 args, 8);
        !           901:        p->state = EDevSoapConnect;
        !           902:        upnpc_connect(p, p->control_conn_url);
        !           903:        return 0;
        !           904: }
        !           905: 
        !           906: #ifdef UPNPC_USE_SELECT
        !           907: int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds)
        !           908: {
        !           909:        upnpc_device_t * d;
        !           910:        int n = 0;
        !           911:        if(!p) return UPNPC_ERR_INVALID_ARGS;
        !           912:        for(d = p->device_list; d != NULL; d = d->next) {
        !           913:                switch(d->state) {
        !           914:                case EDevGetDescConnect:
        !           915:                case EDevGetDescRequest:
        !           916:                case EDevSoapConnect:
        !           917:                case EDevSoapRequest:
        !           918:                        FD_SET(d->http_socket, writefds);
        !           919:                        if(*nfds < d->http_socket)
        !           920:                                *nfds = d->http_socket;
        !           921:                        n++;
        !           922:                        break;
        !           923:                case EDevGetDescResponse:
        !           924:                case EDevSoapResponse:
        !           925:                        FD_SET(d->http_socket, readfds);
        !           926:                        if(*nfds < d->http_socket)
        !           927:                                *nfds = d->http_socket;
        !           928:                        n++;
        !           929:                        break;
        !           930:                default:
        !           931:                        break;
        !           932:                }
        !           933:        }
        !           934: 
        !           935:        switch(p->state) {
        !           936:        case EUPnPSendSSDP:
        !           937:                FD_SET(p->ssdp_socket, writefds);
        !           938:                if(*nfds < p->ssdp_socket)
        !           939:                        *nfds = p->ssdp_socket;
        !           940:                n++;
        !           941:                break;
        !           942:        case EUPnPReceiveSSDP:
        !           943:        default:
        !           944:                /* still receive SSDP responses when processing Description, etc. */
        !           945:                FD_SET(p->ssdp_socket, readfds);
        !           946:                if(*nfds < p->ssdp_socket)
        !           947:                        *nfds = p->ssdp_socket;
        !           948:                n++;
        !           949:                break;
        !           950:        }
        !           951:        return n;
        !           952: }
        !           953: 
        !           954: void upnpc_check_select_fds(upnpc_t * p, const fd_set * readfds, const fd_set * writefds)
        !           955: {
        !           956:        upnpc_device_t * d;
        !           957: 
        !           958:        p->socket_flags = 0;
        !           959:        if(FD_ISSET(p->ssdp_socket, readfds))
        !           960:                p->socket_flags = UPNPC_SSDP_READABLE;
        !           961:        if(FD_ISSET(p->ssdp_socket, writefds))
        !           962:                p->socket_flags = UPNPC_SSDP_WRITEABLE;
        !           963: 
        !           964:        for(d = p->device_list; d != NULL; d = d->next) {
        !           965:                d->socket_flags = 0;
        !           966:                if(FD_ISSET(d->http_socket, readfds))
        !           967:                        d->socket_flags = UPNPC_HTTP_READABLE;
        !           968:                if(FD_ISSET(d->http_socket, writefds))
        !           969:                        d->socket_flags = UPNPC_HTTP_WRITEABLE;
        !           970:        }
        !           971: }
        !           972: #endif
        !           973: 
        !           974: static const char * devices_to_search[] = {
        !           975:        "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
        !           976:        "urn:schemas-upnp-org:service:WANIPConnection:1",
        !           977:        "urn:schemas-upnp-org:service:WANPPPConnection:1",
        !           978:        "upnp:rootdevice",
        !           979:        0
        !           980: };
        !           981: 
        !           982: int upnpc_process(upnpc_t * p)
        !           983: {
        !           984:        upnpc_device_t * d;
        !           985: /*
        !           986: 1)     Envoyer les paquets de discovery SSDP
        !           987: 2)     Recevoir et traiter les reponses
        !           988: 3)     recup les descriptions
        !           989: 4)     tester les etats
        !           990: TODO : translate comments to English
        !           991: */
        !           992:        if(!p) return UPNPC_ERR_INVALID_ARGS;
        !           993:        debug_printf("state=%d   socket_flags=0x%04x\n", (int)p->state, p->socket_flags);
        !           994: 
        !           995:        for(d = p->device_list; d != NULL; d = d->next) {
        !           996:                switch(d->state) {
        !           997:                case EDevGetDescConnect:
        !           998:                case EDevSoapConnect:
        !           999:                        upnpc_complete_connect(d);
        !          1000:                        break;
        !          1001:                case EDevGetDescRequest:
        !          1002:                case EDevSoapRequest:
        !          1003:                        upnpc_send_request(d);
        !          1004:                        break;
        !          1005:                case EDevGetDescResponse:
        !          1006:                case EDevSoapResponse:
        !          1007:                        upnpc_get_response(d);
        !          1008:                        break;
        !          1009:                default:
        !          1010:                        break;
        !          1011:                }
        !          1012:        }
        !          1013:        /* all devices ready => ready */
        !          1014:        if(p->device_list != NULL) {
        !          1015:                d = p->device_list;
        !          1016:                while(d && d->state == EDevReady) d = d->next;
        !          1017:                p->state = (d == NULL) ? EUPnPReady : EUPnPProcessing;
        !          1018:        }
        !          1019: 
        !          1020:        if(p->socket_flags & UPNPC_SSDP_READABLE) {
        !          1021:                upnpc_receive_and_parse_ssdp(p);
        !          1022:        }
        !          1023:        switch(p->state) {
        !          1024:        case EUPnPInit:
        !          1025:                upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
        !          1026:                break;
        !          1027:        case EUPnPSendSSDP:
        !          1028:                upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
        !          1029:                break;
        !          1030:        case EUPnPReceiveSSDP:
        !          1031:                /*upnpc_receive_and_parse_ssdp(p);*/
        !          1032:                break;
        !          1033:        /*case EGetDesc:
        !          1034:                upnpc_connect(p);
        !          1035:                break;*/
        !          1036:        case EUPnPReady:
        !          1037:        case EUPnPProcessing:
        !          1038:                break;
        !          1039:        default:
        !          1040:                return UPNPC_ERR_UNKNOWN_STATE;
        !          1041:        }
        !          1042:        return UPNPC_OK;
        !          1043: }
        !          1044: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>