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

1.1     ! misho       1: /* $Id: miniupnpc-libevent.c,v 1.27 2015/07/22 13:51:09 nanard Exp $ */
        !             2: /* miniupnpc-libevent
        !             3:  * Copyright (c) 2008-2016, 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: #include <event2/event.h>
        !            26: #include <event2/buffer.h>
        !            27: /*#include <event2/bufferevent.h>*/
        !            28: #include <event2/http.h>
        !            29: #ifdef _WIN32
        !            30: #include <winsock2.h>
        !            31: #include <ws2tcpip.h>
        !            32: #include <io.h>
        !            33: #define PRINT_SOCKET_ERROR printf
        !            34: #define SOCKET_ERROR GetWSALastError()
        !            35: #define WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
        !            36: #else /* _WIN32 */
        !            37: #include <unistd.h>
        !            38: #include <errno.h>
        !            39: #define closesocket close
        !            40: #define PRINT_SOCKET_ERROR perror
        !            41: #define SOCKET_ERROR errno
        !            42: #define WOULDBLOCK(err) (err == EAGAIN || err == EWOULDBLOCK)
        !            43: #endif /* _WIN32 */
        !            44: #include "miniupnpc-libevent.h"
        !            45: #include "minixml.h"
        !            46: #include "igd_desc_parse.h"
        !            47: #include "upnpreplyparse.h"
        !            48: 
        !            49: #ifndef MIN
        !            50: #define MIN(x,y) (((x)<(y))?(x):(y))
        !            51: #endif /* MIN */
        !            52: 
        !            53: #ifndef MAXHOSTNAMELEN
        !            54: #define MAXHOSTNAMELEN 64
        !            55: #endif /* MAXHOSTNAMELEN */
        !            56: 
        !            57: #define SSDP_PORT 1900
        !            58: #define SSDP_MCAST_ADDR "239.255.255.250"
        !            59: #define XSTR(s) STR(s)
        !            60: #define STR(s) #s
        !            61: 
        !            62: #ifdef DEBUG
        !            63: #define debug_printf(...) fprintf(stderr, __VA_ARGS__)
        !            64: #else
        !            65: #define debug_printf(...) (void)0
        !            66: #endif
        !            67: 
        !            68: /* compare the beginning of a string with a constant string */
        !            69: #define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
        !            70: 
        !            71: /* stuctures */
        !            72: 
        !            73: struct upnp_args {
        !            74:        const char * elt;
        !            75:        const char * val;
        !            76: };
        !            77: 
        !            78: /* private functions */
        !            79: 
        !            80: static int upnpc_get_desc(upnpc_device_t * p, const char * url);
        !            81: static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl);
        !            82: 
        !            83: /* data */
        !            84: static const char * devices_to_search[] = {
        !            85:        "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
        !            86:        "urn:schemas-upnp-org:service:WANIPConnection:1",
        !            87:        "urn:schemas-upnp-org:service:WANPPPConnection:1",
        !            88:        "upnp:rootdevice",
        !            89:        0
        !            90: };
        !            91: 
        !            92: #ifdef DEBUG
        !            93: static void upnpc_conn_close_cb(struct evhttp_connection * conn, void * data)
        !            94: {
        !            95:        upnpc_device_t * d = (upnpc_device_t *)data;
        !            96:        debug_printf("%s %p %p\n", __func__, conn, d);
        !            97: }
        !            98: #endif /* DEBUG */
        !            99: 
        !           100: /* parse_msearch_reply()
        !           101:  * the last 4 arguments are filled during the parsing :
        !           102:  *    - location/locationsize : "location:" field of the SSDP reply packet
        !           103:  *    - st/stsize : "st:" field of the SSDP reply packet.
        !           104:  * The strings are NOT null terminated */
        !           105: static void
        !           106: parse_msearch_reply(const char * reply, int size,
        !           107:                     const char * * location, int * locationsize,
        !           108:                     const char * * st, int * stsize)
        !           109: {
        !           110:        int a, b, i;
        !           111:        i = 0;  /* current character index */
        !           112:        a = i;  /* start of the line */
        !           113:        b = 0;  /* end of the "header" (position of the colon) */
        !           114:        while(i<size) {
        !           115:                switch(reply[i]) {
        !           116:                case ':':
        !           117:                        if(b==0) {
        !           118:                                b = i; /* end of the "header" */
        !           119:                        }
        !           120:                        break;
        !           121:                case '\x0a':
        !           122:                case '\x0d':
        !           123:                        if(b!=0) {
        !           124:                                /* skip the colon and white spaces */
        !           125:                                do { b++; } while(reply[b]==' ' && b<i);
        !           126:                                if(0==strncasecmp(reply+a, "location:", 9)) {
        !           127:                                        *location = reply+b;
        !           128:                                        *locationsize = i-b;
        !           129:                                } else if(0==strncasecmp(reply+a, "st:", 3)) {
        !           130:                                        *st = reply+b;
        !           131:                                        *stsize = i-b;
        !           132:                                }
        !           133:                                b = 0;
        !           134:                        }
        !           135:                        a = i+1;
        !           136:                        break;
        !           137:                default:
        !           138:                        break;
        !           139:                }
        !           140:                i++;
        !           141:        }
        !           142: }
        !           143: 
        !           144: static void upnpc_send_ssdp_msearch(evutil_socket_t s, short events, upnpc_t * p)
        !           145: {
        !           146:        /* envoyer les packets de M-SEARCH discovery sur le socket ssdp */
        !           147:        int n;
        !           148:        char bufr[1024];
        !           149:        struct sockaddr_in addr;
        !           150:        unsigned int mx = 2;
        !           151:        static const char MSearchMsgFmt[] =
        !           152:        "M-SEARCH * HTTP/1.1\r\n"
        !           153:        "HOST: " SSDP_MCAST_ADDR ":" XSTR(SSDP_PORT) "\r\n"
        !           154:        "ST: %s\r\n"
        !           155:        "MAN: \"ssdp:discover\"\r\n"
        !           156:        "MX: %u\r\n"
        !           157:        "\r\n";
        !           158:        (void)p;
        !           159:        (void)events;
        !           160: 
        !           161:        memset(&addr, 0, sizeof(struct sockaddr_in));
        !           162:        addr.sin_family = AF_INET;
        !           163:     addr.sin_port = htons(SSDP_PORT);
        !           164:     addr.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
        !           165:        n = snprintf(bufr, sizeof(bufr),
        !           166:                     MSearchMsgFmt, devices_to_search[p->discover_device_index++], mx);
        !           167:        debug_printf("%s: %s", __func__, bufr);
        !           168:        n = sendto(s, bufr, n, 0,
        !           169:                   (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
        !           170:        if (n < 0) {
        !           171:                PRINT_SOCKET_ERROR("sendto");
        !           172:        }
        !           173: }
        !           174: 
        !           175: static int upnpc_set_root_desc_location(upnpc_device_t * d, const char * location, int locationsize)
        !           176: {
        !           177:        char * tmp;
        !           178:        tmp = realloc(d->root_desc_location, locationsize + 1);
        !           179:        if(tmp == NULL) {
        !           180:                return -1;
        !           181:        }
        !           182:        memcpy(tmp, location, locationsize);
        !           183:        tmp[locationsize] = '\0';
        !           184:        d->root_desc_location = tmp;
        !           185:        return 0;
        !           186: }
        !           187: 
        !           188: static upnpc_device_t * upnpc_find_device_with_location(upnpc_t * p, const char * location, int locationsize)
        !           189: {
        !           190:        upnpc_device_t * d;
        !           191:        for(d = p->devices; d != NULL; d = d->next) {
        !           192:                if(d->root_desc_location
        !           193:                  && ((int)strlen(d->root_desc_location) == locationsize)
        !           194:                  && (0 == memcmp(location, d->root_desc_location, locationsize)))
        !           195:                        return d;
        !           196:        }
        !           197:        return NULL;
        !           198: }
        !           199: 
        !           200: static void upnpc_receive_and_parse_ssdp(evutil_socket_t s, short events, upnpc_t * p)
        !           201: {
        !           202:        char bufr[2048];
        !           203:        ssize_t len;
        !           204: 
        !           205:        if(events == EV_TIMEOUT) {
        !           206:                /* nothing received ... */
        !           207:                debug_printf("%s() TIMEOUT\n", __func__);
        !           208:                if(!devices_to_search[p->discover_device_index]) {
        !           209:                        debug_printf("*** NO MORE DEVICES TO SEARCH ***\n");
        !           210:                        event_del(p->ev_ssdp_recv);
        !           211:                        /* no device found : report error */
        !           212:                        p->ready_cb(UPNPC_ERR_NO_DEVICE_FOUND, p, NULL, p->cb_data);
        !           213:                } else {
        !           214:                        /* send another SSDP M-SEARCH packet */
        !           215:                        if(event_add(p->ev_ssdp_writable, NULL)) {
        !           216:                                debug_printf("event_add FAILED\n");
        !           217:                        }
        !           218:                }
        !           219:                return;
        !           220:        }
        !           221:        len = recv(s, bufr, sizeof(bufr), 0);
        !           222:        debug_printf("input %d bytes\n", (int)len);
        !           223:        if(len < 0) {
        !           224:                PRINT_SOCKET_ERROR("recv");
        !           225:        } else if(len == 0) {
        !           226:                debug_printf("SSDP socket closed ?\n");
        !           227:        } else {
        !           228:                const char * location = NULL;
        !           229:                int locationsize = 0;
        !           230:                const char * st = NULL;
        !           231:                int stsize = 0;
        !           232:                debug_printf("%.*s", (int)len, bufr);
        !           233:                parse_msearch_reply(bufr, len, &location, &locationsize, &st, &stsize);
        !           234:                debug_printf("location = '%.*s'\n", locationsize, location);
        !           235:                debug_printf("st = '%.*s'\n", stsize, st);
        !           236:                if(location != NULL) {
        !           237:                        upnpc_device_t * device;
        !           238:                        device = upnpc_find_device_with_location(p, location, locationsize);
        !           239:                        if(device) {
        !           240:                                debug_printf("device already known\n");
        !           241:                        } else {
        !           242:                                device = malloc(sizeof(upnpc_device_t));
        !           243:                                if(device == NULL) {
        !           244:                                        debug_printf("Memory allocation error\n");
        !           245:                                        return;
        !           246:                                }
        !           247:                                memset(device, 0, sizeof(upnpc_device_t));
        !           248:                                device->parent = p;
        !           249:                                device->next = p->devices;
        !           250:                                p->devices = device;
        !           251:                                if(upnpc_set_root_desc_location(device, location, locationsize) < 0) {
        !           252:                                        return;
        !           253:                                }
        !           254:                                if(upnpc_get_desc(device, device->root_desc_location)) {
        !           255:                                        debug_printf("FAILED to request device root description\n");
        !           256:                                }
        !           257:                        }
        !           258: #if 0
        !           259:                        event_del(p->ev_ssdp_recv);     /* stop receiving SSDP responses */
        !           260: #endif
        !           261:                } else {
        !           262:                        /* or do nothing ? */
        !           263:                        debug_printf("no location\n");
        !           264:                }
        !           265:        }
        !           266: }
        !           267: 
        !           268: static int
        !           269: parseURL(const char * url,
        !           270:          char * hostname, unsigned short * port,
        !           271:          char * * path, unsigned int * scope_id)
        !           272: {
        !           273:        char * p1, *p2, *p3;
        !           274:        if(!url)
        !           275:                return 0;
        !           276:        p1 = strstr(url, "://");
        !           277:        if(!p1)
        !           278:                return 0;
        !           279:        p1 += 3;
        !           280:        if(  (url[0]!='h') || (url[1]!='t')
        !           281:           ||(url[2]!='t') || (url[3]!='p'))
        !           282:                return 0;
        !           283:        memset(hostname, 0, MAXHOSTNAMELEN + 1);
        !           284:        if(*p1 == '[') {
        !           285:                /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
        !           286:                char * scope;
        !           287:                scope = strchr(p1, '%');
        !           288:                p2 = strchr(p1, ']');
        !           289:                if(p2 && scope && scope < p2 && scope_id) {
        !           290:                        /* parse scope */
        !           291: #ifdef IF_NAMESIZE
        !           292:                        char tmp[IF_NAMESIZE];
        !           293:                        int l;
        !           294:                        scope++;
        !           295:                        /* "%25" is just '%' in URL encoding */
        !           296:                        if(scope[0] == '2' && scope[1] == '5')
        !           297:                                scope += 2;     /* skip "25" */
        !           298:                        l = p2 - scope;
        !           299:                        if(l >= IF_NAMESIZE)
        !           300:                                l = IF_NAMESIZE - 1;
        !           301:                        memcpy(tmp, scope, l);
        !           302:                        tmp[l] = '\0';
        !           303:                        *scope_id = if_nametoindex(tmp);
        !           304:                        if(*scope_id == 0) {
        !           305:                                *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
        !           306:                        }
        !           307: #else /* IF_NAMESIZE */
        !           308:                        /* under windows, scope is numerical */
        !           309:                        char tmp[8];
        !           310:                        int l;
        !           311:                        scope++;
        !           312:                        /* "%25" is just '%' in URL encoding */
        !           313:                        if(scope[0] == '2' && scope[1] == '5')
        !           314:                                scope += 2;     /* skip "25" */
        !           315:                        l = p2 - scope;
        !           316:                        if(l >= (int)sizeof(tmp))
        !           317:                                l = sizeof(tmp) - 1;
        !           318:                        memcpy(tmp, scope, l);
        !           319:                        tmp[l] = '\0';
        !           320:                        *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
        !           321: #endif /* IF_NAMESIZE */
        !           322:                }
        !           323:                p3 = strchr(p1, '/');
        !           324:                if(p2 && p3) {
        !           325:                        p2++;
        !           326:                        strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
        !           327:                        if(*p2 == ':') {
        !           328:                                *port = 0;
        !           329:                                p2++;
        !           330:                                while( (*p2 >= '0') && (*p2 <= '9')) {
        !           331:                                        *port *= 10;
        !           332:                                        *port += (unsigned short)(*p2 - '0');
        !           333:                                        p2++;
        !           334:                                }
        !           335:                        } else {
        !           336:                                *port = 80;
        !           337:                        }
        !           338:                        *path = p3;
        !           339:                        return 1;
        !           340:                }
        !           341:        }
        !           342:        p2 = strchr(p1, ':');
        !           343:        p3 = strchr(p1, '/');
        !           344:        if(!p3)
        !           345:                return 0;
        !           346:        if(!p2 || (p2>p3)) {
        !           347:                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
        !           348:                *port = 80;
        !           349:        } else {
        !           350:                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
        !           351:                *port = 0;
        !           352:                p2++;
        !           353:                while( (*p2 >= '0') && (*p2 <= '9')) {
        !           354:                        *port *= 10;
        !           355:                        *port += (unsigned short)(*p2 - '0');
        !           356:                        p2++;
        !           357:                }
        !           358:        }
        !           359:        *path = p3;
        !           360:        return 1;
        !           361: }
        !           362: 
        !           363: static void upnpc_desc_received(struct evhttp_request * req, void * pvoid)
        !           364: {
        !           365:        size_t len;
        !           366:        unsigned char * data;
        !           367:        struct evbuffer * input_buffer;
        !           368:        struct IGDdatas igd;
        !           369:        struct xmlparser parser;
        !           370:        upnpc_device_t * d = (upnpc_device_t *)pvoid;
        !           371: 
        !           372:        if(req == NULL) {
        !           373:                debug_printf("%s(%p, %p) NULL argument !\n", __func__, req, pvoid);
        !           374:                return;
        !           375:        }
        !           376:        input_buffer = evhttp_request_get_input_buffer(req);
        !           377:        len = evbuffer_get_length(input_buffer);
        !           378:        data = evbuffer_pullup(input_buffer, len);
        !           379:        debug_printf("%s %d (%d bytes)\n", __func__, evhttp_request_get_response_code(req), (int)len);
        !           380:        if(evhttp_request_get_response_code(req) != HTTP_OK) {
        !           381:                d->parent->ready_cb(evhttp_request_get_response_code(req), d->parent, d, d->parent->cb_data);
        !           382:                return;
        !           383:        }
        !           384:        if(data == NULL) {
        !           385:                d->parent->ready_cb(UPNPC_ERR_ROOT_DESC_ERROR, d->parent, d, d->parent->cb_data);
        !           386:                return;
        !           387:        }
        !           388:        debug_printf("%.*s\n", (int)len, (char *)data);
        !           389: 
        !           390:        memset(&igd, 0, sizeof(struct IGDdatas));
        !           391:        memset(&parser, 0, sizeof(struct xmlparser));
        !           392:        parser.xmlstart = (char *)data;
        !           393:        parser.xmlsize = len;
        !           394:        parser.data = &igd;
        !           395:        parser.starteltfunc = IGDstartelt;
        !           396:        parser.endeltfunc = IGDendelt;
        !           397:        parser.datafunc = IGDdata;
        !           398:        parsexml(&parser);
        !           399: #ifdef DEBUG
        !           400:        printIGD(&igd);
        !           401: #endif /* DEBUG */
        !           402:        d->control_conn_url = build_url_string(igd.urlbase, d->root_desc_location, igd.first.controlurl);
        !           403:        d->event_conn_url = build_url_string(igd.urlbase, d->root_desc_location, igd.first.eventsuburl);
        !           404:        d->conn_service_type = strdup(igd.first.servicetype);
        !           405:        d->control_cif_url = build_url_string(igd.urlbase, d->root_desc_location, igd.CIF.controlurl);
        !           406:        d->event_cif_url = build_url_string(igd.urlbase, d->root_desc_location, igd.CIF.eventsuburl);
        !           407:        d->cif_service_type = strdup(igd.CIF.servicetype);
        !           408:        debug_printf("control_conn_url='%s'\n  (service_type='%s')\n",
        !           409:                     d->control_conn_url, d->conn_service_type);
        !           410:        debug_printf("event_conn_url='%s'\n", d->event_conn_url);
        !           411:        debug_printf("control_cif_url='%s'\n  (service_type='%s')\n",
        !           412:                     d->control_cif_url, d->cif_service_type);
        !           413: 
        !           414:        if((d->cif_service_type == NULL)
        !           415:          || (d->cif_service_type[0] == '\0')
        !           416:          || (!COMPARE(d->cif_service_type, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))) {
        !           417:                d->parent->ready_cb(UPNPC_ERR_NOT_IGD, d->parent, d, d->parent->cb_data);
        !           418:        } else {
        !           419:                d->state |= UPNPC_DEVICE_GETSTATUS;
        !           420:                upnpc_get_status_info(d);
        !           421:        }
        !           422: }
        !           423: 
        !           424: #ifdef ENABLE_UPNP_EVENTS
        !           425: static void upnpc_subscribe_response(struct evhttp_request * req, void * pvoid)
        !           426: {
        !           427:        size_t len;
        !           428:        unsigned char * data;
        !           429:        struct evbuffer * input_buffer;
        !           430:        upnpc_device_t * d = (upnpc_device_t *)pvoid;
        !           431: 
        !           432:        if(req == NULL) {
        !           433:                debug_printf("%s(%p, %p) NULL argument !\n", __func__, req, pvoid);
        !           434:                return;
        !           435:        }
        !           436:        input_buffer = evhttp_request_get_input_buffer(req);
        !           437:        len = evbuffer_get_length(input_buffer);
        !           438:        data = evbuffer_pullup(input_buffer, len);
        !           439:        debug_printf("%s %d (%d bytes)\n", __func__, evhttp_request_get_response_code(req), (int)len);
        !           440:        d->state &= ~UPNPC_DEVICE_SOAP_REQ;
        !           441:        if(evhttp_request_get_response_code(req) != HTTP_OK) {
        !           442:                /* TODO ERROR */
        !           443:        } else {
        !           444:                const char * sid;
        !           445:                struct evkeyvalq * headers = evhttp_request_get_input_headers(req);
        !           446:                sid = evhttp_find_header(headers, "sid");
        !           447:                debug_printf("SID=%s\n", sid);
        !           448:                if(sid) {
        !           449:                        if(d->event_conn_sid)
        !           450:                                free(d->event_conn_sid);
        !           451:                        d->event_conn_sid = strdup(sid);
        !           452:                }
        !           453:        }
        !           454: }
        !           455: #endif /* ENABLE_UPNP_EVENTS */
        !           456: 
        !           457: static void upnpc_soap_response(struct evhttp_request * req, void * pvoid)
        !           458: {
        !           459:        size_t len;
        !           460:        unsigned char * data;
        !           461:        struct evbuffer * input_buffer;
        !           462:        upnpc_device_t * d = (upnpc_device_t *)pvoid;
        !           463:        int code;
        !           464: 
        !           465:        if(req == NULL) {
        !           466:                debug_printf("%s(%p, %p) NULL argument !\n", __func__, req, pvoid);
        !           467:                return;
        !           468:        }
        !           469:        code = evhttp_request_get_response_code(req);
        !           470:        input_buffer = evhttp_request_get_input_buffer(req);
        !           471:        len = evbuffer_get_length(input_buffer);
        !           472:        data = evbuffer_pullup(input_buffer, len);
        !           473:        debug_printf("%s %d (%d bytes)\n", __func__, code, (int)len);
        !           474:        debug_printf("%.*s\n", (int)len, (char *)data);
        !           475:        if(data == NULL)
        !           476:                return;
        !           477: 
        !           478:        ClearNameValueList(&d->soap_response_data);
        !           479:        ParseNameValue((char *)data, (int)len,
        !           480:                       &d->soap_response_data);
        !           481:        d->state &= ~UPNPC_DEVICE_SOAP_REQ;
        !           482:        if(d->state & UPNPC_DEVICE_READY) {
        !           483:                d->parent->soap_cb(code, d->parent, d, d->parent->cb_data);
        !           484:        } else if(d->state & UPNPC_DEVICE_GETSTATUS) {
        !           485:                const char * connection_status;
        !           486:                d->state &= ~UPNPC_DEVICE_GETSTATUS;
        !           487:                connection_status = GetValueFromNameValueList(&d->soap_response_data, "NewConnectionStatus");
        !           488:                d->state |= UPNPC_DEVICE_READY;
        !           489:                if((code == 200) && connection_status && (0 == strcmp("Connected", connection_status))) {
        !           490:                        d->parent->ready_cb(code, d->parent, d, d->parent->cb_data);
        !           491:                        d->state |= UPNPC_DEVICE_CONNECTED;
        !           492:                        event_del(d->parent->ev_ssdp_recv);
        !           493:                } else {
        !           494:                        d->parent->ready_cb(UPNPC_ERR_NOT_CONNECTED, d->parent, d, d->parent->cb_data);
        !           495:                }
        !           496:        }
        !           497: }
        !           498: 
        !           499: static int upnpc_get_desc(upnpc_device_t * d, const char * url)
        !           500: {
        !           501:        char hostname[MAXHOSTNAMELEN+1];
        !           502:        char hostname_port[MAXHOSTNAMELEN+1+6];
        !           503:        unsigned short port;
        !           504:        char * path;
        !           505:        unsigned int scope_id;
        !           506:        struct evhttp_request * req;
        !           507:        struct evkeyvalq * headers;
        !           508: 
        !           509:        /* if(d->root_desc_location == NULL) {
        !           510:                return -1;
        !           511:        } */
        !           512:        if(!parseURL(url/*d->root_desc_location*/, hostname, &port,
        !           513:                     &path, &scope_id)) {
        !           514:                return -1;
        !           515:        }
        !           516:        if(port != 80)
        !           517:                snprintf(hostname_port, sizeof(hostname_port), "%s:%hu", hostname, port);
        !           518:        else
        !           519:                strncpy(hostname_port, hostname, sizeof(hostname_port));
        !           520:        if(d->desc_conn == NULL) {
        !           521:                d->desc_conn = evhttp_connection_base_new(d->parent->base, NULL, hostname, port);
        !           522:        }
        !           523: #ifdef DEBUG
        !           524:        evhttp_connection_set_closecb(d->desc_conn, upnpc_conn_close_cb, d);
        !           525: #endif /* DEBUG */
        !           526:        /*evhttp_connection_set_timeout(p->desc_conn, 600);*/
        !           527:        req = evhttp_request_new(upnpc_desc_received/*callback*/, d);
        !           528:        headers = evhttp_request_get_output_headers(req);
        !           529:        evhttp_add_header(headers, "Host", hostname_port);
        !           530:        evhttp_add_header(headers, "Connection", "close");
        !           531:        /*evhttp_add_header(headers, "User-Agent", "***");*/
        !           532:        return evhttp_make_request(d->desc_conn, req, EVHTTP_REQ_GET, path);
        !           533: }
        !           534: 
        !           535: static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl)
        !           536: {
        !           537:        int l, n;
        !           538:        char * s;
        !           539:        const char * base;
        !           540:        char * p;
        !           541:        /* if controlurl is an absolute url, return it */
        !           542:        if(0 == memcmp("http://", controlurl, 7))
        !           543:                return strdup(controlurl);
        !           544:        base = (urlbase[0] == '\0') ? root_desc_url : urlbase;
        !           545:        n = strlen(base);
        !           546:        if(n > 7) {
        !           547:                p = strchr(base + 7, '/');
        !           548:                if(p)
        !           549:                        n = p - base;
        !           550:        }
        !           551:        l = n + strlen(controlurl) + 1;
        !           552:        if(controlurl[0] != '/')
        !           553:                l++;
        !           554:        s = malloc(l);
        !           555:        if(s == NULL) return NULL;
        !           556:        memcpy(s, base, n);
        !           557:        if(controlurl[0] != '/')
        !           558:                s[n++] = '/';
        !           559:        memcpy(s + n, controlurl, l - n);
        !           560:        return s;
        !           561: }
        !           562: 
        !           563: #define SOAPPREFIX "s"
        !           564: #define SERVICEPREFIX "u"
        !           565: #define SERVICEPREFIX2 'u'
        !           566: 
        !           567: static int upnpc_send_soap_request(upnpc_device_t * p, const char * url,
        !           568:                                    const char * service,
        !           569:                                    const char * method,
        !           570:                                    const struct upnp_args * args, int arg_count)
        !           571: {
        !           572:        char action[128];
        !           573:        char * body;
        !           574:        const char fmt_soap[] =
        !           575:                "<?xml version=\"1.0\"?>\r\n"
        !           576:                "<" SOAPPREFIX ":Envelope "
        !           577:                "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
        !           578:                SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
        !           579:                "<" SOAPPREFIX ":Body>"
        !           580:                "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
        !           581:                "%s"
        !           582:                "</" SERVICEPREFIX ":%s>"
        !           583:                "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
        !           584:                "\r\n";
        !           585:        int body_len;
        !           586:        char hostname[MAXHOSTNAMELEN+1];
        !           587:        char hostname_port[MAXHOSTNAMELEN+1+6];
        !           588:        unsigned short port;
        !           589:        char * path;
        !           590:        unsigned int scope_id;
        !           591:        char * args_xml = NULL;
        !           592:        struct evhttp_request * req;
        !           593:        struct evkeyvalq * headers;
        !           594:        struct evbuffer * buffer;
        !           595: 
        !           596:        if(p->state & UPNPC_DEVICE_SOAP_REQ) {
        !           597:                debug_printf("%s: another SOAP request in progress\n", __func__);
        !           598:                return UPNPC_ERR_REQ_IN_PROGRESS;
        !           599:        }
        !           600: 
        !           601:        if(arg_count > 0) {
        !           602:                int i;
        !           603:                size_t l, n;
        !           604:                for(i = 0, l = 0; i < arg_count; i++) {
        !           605:                        /* <ELT>VAL</ELT> */
        !           606:                        l += strlen(args[i].elt) * 2 + strlen(args[i].val) + 5;
        !           607:                }
        !           608:                args_xml = malloc(++l);
        !           609:                if(args_xml == NULL) {
        !           610:                        return -1;
        !           611:                }
        !           612:                for(i = 0, n = 0; i < arg_count && n < l; i++) {
        !           613:                        /* <ELT>VAL</ELT> */
        !           614:                        n += snprintf(args_xml + n, l - n, "<%s>%s</%s>",
        !           615:                                      args[i].elt, args[i].val, args[i].elt);
        !           616:                }
        !           617:        }
        !           618: 
        !           619:        body_len = snprintf(NULL, 0, fmt_soap, method, service, args_xml?args_xml:"", method);
        !           620:        body = malloc(body_len + 1);
        !           621:        if(body == NULL) {
        !           622:                free(args_xml);
        !           623:                return -1;
        !           624:        }
        !           625:        if(snprintf(body, body_len + 1, fmt_soap, method, service, args_xml?args_xml:"", method) != body_len) {
        !           626:                debug_printf("%s: snprintf() returned strange value...\n", __func__);
        !           627:        }
        !           628:        free(args_xml);
        !           629:        args_xml = NULL;
        !           630:        if(!parseURL(url, hostname, &port, &path, &scope_id)) {
        !           631:                free(body);
        !           632:                return -1;
        !           633:        }
        !           634:        if(port != 80)
        !           635:                snprintf(hostname_port, sizeof(hostname_port), "%s:%hu", hostname, port);
        !           636:        else
        !           637:                strncpy(hostname_port, hostname, sizeof(hostname_port));
        !           638:        snprintf(action, sizeof(action), "\"%s#%s\"", service, method);
        !           639:        if(p->soap_conn == NULL) {
        !           640:                p->soap_conn = evhttp_connection_base_new(p->parent->base, NULL, hostname, port);
        !           641:        }
        !           642:        req = evhttp_request_new(upnpc_soap_response, p);
        !           643:        headers = evhttp_request_get_output_headers(req);
        !           644:        buffer = evhttp_request_get_output_buffer(req);
        !           645:        evhttp_add_header(headers, "Host", hostname_port);
        !           646:        evhttp_add_header(headers, "SOAPAction", action);
        !           647:        evhttp_add_header(headers, "Content-Type", "text/xml");
        !           648:        /*evhttp_add_header(headers, "User-Agent", "***");*/
        !           649:        /*evhttp_add_header(headers, "Cache-Control", "no-cache");*/
        !           650:        /*evhttp_add_header(headers, "Pragma", "no-cache");*/
        !           651:        evbuffer_add(buffer, body, body_len);
        !           652:        evhttp_make_request(p->soap_conn, req, EVHTTP_REQ_POST, path);
        !           653:        free(body);
        !           654:        p->state |= UPNPC_DEVICE_SOAP_REQ;
        !           655:        return 0;
        !           656: }
        !           657: 
        !           658: #ifdef ENABLE_UPNP_EVENTS
        !           659: #define EVHTTP_REQ_NOTIFY      ((EVHTTP_REQ_MAX) << 1)
        !           660: #define EVHTTP_REQ_SUBSCRIBE ((EVHTTP_REQ_NOTIFY) << 1)
        !           661: #define EVHTTP_REQ_UNSUBSCRIBE ((EVHTTP_REQ_SUBSCRIBE) << 1)
        !           662: static int ext_methods_cb(struct evhttp_ext_method *p)
        !           663: {
        !           664:        if(p == NULL)
        !           665:                return -1;
        !           666:        if(p->method != NULL) {
        !           667:                if(strcmp(p->method, "NOTIFY") == 0) {
        !           668:                        p->type = EVHTTP_REQ_NOTIFY;
        !           669:                        p->flags = EVHTTP_METHOD_HAS_BODY;
        !           670:                } else if(strcmp(p->method, "SUBSCRIBE") == 0) {
        !           671:                        p->type = EVHTTP_REQ_SUBSCRIBE;
        !           672:                } else if(strcmp(p->method, "UNSUBSCRIBE") == 0) {
        !           673:                        p->type = EVHTTP_REQ_UNSUBSCRIBE;
        !           674:                } else {
        !           675:                        return -1;
        !           676:                }
        !           677:        } else switch(p->type) {
        !           678:                case EVHTTP_REQ_NOTIFY:
        !           679:                        p->method = "NOTIFY";
        !           680:                        p->flags = EVHTTP_METHOD_HAS_BODY;
        !           681:                        break;
        !           682:                case EVHTTP_REQ_SUBSCRIBE:
        !           683:                        p->method = "SUBSCRIBE";
        !           684:                        break;
        !           685:                case EVHTTP_REQ_UNSUBSCRIBE:
        !           686:                        p->method = "UNSUBSCRIBE";
        !           687:                        break;
        !           688:                default:
        !           689:                        return -1;
        !           690:        }
        !           691:        return 0;
        !           692: };
        !           693: 
        !           694: void upnpc_event_conn_req(struct evhttp_request * req, void * data)
        !           695: {
        !           696:        size_t len;
        !           697:        char * xml_data;
        !           698:        struct evbuffer * input_buffer;
        !           699:        struct evkeyvalq * headers;
        !           700:        const char * sid;
        !           701:        const char * nts;
        !           702:        const char * nt;
        !           703:        const char * seq;
        !           704:        struct NameValueParserData parsed_data;
        !           705:        struct NameValue * nv;
        !           706:        upnpc_device_t * d = (upnpc_device_t *)data;
        !           707: 
        !           708:        debug_printf("%s(%p, %p)\n", __func__, req, d);
        !           709:        headers = evhttp_request_get_input_headers(req);
        !           710:        input_buffer = evhttp_request_get_input_buffer(req);
        !           711:        len = evbuffer_get_length(input_buffer);
        !           712:        sid = evhttp_find_header(headers, "sid");
        !           713:        nts = evhttp_find_header(headers, "nts");
        !           714:        nt = evhttp_find_header(headers, "nt");
        !           715:        seq = evhttp_find_header(headers, "seq");
        !           716:        if(len == 0 || nts == NULL || nt == NULL) {
        !           717:                /* 400 Bad request :
        !           718:                 * The NT or NTS header field is missing
        !           719:                 * or the request is malformed. */
        !           720:                evhttp_send_reply(req, 400, "Bad Request", NULL);
        !           721:                return;
        !           722:        }
        !           723:        debug_printf("SID=%s NTS=%s SEQ=%s\n", sid, nts, seq);
        !           724:        if(sid == NULL || 0 != strcmp(sid, d->event_conn_sid)
        !           725:           || 0 != strcmp(nt, "upnp:event") || 0 != strcmp(nts, "upnp:propchange")) {
        !           726:                /* 412 Precondition Failed :
        !           727:                 *  An SID does not correspond to a known, un-expired subscription
        !           728:                 *  or the NT header field does not equal upnp:event
        !           729:                 *  or the NTS header field does not equal upnp:propchange
        !           730:                 *  or the SID header field is missing or empty.  */
        !           731:                evhttp_send_reply(req, 412, "Precondition Failed", NULL);
        !           732:                return;
        !           733:        }
        !           734:        xml_data = (char *)evbuffer_pullup(input_buffer, len);
        !           735:        /*debug_printf("%.*s\n", len, xml_data);*/
        !           736:        ParseNameValue(xml_data, len, &parsed_data);
        !           737:        for(nv = parsed_data.l_head; nv != NULL; nv = nv->l_next) {
        !           738:                if(d->parent->value_changed_cb) {
        !           739:                        d->parent->value_changed_cb(d->parent, d, d->parent->cb_data, d->conn_service_type, nv->name, nv->value);
        !           740:                } else {
        !           741:                        debug_printf("%s=%s\n", nv->name, nv->value);
        !           742:                }
        !           743:        }
        !           744:        ClearNameValueList(&parsed_data);
        !           745:        /* response : 200 OK */
        !           746:        evhttp_send_reply(req, 200, "OK", NULL);
        !           747: }
        !           748: #endif /* ENABLE_UPNP_EVENTS */
        !           749: 
        !           750: /* public functions */
        !           751: int upnpc_init(upnpc_t * p, struct event_base * base, const char * multicastif,
        !           752:                upnpc_callback_fn ready_cb, upnpc_callback_fn soap_cb, void * cb_data)
        !           753: {
        !           754:        int opt = 1;
        !           755:        struct sockaddr_in addr;
        !           756: 
        !           757:        if(p == NULL || base == NULL)
        !           758:                return UPNPC_ERR_INVALID_ARGS;
        !           759:        memset(p, 0, sizeof(upnpc_t)); /* clean everything */
        !           760:        p->base = base;
        !           761:        p->ready_cb = ready_cb;
        !           762:        p->soap_cb = soap_cb;
        !           763:        p->cb_data = cb_data;
        !           764:        p->ttl = 2;
        !           765:        /* open the socket for SSDP */
        !           766:        p->ssdp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        !           767:        if(p->ssdp_socket < 0) {
        !           768:                return UPNPC_ERR_SOCKET_FAILED;
        !           769:        }
        !           770:        /* set multicast TTL */
        !           771:        if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_TTL, &p->ttl, sizeof(p->ttl)) < 0)
        !           772:        {
        !           773:                /* not a fatal error */
        !           774:                debug_printf("setsockopt(%d, ..., IP_MULTICAST_TTL, ...) FAILED\n", p->ssdp_socket);
        !           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 /* _WIN32 */
        !           780:        if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        !           781: #endif /* _WIN32 */
        !           782:                /* non fatal error ! */
        !           783:                debug_printf("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, ...) FAILED\n", p->ssdp_socket);
        !           784:        }
        !           785:        if(evutil_make_socket_nonblocking(p->ssdp_socket) < 0) {
        !           786:                debug_printf("evutil_make_socket_nonblocking FAILED\n");
        !           787:        }
        !           788: 
        !           789:        /* receive address */
        !           790:        memset(&addr, 0, sizeof(struct sockaddr_in));
        !           791:        addr.sin_family = AF_INET;
        !           792:        addr.sin_addr.s_addr = INADDR_ANY;
        !           793:        /*addr.sin_port = htons(SSDP_PORT);*/
        !           794: 
        !           795:        if(multicastif) {
        !           796:                struct in_addr mc_if;
        !           797:                mc_if.s_addr = inet_addr(multicastif);
        !           798:        addr.sin_addr.s_addr = mc_if.s_addr;
        !           799:                if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
        !           800:                        PRINT_SOCKET_ERROR("setsockopt");
        !           801:                        /* non fatal error ! */
        !           802:                }
        !           803:        }
        !           804: 
        !           805:        /* bind the socket to the ssdp address in order to receive responses */
        !           806:        if(bind(p->ssdp_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
        !           807:                close(p->ssdp_socket);
        !           808:                return UPNPC_ERR_BIND_FAILED;
        !           809:        }
        !           810:        return UPNPC_OK;
        !           811: }
        !           812: 
        !           813: int upnpc_start(upnpc_t * p)
        !           814: {
        !           815:        struct timeval timeout;
        !           816:        if(p == NULL || p->base == NULL)
        !           817:                return UPNPC_ERR_INVALID_ARGS;
        !           818:        /* event on SSDP */
        !           819:        p->ev_ssdp_recv = event_new(p->base, p->ssdp_socket,
        !           820:                                 EV_READ|EV_PERSIST,
        !           821:                                 (event_callback_fn)upnpc_receive_and_parse_ssdp, p);
        !           822:        timeout.tv_sec = 3;
        !           823:        timeout.tv_usec = 0;
        !           824:        if(event_add(p->ev_ssdp_recv, &timeout)) {
        !           825:                debug_printf("event_add FAILED\n");
        !           826:        }
        !           827:        p->ev_ssdp_writable = event_new(p->base, p->ssdp_socket,
        !           828:                                     EV_WRITE,
        !           829:                                     (event_callback_fn)upnpc_send_ssdp_msearch, p);
        !           830:        if(event_add(p->ev_ssdp_writable, NULL)) {
        !           831:                debug_printf("event_add FAILED\n");
        !           832:        }
        !           833:        return UPNPC_OK;
        !           834: }
        !           835: 
        !           836: int upnpc_set_local_address(upnpc_t * p, const char * address, uint16_t port)
        !           837: {
        !           838:        if(!p || !address) return UPNPC_ERR_INVALID_ARGS;
        !           839:        p->local_address = strdup(address);     /* TODO check error */
        !           840:        p->local_port = port;
        !           841:        return UPNPC_OK;
        !           842: }
        !           843: 
        !           844: #ifdef ENABLE_UPNP_EVENTS
        !           845: int upnpc_set_event_callback(upnpc_t * p, upnpc_event_callback_fn cb)
        !           846: {
        !           847:        if(!p || !cb) return UPNPC_ERR_INVALID_ARGS;
        !           848:        p->value_changed_cb = cb;
        !           849:        return UPNPC_OK;
        !           850: }
        !           851: #endif /* ENABLE_UPNP_EVENTS */
        !           852: 
        !           853: static void upnpc_device_finalize(upnpc_device_t * d)
        !           854: {
        !           855:        d->state = 0;
        !           856:        free(d->root_desc_location);
        !           857:        d->root_desc_location = NULL;
        !           858:        free(d->control_cif_url);
        !           859:        d->control_cif_url = NULL;
        !           860:        free(d->event_cif_url);
        !           861:        d->event_cif_url = NULL;
        !           862:        free(d->cif_service_type);
        !           863:        d->cif_service_type = NULL;
        !           864:        free(d->control_conn_url);
        !           865:        d->control_conn_url = NULL;
        !           866:        free(d->event_conn_url);
        !           867:        d->event_conn_url = NULL;
        !           868:        free(d->conn_service_type);
        !           869:        d->conn_service_type = NULL;
        !           870:        if(d->desc_conn) {
        !           871:                evhttp_connection_free(d->desc_conn);
        !           872:                d->desc_conn = NULL;
        !           873:        }
        !           874:        if(d->soap_conn) {
        !           875:                evhttp_connection_free(d->soap_conn);
        !           876:                d->soap_conn = NULL;
        !           877:        }
        !           878:        ClearNameValueList(&d->soap_response_data);
        !           879: #ifdef ENABLE_UPNP_EVENTS
        !           880:        free(d->event_conn_sid);
        !           881:        d->event_conn_sid = NULL;
        !           882: #endif /* ENABLE_UPNP_EVENTS */
        !           883: }
        !           884: 
        !           885: int upnpc_finalize(upnpc_t * p)
        !           886: {
        !           887:        if(!p) return UPNPC_ERR_INVALID_ARGS;
        !           888:        p->discover_device_index = 0;
        !           889:        if(p->ssdp_socket >= 0) {
        !           890:                close(p->ssdp_socket);
        !           891:                p->ssdp_socket = -1;
        !           892:        }
        !           893:        if(p->ev_ssdp_recv) {
        !           894:                event_free(p->ev_ssdp_recv);
        !           895:                p->ev_ssdp_recv = NULL;
        !           896:        }
        !           897:        if(p->ev_ssdp_writable) {
        !           898:                event_free(p->ev_ssdp_writable);
        !           899:                p->ev_ssdp_writable = NULL;
        !           900:        }
        !           901:        while(p->devices != NULL) {
        !           902:                upnpc_device_t * d = p->devices;
        !           903:                upnpc_device_finalize(d);
        !           904:                p->devices = d->next;
        !           905:                free(d);
        !           906:        }
        !           907:        free(p->local_address);
        !           908:        p->local_address = NULL;
        !           909: #ifdef ENABLE_UPNP_EVENTS
        !           910:        if(p->http_server) {
        !           911:                evhttp_free(p->http_server);
        !           912:                p->http_server = NULL;
        !           913:        }
        !           914: #endif /* ENABLE_UPNP_EVENTS */
        !           915:        return UPNPC_OK;
        !           916: }
        !           917: 
        !           918: #ifdef ENABLE_UPNP_EVENTS
        !           919: int upnpc_event_subscribe(upnpc_device_t * p)
        !           920: {
        !           921:        char hostname[MAXHOSTNAMELEN+1];
        !           922:        char hostname_port[MAXHOSTNAMELEN+1+6];
        !           923:        unsigned short port;
        !           924:        char * path;
        !           925:        unsigned int scope_id;
        !           926:        struct evhttp_request * req;
        !           927:        struct evkeyvalq * headers;
        !           928:        char callback_header[7+15+1+5+9+2+1];
        !           929: 
        !           930:        if(p->parent->http_server == NULL) {
        !           931:                /* HTTP server to receive event notifications */
        !           932:                p->parent->http_server = evhttp_new(p->parent->base);
        !           933:                if(p->parent->http_server == NULL) {
        !           934:                        debug_printf("evhttp_new() FAILED\n");
        !           935:                        return -1;
        !           936:                }
        !           937:                evhttp_set_ext_method_cmp(p->parent->http_server, ext_methods_cb);
        !           938:                evhttp_set_allowed_methods(p->parent->http_server, EVHTTP_REQ_NOTIFY);
        !           939:                evhttp_set_cb(p->parent->http_server, "/evt_conn", upnpc_event_conn_req, p);
        !           940:                if(evhttp_bind_socket(p->parent->http_server, p->parent->local_address, p->parent->local_port) < 0) {
        !           941:                        debug_printf("evhttp_bind_socket() FAILED\n");
        !           942:                        return -1;
        !           943:                }
        !           944:        }
        !           945:        /*if(!parseURL(p->event_cif_url, hostname, &port, &path, &scope_id)) {*/
        !           946:        if(!parseURL(p->event_conn_url, hostname, &port, &path, &scope_id)) {
        !           947:                return -1;
        !           948:        }
        !           949:        if(port != 80)
        !           950:                snprintf(hostname_port, sizeof(hostname_port), "%s:%hu", hostname, port);
        !           951:        else
        !           952:                strncpy(hostname_port, hostname, sizeof(hostname_port));
        !           953:        if(p->soap_conn == NULL) {
        !           954:                p->soap_conn = evhttp_connection_base_new(p->parent->base, NULL, hostname, port);
        !           955:        }
        !           956:        evhttp_connection_set_ext_method_cmp(p->soap_conn, ext_methods_cb);
        !           957:        req = evhttp_request_new(upnpc_subscribe_response, p);
        !           958:        headers = evhttp_request_get_output_headers(req);
        !           959:        /*buffer = evhttp_request_get_output_buffer(req);*/
        !           960:        evhttp_add_header(headers, "Host", hostname_port);
        !           961:        /*evhttp_add_header(headers, "User-Agent", "***");*/
        !           962:        snprintf(callback_header, sizeof(callback_header), "<http://%s:%hu/evt_conn>", p->parent->local_address, p->parent->local_port);
        !           963:        evhttp_add_header(headers, "Callback", callback_header);
        !           964:        evhttp_add_header(headers, "NT", "upnp:event");
        !           965:        /*evhttp_add_header(headers, "NTS", "");*/
        !           966:        evhttp_add_header(headers, "Timeout", "3600");
        !           967:        /*evbuffer_add(buffer, body, body_len);*/
        !           968:        evhttp_make_request(p->soap_conn, req, EVHTTP_REQ_SUBSCRIBE, path);
        !           969:        p->state |= UPNPC_DEVICE_SOAP_REQ;
        !           970:        return 0;
        !           971: }
        !           972: #endif /* ENABLE_UPNP_EVENTS */
        !           973: 
        !           974: int upnpc_get_external_ip_address(upnpc_device_t * p)
        !           975: {
        !           976:        return upnpc_send_soap_request(p, p->control_conn_url,
        !           977:                                 p->conn_service_type/*"urn:schemas-upnp-org:service:WANIPConnection:1"*/,
        !           978:                                 "GetExternalIPAddress", NULL, 0);
        !           979: }
        !           980: 
        !           981: int upnpc_get_link_layer_max_rate(upnpc_device_t * p)
        !           982: {
        !           983:        return upnpc_send_soap_request(p, p->control_cif_url,
        !           984:                                 p->cif_service_type/*"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"*/,
        !           985:                                 "GetCommonLinkProperties", NULL, 0);
        !           986: }
        !           987: 
        !           988: int upnpc_delete_port_mapping(upnpc_device_t * p,
        !           989:                               const char * remote_host, unsigned short ext_port,
        !           990:                               const char * proto)
        !           991: {
        !           992:        struct upnp_args args[3];
        !           993:        char ext_port_str[8];
        !           994: 
        !           995:        if(proto == NULL || ext_port == 0)
        !           996:                return UPNPC_ERR_INVALID_ARGS;
        !           997:        snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
        !           998:        args[0].elt = "NewRemoteHost";
        !           999:        args[0].val = remote_host?remote_host:"";
        !          1000:        args[1].elt = "NewExternalPort";
        !          1001:        args[1].val = ext_port_str;
        !          1002:        args[2].elt = "NewProtocol";
        !          1003:        args[2].val = proto;
        !          1004:        return upnpc_send_soap_request(p, p->control_conn_url,
        !          1005:                                 p->conn_service_type,/*"urn:schemas-upnp-org:service:WANIPConnection:1",*/
        !          1006:                                 "DeletePortMapping",
        !          1007:                                 args, 3);
        !          1008: }
        !          1009: 
        !          1010: int upnpc_add_port_mapping(upnpc_device_t * p,
        !          1011:                            const char * remote_host, unsigned short ext_port,
        !          1012:                            unsigned short int_port, const char * int_client,
        !          1013:                            const char * proto, const char * description,
        !          1014:                            unsigned int lease_duration)
        !          1015: {
        !          1016:        struct upnp_args args[8];
        !          1017:        char lease_duration_str[16];
        !          1018:        char int_port_str[8];
        !          1019:        char ext_port_str[8];
        !          1020: 
        !          1021:        if(int_client == NULL || int_port == 0 || ext_port == 0 || proto == NULL)
        !          1022:                return UPNPC_ERR_INVALID_ARGS;
        !          1023:        snprintf(lease_duration_str, sizeof(lease_duration_str), "%u", lease_duration);
        !          1024:        snprintf(int_port_str, sizeof(int_port_str), "%hu", int_port);
        !          1025:        snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
        !          1026:        args[0].elt = "NewRemoteHost";
        !          1027:        args[0].val = remote_host?remote_host:"";
        !          1028:        args[1].elt = "NewExternalPort";
        !          1029:        args[1].val = ext_port_str;
        !          1030:        args[2].elt = "NewProtocol";
        !          1031:        args[2].val = proto;
        !          1032:        args[3].elt = "NewInternalPort";
        !          1033:        args[3].val = int_port_str;
        !          1034:        args[4].elt = "NewInternalClient";
        !          1035:        args[4].val = int_client;
        !          1036:        args[5].elt = "NewEnabled";
        !          1037:        args[5].val = "1";
        !          1038:        args[6].elt = "NewPortMappingDescription";
        !          1039:        args[6].val = description?description:"miniupnpc-libevent";
        !          1040:        args[7].elt = "NewLeaseDuration";
        !          1041:        args[7].val = lease_duration_str;
        !          1042:        return upnpc_send_soap_request(p, p->control_conn_url,
        !          1043:                                 p->conn_service_type/*"urn:schemas-upnp-org:service:WANIPConnection:1"*/,
        !          1044:                                 "AddPortMapping",
        !          1045:                                 args, 8);
        !          1046: }
        !          1047: 
        !          1048: int upnpc_get_status_info(upnpc_device_t * p)
        !          1049: {
        !          1050:        return upnpc_send_soap_request(p, p->control_conn_url,
        !          1051:                                 p->conn_service_type/*"urn:schemas-upnp-org:service:WANIPConnection:1"*/,
        !          1052:                                 "GetStatusInfo", NULL, 0);
        !          1053: }

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