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