Annotation of embedaddon/miniupnpd/upnphttp.c, revision 1.1.1.3
1.1.1.3 ! misho 1: /* $Id: upnphttp.c,v 1.85 2013/01/29 21:52:43 nanard Exp $ */
1.1 misho 2: /* Project : miniupnp
3: * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * Author : Thomas Bernard
1.1.1.3 ! misho 5: * Copyright (c) 2005-2012 Thomas Bernard
1.1 misho 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>
1.1.1.3 ! misho 16: #include <netinet/in.h>
1.1.1.2 misho 17: #include <arpa/inet.h>
1.1 misho 18: #include <syslog.h>
19: #include <ctype.h>
1.1.1.3 ! misho 20: #include <errno.h>
1.1 misho 21: #include "config.h"
1.1.1.3 ! misho 22: #ifdef ENABLE_HTTP_DATE
! 23: #include <time.h>
! 24: #endif
1.1 misho 25: #include "upnphttp.h"
26: #include "upnpdescgen.h"
27: #include "miniupnpdpath.h"
28: #include "upnpsoap.h"
29: #include "upnpevents.h"
1.1.1.3 ! misho 30: #include "upnputils.h"
1.1 misho 31:
1.1.1.3 ! misho 32: struct upnphttp *
1.1 misho 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;
1.1.1.3 ! misho 43: if(!set_non_blocking(s))
! 44: syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
1.1 misho 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;
1.1.1.3 ! misho 56: h->state = EToDelete;
1.1 misho 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:
1.1.1.3 ! misho 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 */
1.1 misho 77: static void
78: ParseHttpHeaders(struct upnphttp * h)
79: {
80: char * line;
81: char * colon;
82: char * p;
83: int n;
1.1.1.3 ! misho 84: if((h->req_buf == NULL) || (h->req_contentoff <= 0))
! 85: return;
1.1 misho 86: line = h->req_buf;
87: while(line < (h->req_buf + h->req_contentoff))
88: {
1.1.1.3 ! misho 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: }
1.1 misho 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);
1.1.1.3 ! misho 107: if(h->req_contentlen < 0) {
! 108: syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
! 109: h->req_contentlen = 0;
! 110: }
1.1 misho 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: }
1.1.1.3 ! misho 128: h->req_soapActionOff = p - h->req_buf;
1.1 misho 129: h->req_soapActionLen = n;
130: }
1.1.1.3 ! misho 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: }
1.1 misho 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++;
1.1.1.3 ! misho 171: h->req_CallbackOff = p + 1 - h->req_buf;
1.1 misho 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++;
1.1.1.3 ! misho 182: h->req_SIDOff = p - h->req_buf;
1.1 misho 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: }
1.1.1.3 ! misho 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
1.1 misho 214: #endif
215: }
1.1.1.3 ! misho 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 */
1.1 misho 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";
1.1.1.3 ! misho 233:
1.1 misho 234: h->respflags = FLAG_HTML;
235: BuildResp2_upnphttp(h, 404, "Not Found",
236: body404, sizeof(body404) - 1);
1.1.1.3 ! misho 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);
1.1 misho 252: }
253:
254: /* very minimalistic 501 error message */
255: static void
256: Send501(struct upnphttp * h)
257: {
1.1.1.3 ! misho 258: static const char body501[] =
1.1 misho 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";
1.1.1.3 ! misho 262:
1.1 misho 263: h->respflags = FLAG_HTML;
264: BuildResp2_upnphttp(h, 501, "Not Implemented",
265: body501, sizeof(body501) - 1);
1.1.1.3 ! misho 266: SendRespAndClose_upnphttp(h);
1.1 misho 267: }
268:
1.1.1.3 ! misho 269: /* findendheaders() find the \r\n\r\n character sequence and
! 270: * return a pointer to it.
! 271: * It returns NULL if not found */
1.1 misho 272: static const char *
273: findendheaders(const char * s, int len)
274: {
1.1.1.3 ! misho 275: while(len-->3)
1.1 misho 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);
1.1.1.3 ! misho 298: SendRespAndClose_upnphttp(h);
1.1 misho 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: }
1.1.1.3 ! misho 322: SendRespAndClose_upnphttp(h);
1.1 misho 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: {
1.1.1.3 ! misho 333: /* the request body is received */
! 334: if(h->req_soapActionOff > 0)
1.1 misho 335: {
336: /* we can process the request */
337: syslog(LOG_INFO, "SOAPAction: %.*s",
1.1.1.3 ! misho 338: h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
! 339: ExecuteSoapAction(h,
! 340: h->req_buf + h->req_soapActionOff,
1.1 misho 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);
1.1.1.3 ! misho 351: SendRespAndClose_upnphttp(h);
1.1 misho 352: }
353: }
1.1.1.3 ! misho 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: }
1.1 misho 368: else
369: {
370: /* waiting for remaining data */
1.1.1.3 ! misho 371: h->state = EWaitingForHttpContent;
1.1 misho 372: }
373: }
374:
375: #ifdef ENABLE_EVENTS
1.1.1.2 misho 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;
1.1.1.3 ! misho 386: unsigned int i;
1.1.1.2 misho 387:
1.1.1.3 ! misho 388: if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8)
1.1.1.2 misho 389: return 0;
1.1.1.3 ! misho 390: if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0)
1.1.1.2 misho 391: return 0;
392: ipv6 = 0;
393: i = 0;
1.1.1.3 ! misho 394: p = h->req_buf + h->req_CallbackOff + 7;
1.1.1.2 misho 395: if(*p == '[') {
396: p++;
397: ipv6 = 1;
398: while(*p != ']' && i < (sizeof(addrstr)-1)
1.1.1.3 ! misho 399: && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
1.1.1.2 misho 400: addrstr[i++] = *(p++);
401: } else {
402: while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
1.1.1.3 ! misho 403: && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
1.1.1.2 misho 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:
1.1 misho 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",
1.1.1.3 ! misho 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)) {
1.1 misho 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);
1.1.1.3 ! misho 454: SendRespAndClose_upnphttp(h);
1.1 misho 455: } else {
456: /* - add to the subscriber list
1.1.1.3 ! misho 457: * - respond HTTP/x.x 200 OK
1.1 misho 458: * - Send the initial event message */
459: /* Server:, SID:; Timeout: Second-(xx|infinite) */
1.1.1.2 misho 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 ?) */
1.1.1.3 ! misho 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
1.1.1.2 misho 476: if(checkCallbackURL(h)) {
1.1.1.3 ! misho 477: sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
1.1.1.2 misho 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;
1.1.1.3 ! misho 483: h->res_SID = sid;
1.1.1.2 misho 484: }
485: BuildResp_upnphttp(h, 0, 0);
486: } else {
487: syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
1.1.1.3 ! misho 488: h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
1.1.1.2 misho 489: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
1.1 misho 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. */
1.1.1.3 ! misho 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) {
1.1 misho 506: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
507: } else {
1.1.1.2 misho 508: h->respflags = FLAG_TIMEOUT;
1.1 misho 509: BuildResp_upnphttp(h, 0, 0);
510: }
511: }
1.1.1.3 ! misho 512: SendRespAndClose_upnphttp(h);
1.1 misho 513: }
514: }
515:
516: static void
517: ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
518: {
519: syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
1.1.1.3 ! misho 520: syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
1.1 misho 521: /* Remove from the list */
1.1.1.3 ! misho 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) {
1.1 misho 532: BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
533: } else {
534: BuildResp_upnphttp(h, 0, 0);
535: }
1.1.1.3 ! misho 536: SendRespAndClose_upnphttp(h);
1.1 misho 537: }
538: #endif
539:
1.1.1.3 ! misho 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 */
1.1 misho 544: static void
545: ProcessHttpQuery_upnphttp(struct upnphttp * h)
546: {
1.1.1.3 ! misho 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: };
1.1 misho 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;
1.1.1.3 ! misho 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 */
1.1 misho 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;
1.1.1.3 ! misho 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
1.1 misho 609: #ifdef HAS_DUMMY_SERVICE
1.1.1.3 ! misho 610: sendDummyDesc(h);
! 611: #else
! 612: continue;
1.1 misho 613: #endif
1.1.1.3 ! misho 614: return;
! 615: }
1.1 misho 616: }
1.1.1.3 ! misho 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;
1.1.1.2 misho 623: }
1.1.1.3 ! misho 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;
1.1.1.2 misho 631: }
632: #endif
1.1.1.3 ! misho 633: syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
! 634: Send404(h);
1.1 misho 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: {
1.1.1.3 ! misho 665: char * h_tmp;
1.1 misho 666: char buf[2048];
667: int n;
1.1.1.3 ! misho 668:
1.1 misho 669: if(!h)
670: return;
671: switch(h->state)
672: {
1.1.1.3 ! misho 673: case EWaitingForHttpRequest:
! 674: n = recv(h->socket, buf, sizeof(buf), 0);
1.1 misho 675: if(n<0)
676: {
1.1.1.3 ! misho 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 */
1.1 misho 685: }
686: else if(n==0)
687: {
1.1.1.3 ! misho 688: syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
! 689: h->state = EToDelete;
1.1 misho 690: }
691: else
692: {
693: const char * endheaders;
694: /* if 1st arg of realloc() is null,
695: * realloc behaves the same as malloc() */
1.1.1.3 ! misho 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: }
1.1 misho 709: /* search for the string "\r\n\r\n" */
710: endheaders = findendheaders(h->req_buf, h->req_buflen);
711: if(endheaders)
712: {
1.1.1.3 ! misho 713: /* at this point, the request buffer (h->req_buf)
! 714: * is guaranteed to contain the \r\n\r\n character sequence */
1.1 misho 715: h->req_contentoff = endheaders - h->req_buf + 4;
716: ProcessHttpQuery_upnphttp(h);
717: }
718: }
719: break;
1.1.1.3 ! misho 720: case EWaitingForHttpContent:
! 721: n = recv(h->socket, buf, sizeof(buf), 0);
1.1 misho 722: if(n<0)
723: {
1.1.1.3 ! misho 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 */
1.1 misho 732: }
733: else if(n==0)
734: {
735: syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
1.1.1.3 ! misho 736: h->state = EToDelete;
1.1 misho 737: }
738: else
739: {
1.1.1.3 ! misho 740: void * tmp = realloc(h->req_buf, n + h->req_buflen);
! 741: if(!tmp)
1.1 misho 742: {
1.1.1.3 ! misho 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: }
1.1 misho 755: }
756: }
757: break;
1.1.1.3 ! misho 758: case ESendingContinue:
! 759: if(SendResp_upnphttp(h))
! 760: h->state = EWaitingForHttpContent;
! 761: break;
! 762: case ESendingAndClosing:
! 763: SendRespAndClose_upnphttp(h);
! 764: break;
1.1 misho 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;
1.1.1.3 ! misho 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;
1.1 misho 800: h->res_buf = (char *)malloc(templen);
1.1.1.3 ! misho 801: if(!h->res_buf) {
1.1.1.2 misho 802: syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
803: return;
804: }
1.1 misho 805: h->res_buf_alloclen = templen;
806: }
1.1.1.3 ! misho 807: h->res_sent = 0;
1.1 misho 808: h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
809: httpresphead, h->HttpVer,
810: respcode, respmsg,
1.1.1.3 ! misho 811: (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1.1 misho 812: bodylen);
1.1.1.3 ! misho 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 */
1.1 misho 815: /* Additional headers */
1.1.1.3 ! misho 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
1.1 misho 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,
1.1.1.3 ! misho 849: "SID: %s\r\n", h->res_SID);
1.1 misho 850: }
851: #endif
1.1.1.3 ! misho 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: }
1.1 misho 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: {
1.1.1.2 misho 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: }
1.1 misho 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:
1.1.1.3 ! misho 905: int
1.1 misho 906: SendResp_upnphttp(struct upnphttp * h)
907: {
1.1.1.2 misho 908: ssize_t n;
1.1.1.3 ! misho 909:
! 910: while (h->res_sent < h->res_buflen)
1.1 misho 911: {
1.1.1.3 ! misho 912: n = send(h->socket, h->res_buf + h->res_sent,
! 913: h->res_buflen - h->res_sent, 0);
1.1.1.2 misho 914: if(n<0)
915: {
1.1.1.3 ! misho 916: if(errno == EINTR)
! 917: continue; /* try again immediatly */
! 918: if(errno == EAGAIN || errno == EWOULDBLOCK)
! 919: {
! 920: /* try again later */
! 921: return 0;
! 922: }
1.1.1.2 misho 923: syslog(LOG_ERR, "send(res_buf): %m");
1.1.1.3 ! misho 924: break; /* avoid infinite loop */
1.1.1.2 misho 925: }
926: else if(n == 0)
927: {
1.1.1.3 ! misho 928: syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
! 929: h->res_sent, h->res_buflen);
1.1.1.2 misho 930: break;
931: }
932: else
933: {
1.1.1.3 ! misho 934: h->res_sent += n;
1.1.1.2 misho 935: }
1.1 misho 936: }
1.1.1.3 ! misho 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);
1.1 misho 974: }
975:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>