1: /* $Id: upnphttp.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */
2: /* Project : miniupnp
3: * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * Author : Thomas Bernard
5: * Copyright (c) 2005-2012 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 <netinet/in.h>
17: #include <arpa/inet.h>
18: #include <syslog.h>
19: #include <ctype.h>
20: #include <errno.h>
21: #include "config.h"
22: #ifdef ENABLE_HTTP_DATE
23: #include <time.h>
24: #endif
25: #include "upnphttp.h"
26: #include "upnpdescgen.h"
27: #include "miniupnpdpath.h"
28: #include "upnpsoap.h"
29: #include "upnpevents.h"
30: #include "upnputils.h"
31:
32: struct upnphttp *
33: New_upnphttp(int s)
34: {
35: struct upnphttp * ret;
36: if(s<0)
37: return NULL;
38: ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
39: if(ret == NULL)
40: return NULL;
41: memset(ret, 0, sizeof(struct upnphttp));
42: ret->socket = s;
43: if(!set_non_blocking(s))
44: syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
45: return ret;
46: }
47:
48: void
49: CloseSocket_upnphttp(struct upnphttp * h)
50: {
51: if(close(h->socket) < 0)
52: {
53: syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
54: }
55: h->socket = -1;
56: h->state = EToDelete;
57: }
58:
59: void
60: Delete_upnphttp(struct upnphttp * h)
61: {
62: if(h)
63: {
64: if(h->socket >= 0)
65: CloseSocket_upnphttp(h);
66: if(h->req_buf)
67: free(h->req_buf);
68: if(h->res_buf)
69: free(h->res_buf);
70: free(h);
71: }
72: }
73:
74: /* parse HttpHeaders of the REQUEST
75: * This function is called after the \r\n\r\n character
76: * sequence has been found in h->req_buf */
77: static void
78: ParseHttpHeaders(struct upnphttp * h)
79: {
80: char * line;
81: char * colon;
82: char * p;
83: int n;
84: if((h->req_buf == NULL) || (h->req_contentoff <= 0))
85: return;
86: line = h->req_buf;
87: while(line < (h->req_buf + h->req_contentoff))
88: {
89: colon = line;
90: while(*colon != ':')
91: {
92: if(*colon == '\r' || *colon == '\n')
93: {
94: colon = NULL; /* no ':' character found on the line */
95: break;
96: }
97: colon++;
98: }
99: if(colon)
100: {
101: if(strncasecmp(line, "Content-Length", 14)==0)
102: {
103: p = colon;
104: while(*p < '0' || *p > '9')
105: p++;
106: h->req_contentlen = atoi(p);
107: if(h->req_contentlen < 0) {
108: syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
109: h->req_contentlen = 0;
110: }
111: /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
112: printf(" readbufflen=%d contentoff = %d\n",
113: h->req_buflen, h->req_contentoff);*/
114: }
115: else if(strncasecmp(line, "SOAPAction", 10)==0)
116: {
117: p = colon;
118: n = 0;
119: while(*p == ':' || *p == ' ' || *p == '\t')
120: p++;
121: while(p[n]>=' ')
122: n++;
123: if((p[0] == '"' && p[n-1] == '"')
124: || (p[0] == '\'' && p[n-1] == '\''))
125: {
126: p++; n -= 2;
127: }
128: h->req_soapActionOff = p - h->req_buf;
129: h->req_soapActionLen = n;
130: }
131: else if(strncasecmp(line, "accept-language", 15) == 0)
132: {
133: p = colon;
134: n = 0;
135: while(*p == ':' || *p == ' ' || *p == '\t')
136: p++;
137: while(p[n]>=' ')
138: n++;
139: syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p);
140: /* keep only the 1st accepted language */
141: n = 0;
142: while(p[n]>' ' && p[n] != ',')
143: n++;
144: if(n >= (int)sizeof(h->accept_language))
145: n = (int)sizeof(h->accept_language) - 1;
146: memcpy(h->accept_language, p, n);
147: h->accept_language[n] = '\0';
148: }
149: else if(strncasecmp(line, "expect", 6) == 0)
150: {
151: p = colon;
152: n = 0;
153: while(*p == ':' || *p == ' ' || *p == '\t')
154: p++;
155: while(p[n]>=' ')
156: n++;
157: if(strncasecmp(p, "100-continue", 12) == 0) {
158: h->respflags |= FLAG_CONTINUE;
159: syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
160: }
161: }
162: #ifdef ENABLE_EVENTS
163: else if(strncasecmp(line, "Callback", 8)==0)
164: {
165: p = colon;
166: while(*p != '<' && *p != '\r' )
167: p++;
168: n = 0;
169: while(p[n] != '>' && p[n] != '\r' )
170: n++;
171: h->req_CallbackOff = p + 1 - h->req_buf;
172: h->req_CallbackLen = MAX(0, n - 1);
173: }
174: else if(strncasecmp(line, "SID", 3)==0)
175: {
176: p = colon + 1;
177: while(isspace(*p))
178: p++;
179: n = 0;
180: while(!isspace(p[n]))
181: n++;
182: h->req_SIDOff = p - h->req_buf;
183: h->req_SIDLen = n;
184: }
185: /* Timeout: Seconds-nnnn */
186: /* TIMEOUT
187: Recommended. Requested duration until subscription expires,
188: either number of seconds or infinite. Recommendation
189: by a UPnP Forum working committee. Defined by UPnP vendor.
190: Consists of the keyword "Second-" followed (without an
191: intervening space) by either an integer or the keyword "infinite". */
192: else if(strncasecmp(line, "Timeout", 7)==0)
193: {
194: p = colon + 1;
195: while(isspace(*p))
196: p++;
197: if(strncasecmp(p, "Second-", 7)==0) {
198: h->req_Timeout = atoi(p+7);
199: }
200: }
201: #ifdef UPNP_STRICT
202: else if(strncasecmp(line, "nt", 2)==0)
203: {
204: p = colon + 1;
205: while(isspace(*p))
206: p++;
207: n = 0;
208: while(!isspace(p[n]))
209: n++;
210: h->req_NTOff = p - h->req_buf;
211: h->req_NTLen = n;
212: }
213: #endif
214: #endif
215: }
216: /* the loop below won't run off the end of the buffer
217: * because the buffer is guaranteed to contain the \r\n\r\n
218: * character sequence */
219: while(!(line[0] == '\r' && line[1] == '\n'))
220: line++;
221: line += 2;
222: }
223: }
224:
225: /* very minimalistic 404 error message */
226: static void
227: Send404(struct upnphttp * h)
228: {
229: static const char body404[] =
230: "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
231: "<BODY><H1>Not Found</H1>The requested URL was not found"
232: " on this server.</BODY></HTML>\r\n";
233:
234: h->respflags = FLAG_HTML;
235: BuildResp2_upnphttp(h, 404, "Not Found",
236: body404, sizeof(body404) - 1);
237: SendRespAndClose_upnphttp(h);
238: }
239:
240: static void
241: Send405(struct upnphttp * h)
242: {
243: static const char body405[] =
244: "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
245: "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
246: "is not allowed on this resource.</BODY></HTML>\r\n";
247:
248: h->respflags |= FLAG_HTML;
249: BuildResp2_upnphttp(h, 405, "Method Not Allowed",
250: body405, sizeof(body405) - 1);
251: SendRespAndClose_upnphttp(h);
252: }
253:
254: /* very minimalistic 501 error message */
255: static void
256: Send501(struct upnphttp * h)
257: {
258: static const char body501[] =
259: "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
260: "<BODY><H1>Not Implemented</H1>The HTTP Method "
261: "is not implemented by this server.</BODY></HTML>\r\n";
262:
263: h->respflags = FLAG_HTML;
264: BuildResp2_upnphttp(h, 501, "Not Implemented",
265: body501, sizeof(body501) - 1);
266: SendRespAndClose_upnphttp(h);
267: }
268:
269: /* findendheaders() find the \r\n\r\n character sequence and
270: * return a pointer to it.
271: * It returns NULL if not found */
272: static const char *
273: findendheaders(const char * s, int len)
274: {
275: while(len-->3)
276: {
277: if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
278: return s;
279: s++;
280: }
281: return NULL;
282: }
283:
284: #ifdef HAS_DUMMY_SERVICE
285: static void
286: sendDummyDesc(struct upnphttp * h)
287: {
288: static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
289: "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
290: " <specVersion>"
291: " <major>1</major>"
292: " <minor>0</minor>"
293: " </specVersion>"
294: " <actionList />"
295: " <serviceStateTable />"
296: "</scpd>\r\n";
297: BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
298: SendRespAndClose_upnphttp(h);
299: }
300: #endif
301:
302: /* Sends the description generated by the parameter */
303: static void
304: sendXMLdesc(struct upnphttp * h, char * (f)(int *))
305: {
306: char * desc;
307: int len;
308: desc = f(&len);
309: if(!desc)
310: {
311: static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
312: "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
313: syslog(LOG_ERR, "Failed to generate XML description");
314: h->respflags = FLAG_HTML;
315: BuildResp2_upnphttp(h, 500, "Internal Server Error",
316: error500, sizeof(error500)-1);
317: }
318: else
319: {
320: BuildResp_upnphttp(h, desc, len);
321: }
322: SendRespAndClose_upnphttp(h);
323: free(desc);
324: }
325:
326: /* ProcessHTTPPOST_upnphttp()
327: * executes the SOAP query if it is possible */
328: static void
329: ProcessHTTPPOST_upnphttp(struct upnphttp * h)
330: {
331: if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
332: {
333: /* the request body is received */
334: if(h->req_soapActionOff > 0)
335: {
336: /* we can process the request */
337: syslog(LOG_INFO, "SOAPAction: %.*s",
338: h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
339: ExecuteSoapAction(h,
340: h->req_buf + h->req_soapActionOff,
341: h->req_soapActionLen);
342: }
343: else
344: {
345: static const char err400str[] =
346: "<html><body>Bad request</body></html>";
347: syslog(LOG_INFO, "No SOAPAction in HTTP headers");
348: h->respflags = FLAG_HTML;
349: BuildResp2_upnphttp(h, 400, "Bad Request",
350: err400str, sizeof(err400str) - 1);
351: SendRespAndClose_upnphttp(h);
352: }
353: }
354: else if(h->respflags & FLAG_CONTINUE)
355: {
356: /* Sending the 100 Continue response */
357: if(!h->res_buf) {
358: h->res_buf = malloc(256);
359: h->res_buf_alloclen = 256;
360: }
361: h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
362: "%s 100 Continue\r\n\r\n", h->HttpVer);
363: h->res_sent = 0;
364: h->state = ESendingContinue;
365: if(SendResp_upnphttp(h))
366: h->state = EWaitingForHttpContent;
367: }
368: else
369: {
370: /* waiting for remaining data */
371: h->state = EWaitingForHttpContent;
372: }
373: }
374:
375: #ifdef ENABLE_EVENTS
376: /**
377: * returns 0 if the callback header value is not valid
378: * 1 if it is valid.
379: */
380: static int
381: checkCallbackURL(struct upnphttp * h)
382: {
383: char addrstr[48];
384: int ipv6;
385: const char * p;
386: unsigned int i;
387:
388: if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8)
389: return 0;
390: if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0)
391: return 0;
392: ipv6 = 0;
393: i = 0;
394: p = h->req_buf + h->req_CallbackOff + 7;
395: if(*p == '[') {
396: p++;
397: ipv6 = 1;
398: while(*p != ']' && i < (sizeof(addrstr)-1)
399: && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
400: addrstr[i++] = *(p++);
401: } else {
402: while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
403: && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
404: addrstr[i++] = *(p++);
405: }
406: addrstr[i] = '\0';
407: if(ipv6) {
408: struct in6_addr addr;
409: if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
410: return 0;
411: #ifdef ENABLE_IPV6
412: if(!h->ipv6
413: || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
414: return 0;
415: #else
416: return 0;
417: #endif
418: } else {
419: struct in_addr addr;
420: if(inet_pton(AF_INET, addrstr, &addr) <= 0)
421: return 0;
422: #ifdef ENABLE_IPV6
423: if(h->ipv6) {
424: if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
425: return 0;
426: if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
427: return 0;
428: } else {
429: if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
430: return 0;
431: }
432: #else
433: if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
434: return 0;
435: #endif
436: }
437: return 1;
438: }
439:
440: static void
441: ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
442: {
443: const char * sid;
444: syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
445: syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
446: h->req_CallbackLen, h->req_buf + h->req_CallbackOff,
447: h->req_Timeout);
448: syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
449: if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
450: /* Missing or invalid CALLBACK : 412 Precondition Failed.
451: * If CALLBACK header is missing or does not contain a valid HTTP URL,
452: * the publisher must respond with HTTP error 412 Precondition Failed*/
453: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
454: SendRespAndClose_upnphttp(h);
455: } else {
456: /* - add to the subscriber list
457: * - respond HTTP/x.x 200 OK
458: * - Send the initial event message */
459: /* Server:, SID:; Timeout: Second-(xx|infinite) */
460: /* Check that the callback URL is on the same IP as
461: * the request, and not on the internet, nor on ourself (DOS attack ?) */
462: if(h->req_CallbackOff > 0) {
463: #ifdef UPNP_STRICT
464: /* SID: and Callback: are incompatible */
465: if(h->req_SIDOff > 0) {
466: syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
467: BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
468: /* "NT: upnp:event" header is mandatory */
469: } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
470: 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
471: syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
472: h->req_NTLen, h->req_buf + h->req_NTOff);
473: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
474: } else
475: #endif
476: if(checkCallbackURL(h)) {
477: sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
478: h->req_CallbackLen, h->req_Timeout);
479: h->respflags = FLAG_TIMEOUT;
480: if(sid) {
481: syslog(LOG_DEBUG, "generated sid=%s", sid);
482: h->respflags |= FLAG_SID;
483: h->res_SID = sid;
484: }
485: BuildResp_upnphttp(h, 0, 0);
486: } else {
487: syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
488: h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
489: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
490: }
491: } else {
492: /* subscription renew */
493: /* Invalid SID
494: 412 Precondition Failed. If a SID does not correspond to a known,
495: un-expired subscription, the publisher must respond
496: with HTTP error 412 Precondition Failed. */
497: #ifdef UPNP_STRICT
498: /* SID: and NT: headers are incompatibles */
499: if(h->req_NTOff > 0) {
500: syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
501: BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
502: } else
503: #endif
504: if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen,
505: h->req_Timeout) < 0) {
506: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
507: } else {
508: h->respflags = FLAG_TIMEOUT;
509: BuildResp_upnphttp(h, 0, 0);
510: }
511: }
512: SendRespAndClose_upnphttp(h);
513: }
514: }
515:
516: static void
517: ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
518: {
519: syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
520: syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
521: /* Remove from the list */
522: #ifdef UPNP_STRICT
523: if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
524: /* SID: header missing or empty */
525: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
526: } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
527: /* no NT: or Callback: header must be present */
528: BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
529: } else
530: #endif
531: if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
532: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
533: } else {
534: BuildResp_upnphttp(h, 0, 0);
535: }
536: SendRespAndClose_upnphttp(h);
537: }
538: #endif
539:
540: /* Parse and process Http Query
541: * called once all the HTTP headers have been received,
542: * so it is guaranteed that h->req_buf contains the \r\n\r\n
543: * character sequence */
544: static void
545: ProcessHttpQuery_upnphttp(struct upnphttp * h)
546: {
547: static const struct {
548: const char * path;
549: char * (* f)(int *);
550: } path_desc[] = {
551: { ROOTDESC_PATH, genRootDesc},
552: { WANIPC_PATH, genWANIPCn},
553: { WANCFG_PATH, genWANCfg},
554: #ifdef HAS_DUMMY_SERVICE
555: { DUMMY_PATH, NULL},
556: #endif
557: #ifdef ENABLE_L3F_SERVICE
558: { L3F_PATH, genL3F},
559: #endif
560: #ifdef ENABLE_6FC_SERVICE
561: { WANIP6FC_PATH, gen6FC},
562: #endif
563: #ifdef ENABLE_DP_SERVICE
564: { DP_PATH, genDP},
565: #endif
566: { NULL, NULL}
567: };
568: char HttpCommand[16];
569: char HttpUrl[128];
570: char * HttpVer;
571: char * p;
572: int i;
573: p = h->req_buf;
574: if(!p)
575: return;
576: /* note : checking (*p != '\r') is enough to avoid runing off the
577: * end of the buffer, because h->req_buf is guaranteed to contain
578: * the \r\n\r\n character sequence */
579: for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
580: HttpCommand[i] = *(p++);
581: HttpCommand[i] = '\0';
582: while(*p==' ')
583: p++;
584: for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
585: HttpUrl[i] = *(p++);
586: HttpUrl[i] = '\0';
587: while(*p==' ')
588: p++;
589: HttpVer = h->HttpVer;
590: for(i = 0; i<15 && *p != '\r'; i++)
591: HttpVer[i] = *(p++);
592: HttpVer[i] = '\0';
593: syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
594: HttpCommand, HttpUrl, HttpVer);
595: ParseHttpHeaders(h);
596: if(strcmp("POST", HttpCommand) == 0)
597: {
598: h->req_command = EPost;
599: ProcessHTTPPOST_upnphttp(h);
600: }
601: else if(strcmp("GET", HttpCommand) == 0)
602: {
603: h->req_command = EGet;
604: for(i=0; path_desc[i].path; i++) {
605: if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
606: if(path_desc[i].f)
607: sendXMLdesc(h, path_desc[i].f);
608: else
609: #ifdef HAS_DUMMY_SERVICE
610: sendDummyDesc(h);
611: #else
612: continue;
613: #endif
614: return;
615: }
616: }
617: if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
618: /* 405 Method Not Allowed
619: * Allow: POST */
620: h->respflags = FLAG_ALLOW_POST;
621: Send405(h);
622: return;
623: }
624: #ifdef ENABLE_EVENTS
625: if(0 == memcmp(HttpUrl, "/evt/", 5)) {
626: /* 405 Method Not Allowed
627: * Allow: SUBSCRIBE, UNSUBSCRIBE */
628: h->respflags = FLAG_ALLOW_SUB_UNSUB;
629: Send405(h);
630: return;
631: }
632: #endif
633: syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
634: Send404(h);
635: }
636: #ifdef ENABLE_EVENTS
637: else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
638: {
639: h->req_command = ESubscribe;
640: ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
641: }
642: else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
643: {
644: h->req_command = EUnSubscribe;
645: ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
646: }
647: #else
648: else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
649: {
650: syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
651: Send501(h);
652: }
653: #endif
654: else
655: {
656: syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
657: Send501(h);
658: }
659: }
660:
661:
662: void
663: Process_upnphttp(struct upnphttp * h)
664: {
665: char * h_tmp;
666: char buf[2048];
667: int n;
668:
669: if(!h)
670: return;
671: switch(h->state)
672: {
673: case EWaitingForHttpRequest:
674: n = recv(h->socket, buf, sizeof(buf), 0);
675: if(n<0)
676: {
677: if(errno != EAGAIN &&
678: errno != EWOULDBLOCK &&
679: errno != EINTR)
680: {
681: syslog(LOG_ERR, "recv (state0): %m");
682: h->state = EToDelete;
683: }
684: /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
685: }
686: else if(n==0)
687: {
688: syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
689: h->state = EToDelete;
690: }
691: else
692: {
693: const char * endheaders;
694: /* if 1st arg of realloc() is null,
695: * realloc behaves the same as malloc() */
696: h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
697: if (h_tmp == NULL)
698: {
699: syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
700: h->state = EToDelete;
701: }
702: else
703: {
704: h->req_buf = h_tmp;
705: memcpy(h->req_buf + h->req_buflen, buf, n);
706: h->req_buflen += n;
707: h->req_buf[h->req_buflen] = '\0';
708: }
709: /* search for the string "\r\n\r\n" */
710: endheaders = findendheaders(h->req_buf, h->req_buflen);
711: if(endheaders)
712: {
713: /* at this point, the request buffer (h->req_buf)
714: * is guaranteed to contain the \r\n\r\n character sequence */
715: h->req_contentoff = endheaders - h->req_buf + 4;
716: ProcessHttpQuery_upnphttp(h);
717: }
718: }
719: break;
720: case EWaitingForHttpContent:
721: n = recv(h->socket, buf, sizeof(buf), 0);
722: if(n<0)
723: {
724: if(errno != EAGAIN &&
725: errno != EWOULDBLOCK &&
726: errno != EINTR)
727: {
728: syslog(LOG_ERR, "recv (state1): %m");
729: h->state = EToDelete;
730: }
731: /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
732: }
733: else if(n==0)
734: {
735: syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
736: h->state = EToDelete;
737: }
738: else
739: {
740: void * tmp = realloc(h->req_buf, n + h->req_buflen);
741: if(!tmp)
742: {
743: syslog(LOG_ERR, "memory allocation error %m");
744: h->state = EToDelete;
745: }
746: else
747: {
748: h->req_buf = tmp;
749: memcpy(h->req_buf + h->req_buflen, buf, n);
750: h->req_buflen += n;
751: if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
752: {
753: ProcessHTTPPOST_upnphttp(h);
754: }
755: }
756: }
757: break;
758: case ESendingContinue:
759: if(SendResp_upnphttp(h))
760: h->state = EWaitingForHttpContent;
761: break;
762: case ESendingAndClosing:
763: SendRespAndClose_upnphttp(h);
764: break;
765: default:
766: syslog(LOG_WARNING, "Unexpected state: %d", h->state);
767: }
768: }
769:
770: static const char httpresphead[] =
771: "%s %d %s\r\n"
772: "Content-Type: %s\r\n"
773: "Connection: close\r\n"
774: "Content-Length: %d\r\n"
775: "Server: " MINIUPNPD_SERVER_STRING "\r\n"
776: ; /*"\r\n";*/
777: /*
778: "<?xml version=\"1.0\"?>\n"
779: "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
780: "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
781: "<s:Body>"
782:
783: "</s:Body>"
784: "</s:Envelope>";
785: */
786: /* with response code and response message
787: * also allocate enough memory */
788:
789: void
790: BuildHeader_upnphttp(struct upnphttp * h, int respcode,
791: const char * respmsg,
792: int bodylen)
793: {
794: int templen;
795: if(!h->res_buf ||
796: h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
797: if(h->res_buf)
798: free(h->res_buf);
799: templen = sizeof(httpresphead) + 256 + bodylen;
800: h->res_buf = (char *)malloc(templen);
801: if(!h->res_buf) {
802: syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
803: return;
804: }
805: h->res_buf_alloclen = templen;
806: }
807: h->res_sent = 0;
808: h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
809: httpresphead, h->HttpVer,
810: respcode, respmsg,
811: (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
812: bodylen);
813: /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
814: /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
815: /* Additional headers */
816: #ifdef ENABLE_HTTP_DATE
817: {
818: char http_date[64];
819: time_t t;
820: struct tm tm;
821: time(&t);
822: gmtime_r(&t, &tm);
823: /* %a and %b depend on locale */
824: strftime(http_date, sizeof(http_date),
825: "%a, %d %b %Y %H:%M:%S GMT", &tm);
826: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
827: h->res_buf_alloclen - h->res_buflen,
828: "Date: %s\r\n", http_date);
829: }
830: #endif
831: #ifdef ENABLE_EVENTS
832: if(h->respflags & FLAG_TIMEOUT) {
833: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
834: h->res_buf_alloclen - h->res_buflen,
835: "Timeout: Second-");
836: if(h->req_Timeout) {
837: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
838: h->res_buf_alloclen - h->res_buflen,
839: "%d\r\n", h->req_Timeout);
840: } else {
841: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
842: h->res_buf_alloclen - h->res_buflen,
843: "infinite\r\n");
844: }
845: }
846: if(h->respflags & FLAG_SID) {
847: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
848: h->res_buf_alloclen - h->res_buflen,
849: "SID: %s\r\n", h->res_SID);
850: }
851: #endif
852: if(h->respflags & FLAG_ALLOW_POST) {
853: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
854: h->res_buf_alloclen - h->res_buflen,
855: "Allow: %s\r\n", "POST");
856: } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
857: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
858: h->res_buf_alloclen - h->res_buflen,
859: "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
860: }
861: if(h->accept_language[0] != '\0') {
862: /* defaulting to "en" */
863: h->res_buflen += snprintf(h->res_buf + h->res_buflen,
864: h->res_buf_alloclen - h->res_buflen,
865: "Content-Language: %s\r\n",
866: h->accept_language[0] == '*' ? "en" : h->accept_language);
867: }
868: h->res_buf[h->res_buflen++] = '\r';
869: h->res_buf[h->res_buflen++] = '\n';
870: if(h->res_buf_alloclen < (h->res_buflen + bodylen))
871: {
872: char * tmp;
873: tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
874: if(tmp)
875: {
876: h->res_buf = tmp;
877: h->res_buf_alloclen = h->res_buflen + bodylen;
878: }
879: else
880: {
881: syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
882: }
883: }
884: }
885:
886: void
887: BuildResp2_upnphttp(struct upnphttp * h, int respcode,
888: const char * respmsg,
889: const char * body, int bodylen)
890: {
891: BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
892: if(body)
893: memcpy(h->res_buf + h->res_buflen, body, bodylen);
894: h->res_buflen += bodylen;
895: }
896:
897: /* responding 200 OK ! */
898: void
899: BuildResp_upnphttp(struct upnphttp * h,
900: const char * body, int bodylen)
901: {
902: BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
903: }
904:
905: int
906: SendResp_upnphttp(struct upnphttp * h)
907: {
908: ssize_t n;
909:
910: while (h->res_sent < h->res_buflen)
911: {
912: n = send(h->socket, h->res_buf + h->res_sent,
913: h->res_buflen - h->res_sent, 0);
914: if(n<0)
915: {
916: if(errno == EINTR)
917: continue; /* try again immediatly */
918: if(errno == EAGAIN || errno == EWOULDBLOCK)
919: {
920: /* try again later */
921: return 0;
922: }
923: syslog(LOG_ERR, "send(res_buf): %m");
924: break; /* avoid infinite loop */
925: }
926: else if(n == 0)
927: {
928: syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
929: h->res_sent, h->res_buflen);
930: break;
931: }
932: else
933: {
934: h->res_sent += n;
935: }
936: }
937: return 1; /* finished */
938: }
939:
940: void
941: SendRespAndClose_upnphttp(struct upnphttp * h)
942: {
943: ssize_t n;
944:
945: while (h->res_sent < h->res_buflen)
946: {
947: n = send(h->socket, h->res_buf + h->res_sent,
948: h->res_buflen - h->res_sent, 0);
949: if(n<0)
950: {
951: if(errno == EINTR)
952: continue; /* try again immediatly */
953: if(errno == EAGAIN || errno == EWOULDBLOCK)
954: {
955: /* try again later */
956: h->state = ESendingAndClosing;
957: return;
958: }
959: syslog(LOG_ERR, "send(res_buf): %m");
960: break; /* avoid infinite loop */
961: }
962: else if(n == 0)
963: {
964: syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
965: h->res_sent, h->res_buflen);
966: break;
967: }
968: else
969: {
970: h->res_sent += n;
971: }
972: }
973: CloseSocket_upnphttp(h);
974: }
975:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>