Annotation of embedaddon/miniupnpd/upnphttp.c, revision 1.1.1.1
1.1 misho 1: /* $Id: upnphttp.c,v 1.57 2009/02/12 23:38:40 nanard Exp $ */
2: /* Project : miniupnp
3: * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * Author : Thomas Bernard
5: * Copyright (c) 2005-2008 Thomas Bernard
6: * This software is subject to the conditions detailed in the
7: * LICENCE file included in this distribution.
8: * */
9: #include <stdlib.h>
10: #include <unistd.h>
11: #include <stdio.h>
12: #include <string.h>
13: #include <sys/types.h>
14: #include <sys/socket.h>
15: #include <sys/param.h>
16: #include <syslog.h>
17: #include <ctype.h>
18: #include "config.h"
19: #include "upnphttp.h"
20: #include "upnpdescgen.h"
21: #include "miniupnpdpath.h"
22: #include "upnpsoap.h"
23: #include "upnpevents.h"
24:
25: struct upnphttp *
26: New_upnphttp(int s)
27: {
28: struct upnphttp * ret;
29: if(s<0)
30: return NULL;
31: ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
32: if(ret == NULL)
33: return NULL;
34: memset(ret, 0, sizeof(struct upnphttp));
35: ret->socket = s;
36: return ret;
37: }
38:
39: void
40: CloseSocket_upnphttp(struct upnphttp * h)
41: {
42: if(close(h->socket) < 0)
43: {
44: syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
45: }
46: h->socket = -1;
47: h->state = 100;
48: }
49:
50: void
51: Delete_upnphttp(struct upnphttp * h)
52: {
53: if(h)
54: {
55: if(h->socket >= 0)
56: CloseSocket_upnphttp(h);
57: if(h->req_buf)
58: free(h->req_buf);
59: if(h->res_buf)
60: free(h->res_buf);
61: free(h);
62: }
63: }
64:
65: /* parse HttpHeaders of the REQUEST */
66: static void
67: ParseHttpHeaders(struct upnphttp * h)
68: {
69: char * line;
70: char * colon;
71: char * p;
72: int n;
73: line = h->req_buf;
74: /* TODO : check if req_buf, contentoff are ok */
75: while(line < (h->req_buf + h->req_contentoff))
76: {
77: colon = strchr(line, ':');
78: if(colon)
79: {
80: if(strncasecmp(line, "Content-Length", 14)==0)
81: {
82: p = colon;
83: while(*p < '0' || *p > '9')
84: p++;
85: h->req_contentlen = atoi(p);
86: /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
87: printf(" readbufflen=%d contentoff = %d\n",
88: h->req_buflen, h->req_contentoff);*/
89: }
90: else if(strncasecmp(line, "SOAPAction", 10)==0)
91: {
92: p = colon;
93: n = 0;
94: while(*p == ':' || *p == ' ' || *p == '\t')
95: p++;
96: while(p[n]>=' ')
97: {
98: n++;
99: }
100: if((p[0] == '"' && p[n-1] == '"')
101: || (p[0] == '\'' && p[n-1] == '\''))
102: {
103: p++; n -= 2;
104: }
105: h->req_soapAction = p;
106: h->req_soapActionLen = n;
107: }
108: #ifdef ENABLE_EVENTS
109: else if(strncasecmp(line, "Callback", 8)==0)
110: {
111: p = colon;
112: while(*p != '<' && *p != '\r' )
113: p++;
114: n = 0;
115: while(p[n] != '>' && p[n] != '\r' )
116: n++;
117: h->req_Callback = p + 1;
118: h->req_CallbackLen = MAX(0, n - 1);
119: }
120: else if(strncasecmp(line, "SID", 3)==0)
121: {
122: p = colon + 1;
123: while(isspace(*p))
124: p++;
125: n = 0;
126: while(!isspace(p[n]))
127: n++;
128: h->req_SID = p;
129: h->req_SIDLen = n;
130: }
131: /* Timeout: Seconds-nnnn */
132: /* TIMEOUT
133: Recommended. Requested duration until subscription expires,
134: either number of seconds or infinite. Recommendation
135: by a UPnP Forum working committee. Defined by UPnP vendor.
136: Consists of the keyword "Second-" followed (without an
137: intervening space) by either an integer or the keyword "infinite". */
138: else if(strncasecmp(line, "Timeout", 7)==0)
139: {
140: p = colon + 1;
141: while(isspace(*p))
142: p++;
143: if(strncasecmp(p, "Second-", 7)==0) {
144: h->req_Timeout = atoi(p+7);
145: }
146: }
147: #endif
148: }
149: while(!(line[0] == '\r' && line[1] == '\n'))
150: line++;
151: line += 2;
152: }
153: }
154:
155: /* very minimalistic 404 error message */
156: static void
157: Send404(struct upnphttp * h)
158: {
159: /*
160: static const char error404[] = "HTTP/1.1 404 Not found\r\n"
161: "Connection: close\r\n"
162: "Content-type: text/html\r\n"
163: "\r\n"
164: "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
165: "<BODY><H1>Not Found</H1>The requested URL was not found"
166: " on this server.</BODY></HTML>\r\n";
167: int n;
168: n = send(h->socket, error404, sizeof(error404) - 1, 0);
169: if(n < 0)
170: {
171: syslog(LOG_ERR, "Send404: send(http): %m");
172: }*/
173: static const char body404[] =
174: "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
175: "<BODY><H1>Not Found</H1>The requested URL was not found"
176: " on this server.</BODY></HTML>\r\n";
177: h->respflags = FLAG_HTML;
178: BuildResp2_upnphttp(h, 404, "Not Found",
179: body404, sizeof(body404) - 1);
180: SendResp_upnphttp(h);
181: CloseSocket_upnphttp(h);
182: }
183:
184: /* very minimalistic 501 error message */
185: static void
186: Send501(struct upnphttp * h)
187: {
188: /*
189: static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n"
190: "Connection: close\r\n"
191: "Content-type: text/html\r\n"
192: "\r\n"
193: "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
194: "<BODY><H1>Not Implemented</H1>The HTTP Method "
195: "is not implemented by this server.</BODY></HTML>\r\n";
196: int n;
197: n = send(h->socket, error501, sizeof(error501) - 1, 0);
198: if(n < 0)
199: {
200: syslog(LOG_ERR, "Send501: send(http): %m");
201: }
202: */
203: static const char body501[] =
204: "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
205: "<BODY><H1>Not Implemented</H1>The HTTP Method "
206: "is not implemented by this server.</BODY></HTML>\r\n";
207: h->respflags = FLAG_HTML;
208: BuildResp2_upnphttp(h, 501, "Not Implemented",
209: body501, sizeof(body501) - 1);
210: SendResp_upnphttp(h);
211: CloseSocket_upnphttp(h);
212: }
213:
214: static const char *
215: findendheaders(const char * s, int len)
216: {
217: while(len-->0)
218: {
219: if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
220: return s;
221: s++;
222: }
223: return NULL;
224: }
225:
226: #ifdef HAS_DUMMY_SERVICE
227: static void
228: sendDummyDesc(struct upnphttp * h)
229: {
230: static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
231: "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
232: " <specVersion>"
233: " <major>1</major>"
234: " <minor>0</minor>"
235: " </specVersion>"
236: " <actionList />"
237: " <serviceStateTable />"
238: "</scpd>\r\n";
239: BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
240: SendResp_upnphttp(h);
241: CloseSocket_upnphttp(h);
242: }
243: #endif
244:
245: /* Sends the description generated by the parameter */
246: static void
247: sendXMLdesc(struct upnphttp * h, char * (f)(int *))
248: {
249: char * desc;
250: int len;
251: desc = f(&len);
252: if(!desc)
253: {
254: static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
255: "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
256: syslog(LOG_ERR, "Failed to generate XML description");
257: h->respflags = FLAG_HTML;
258: BuildResp2_upnphttp(h, 500, "Internal Server Error",
259: error500, sizeof(error500)-1);
260: }
261: else
262: {
263: BuildResp_upnphttp(h, desc, len);
264: }
265: SendResp_upnphttp(h);
266: CloseSocket_upnphttp(h);
267: free(desc);
268: }
269:
270: /* ProcessHTTPPOST_upnphttp()
271: * executes the SOAP query if it is possible */
272: static void
273: ProcessHTTPPOST_upnphttp(struct upnphttp * h)
274: {
275: if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
276: {
277: if(h->req_soapAction)
278: {
279: /* we can process the request */
280: syslog(LOG_INFO, "SOAPAction: %.*s",
281: h->req_soapActionLen, h->req_soapAction);
282: ExecuteSoapAction(h,
283: h->req_soapAction,
284: h->req_soapActionLen);
285: }
286: else
287: {
288: static const char err400str[] =
289: "<html><body>Bad request</body></html>";
290: syslog(LOG_INFO, "No SOAPAction in HTTP headers");
291: h->respflags = FLAG_HTML;
292: BuildResp2_upnphttp(h, 400, "Bad Request",
293: err400str, sizeof(err400str) - 1);
294: SendResp_upnphttp(h);
295: CloseSocket_upnphttp(h);
296: }
297: }
298: else
299: {
300: /* waiting for remaining data */
301: h->state = 1;
302: }
303: }
304:
305: #ifdef ENABLE_EVENTS
306: static void
307: ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
308: {
309: const char * sid;
310: syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
311: syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
312: h->req_CallbackLen, h->req_Callback, h->req_Timeout);
313: syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
314: if(!h->req_Callback && !h->req_SID) {
315: /* Missing or invalid CALLBACK : 412 Precondition Failed.
316: * If CALLBACK header is missing or does not contain a valid HTTP URL,
317: * the publisher must respond with HTTP error 412 Precondition Failed*/
318: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
319: SendResp_upnphttp(h);
320: CloseSocket_upnphttp(h);
321: } else {
322: /* - add to the subscriber list
323: * - respond HTTP/x.x 200 OK
324: * - Send the initial event message */
325: /* Server:, SID:; Timeout: Second-(xx|infinite) */
326: if(h->req_Callback) {
327: sid = upnpevents_addSubscriber(path, h->req_Callback,
328: h->req_CallbackLen, h->req_Timeout);
329: h->respflags = FLAG_TIMEOUT;
330: if(sid) {
331: syslog(LOG_DEBUG, "generated sid=%s", sid);
332: h->respflags |= FLAG_SID;
333: h->req_SID = sid;
334: h->req_SIDLen = strlen(sid);
335: }
336: BuildResp_upnphttp(h, 0, 0);
337: } else {
338: /* subscription renew */
339: /* Invalid SID
340: 412 Precondition Failed. If a SID does not correspond to a known,
341: un-expired subscription, the publisher must respond
342: with HTTP error 412 Precondition Failed. */
343: if(renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0) {
344: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
345: } else {
346: BuildResp_upnphttp(h, 0, 0);
347: }
348: }
349: SendResp_upnphttp(h);
350: CloseSocket_upnphttp(h);
351: }
352: }
353:
354: static void
355: ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
356: {
357: syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
358: syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
359: /* Remove from the list */
360: if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0) {
361: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
362: } else {
363: BuildResp_upnphttp(h, 0, 0);
364: }
365: SendResp_upnphttp(h);
366: CloseSocket_upnphttp(h);
367: }
368: #endif
369:
370: /* Parse and process Http Query
371: * called once all the HTTP headers have been received. */
372: static void
373: ProcessHttpQuery_upnphttp(struct upnphttp * h)
374: {
375: char HttpCommand[16];
376: char HttpUrl[128];
377: char * HttpVer;
378: char * p;
379: int i;
380: p = h->req_buf;
381: if(!p)
382: return;
383: for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
384: HttpCommand[i] = *(p++);
385: HttpCommand[i] = '\0';
386: while(*p==' ')
387: p++;
388: for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
389: HttpUrl[i] = *(p++);
390: HttpUrl[i] = '\0';
391: while(*p==' ')
392: p++;
393: HttpVer = h->HttpVer;
394: for(i = 0; i<15 && *p != '\r'; i++)
395: HttpVer[i] = *(p++);
396: HttpVer[i] = '\0';
397: syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
398: HttpCommand, HttpUrl, HttpVer);
399: ParseHttpHeaders(h);
400: if(strcmp("POST", HttpCommand) == 0)
401: {
402: h->req_command = EPost;
403: ProcessHTTPPOST_upnphttp(h);
404: }
405: else if(strcmp("GET", HttpCommand) == 0)
406: {
407: h->req_command = EGet;
408: if(strcasecmp(ROOTDESC_PATH, HttpUrl) == 0)
409: {
410: sendXMLdesc(h, genRootDesc);
411: }
412: else if(strcasecmp(WANIPC_PATH, HttpUrl) == 0)
413: {
414: sendXMLdesc(h, genWANIPCn);
415: }
416: else if(strcasecmp(WANCFG_PATH, HttpUrl) == 0)
417: {
418: sendXMLdesc(h, genWANCfg);
419: }
420: #ifdef HAS_DUMMY_SERVICE
421: else if(strcasecmp(DUMMY_PATH, HttpUrl) == 0)
422: {
423: sendDummyDesc(h);
424: }
425: #endif
426: #ifdef ENABLE_L3F_SERVICE
427: else if(strcasecmp(L3F_PATH, HttpUrl) == 0)
428: {
429: sendXMLdesc(h, genL3F);
430: }
431: #endif
432: else
433: {
434: syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
435: Send404(h);
436: }
437: }
438: #ifdef ENABLE_EVENTS
439: else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
440: {
441: h->req_command = ESubscribe;
442: ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
443: }
444: else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
445: {
446: h->req_command = EUnSubscribe;
447: ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
448: }
449: #else
450: else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
451: {
452: syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
453: Send501(h);
454: }
455: #endif
456: else
457: {
458: syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
459: Send501(h);
460: }
461: }
462:
463:
464: void
465: Process_upnphttp(struct upnphttp * h)
466: {
467: char buf[2048];
468: int n;
469: if(!h)
470: return;
471: switch(h->state)
472: {
473: case 0:
474: n = recv(h->socket, buf, 2048, 0);
475: if(n<0)
476: {
477: syslog(LOG_ERR, "recv (state0): %m");
478: h->state = 100;
479: }
480: else if(n==0)
481: {
482: syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
483: h->state = 100;
484: }
485: else
486: {
487: const char * endheaders;
488: /* if 1st arg of realloc() is null,
489: * realloc behaves the same as malloc() */
490: h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
491: memcpy(h->req_buf + h->req_buflen, buf, n);
492: h->req_buflen += n;
493: h->req_buf[h->req_buflen] = '\0';
494: /* search for the string "\r\n\r\n" */
495: endheaders = findendheaders(h->req_buf, h->req_buflen);
496: if(endheaders)
497: {
498: h->req_contentoff = endheaders - h->req_buf + 4;
499: ProcessHttpQuery_upnphttp(h);
500: }
501: }
502: break;
503: case 1:
504: n = recv(h->socket, buf, 2048, 0);
505: if(n<0)
506: {
507: syslog(LOG_ERR, "recv (state1): %m");
508: h->state = 100;
509: }
510: else if(n==0)
511: {
512: syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
513: h->state = 100;
514: }
515: else
516: {
517: /*fwrite(buf, 1, n, stdout);*/ /* debug */
518: h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
519: memcpy(h->req_buf + h->req_buflen, buf, n);
520: h->req_buflen += n;
521: if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
522: {
523: ProcessHTTPPOST_upnphttp(h);
524: }
525: }
526: break;
527: default:
528: syslog(LOG_WARNING, "Unexpected state: %d", h->state);
529: }
530: }
531:
532: static const char httpresphead[] =
533: "%s %d %s\r\n"
534: /*"Content-Type: text/xml; charset=\"utf-8\"\r\n"*/
535: "Content-Type: %s\r\n"
536: "Connection: close\r\n"
537: "Content-Length: %d\r\n"
538: /*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/
539: "Server: " MINIUPNPD_SERVER_STRING "\r\n"
540: ; /*"\r\n";*/
541: /*
542: "<?xml version=\"1.0\"?>\n"
543: "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
544: "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
545: "<s:Body>"
546:
547: "</s:Body>"
548: "</s:Envelope>";
549: */
550: /* with response code and response message
551: * also allocate enough memory */
552:
553: void
554: BuildHeader_upnphttp(struct upnphttp * h, int respcode,
555: const char * respmsg,
556: int bodylen)
557: {
558: int templen;
559: if(!h->res_buf)
560: {
561: templen = sizeof(httpresphead) + 128 + bodylen;
562: h->res_buf = (char *)malloc(templen);
563: h->res_buf_alloclen = templen;
564: }
565: h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
566: httpresphead, h->HttpVer,
567: respcode, respmsg,
568: (h->respflags&FLAG_HTML)?"text/html":"text/xml",
569: bodylen);
570: /* Additional headers */
571: #ifdef ENABLE_EVENTS
572: if(h->respflags & FLAG_TIMEOUT) {
573: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
574: h->res_buf_alloclen - h->res_buflen,
575: "Timeout: Second-");
576: if(h->req_Timeout) {
577: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
578: h->res_buf_alloclen - h->res_buflen,
579: "%d\r\n", h->req_Timeout);
580: } else {
581: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
582: h->res_buf_alloclen - h->res_buflen,
583: "infinite\r\n");
584: }
585: }
586: if(h->respflags & FLAG_SID) {
587: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
588: h->res_buf_alloclen - h->res_buflen,
589: "SID: %s\r\n", h->req_SID);
590: }
591: #endif
592: h->res_buf[h->res_buflen++] = '\r';
593: h->res_buf[h->res_buflen++] = '\n';
594: if(h->res_buf_alloclen < (h->res_buflen + bodylen))
595: {
596: h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
597: h->res_buf_alloclen = h->res_buflen + bodylen;
598: }
599: }
600:
601: void
602: BuildResp2_upnphttp(struct upnphttp * h, int respcode,
603: const char * respmsg,
604: const char * body, int bodylen)
605: {
606: BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
607: if(body)
608: memcpy(h->res_buf + h->res_buflen, body, bodylen);
609: h->res_buflen += bodylen;
610: }
611:
612: /* responding 200 OK ! */
613: void
614: BuildResp_upnphttp(struct upnphttp * h,
615: const char * body, int bodylen)
616: {
617: BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
618: }
619:
620: void
621: SendResp_upnphttp(struct upnphttp * h)
622: {
623: int n;
624: n = send(h->socket, h->res_buf, h->res_buflen, 0);
625: if(n<0)
626: {
627: syslog(LOG_ERR, "send(res_buf): %m");
628: }
629: else if(n < h->res_buflen)
630: {
631: /* TODO : handle correctly this case */
632: syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
633: n, h->res_buflen);
634: }
635: }
636:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>