Annotation of embedaddon/miniupnpd/miniupnpc-libevent/miniupnpc-libevent.c, revision 1.1.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>