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>