BCM WL 6.30.102.9 (r366174)
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blobc37b3fffe5eb8100b8bdc96f29af9f716f1c0c01
1 /* $Id: upnphttp.c,v 1.86 2013/02/07 10:26:07 nanard 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"
32 struct upnphttp *
33 New_upnphttp(int s)
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;
48 void
49 CloseSocket_upnphttp(struct upnphttp * h)
51 if(close(h->socket) < 0)
53 syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
55 h->socket = -1;
56 h->state = EToDelete;
59 void
60 Delete_upnphttp(struct upnphttp * h)
62 if(h)
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);
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)
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))
89 colon = line;
90 while(*colon != ':')
92 if(*colon == '\r' || *colon == '\n')
94 colon = NULL; /* no ':' character found on the line */
95 break;
97 colon++;
99 if(colon)
101 if(strncasecmp(line, "Content-Length", 14)==0)
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;
111 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
112 printf(" readbufflen=%d contentoff = %d\n",
113 h->req_buflen, h->req_contentoff);*/
115 else if(strncasecmp(line, "SOAPAction", 10)==0)
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] == '\''))
126 p++; n -= 2;
128 h->req_soapActionOff = p - h->req_buf;
129 h->req_soapActionLen = n;
131 else if(strncasecmp(line, "accept-language", 15) == 0)
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';
149 else if(strncasecmp(line, "expect", 6) == 0)
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");
162 #ifdef ENABLE_EVENTS
163 else if(strncasecmp(line, "Callback", 8)==0)
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);
174 else if(strncasecmp(line, "SID", 3)==0)
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;
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)
194 p = colon + 1;
195 while(isspace(*p))
196 p++;
197 if(strncasecmp(p, "Second-", 7)==0) {
198 h->req_Timeout = atoi(p+7);
201 #ifdef UPNP_STRICT
202 else if(strncasecmp(line, "nt", 2)==0)
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;
213 #endif
214 #endif
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;
225 /* very minimalistic 404 error message */
226 static void
227 Send404(struct upnphttp * h)
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";
234 h->respflags = FLAG_HTML;
235 BuildResp2_upnphttp(h, 404, "Not Found",
236 body404, sizeof(body404) - 1);
237 SendRespAndClose_upnphttp(h);
240 static void
241 Send405(struct upnphttp * h)
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";
248 h->respflags |= FLAG_HTML;
249 BuildResp2_upnphttp(h, 405, "Method Not Allowed",
250 body405, sizeof(body405) - 1);
251 SendRespAndClose_upnphttp(h);
254 /* very minimalistic 501 error message */
255 static void
256 Send501(struct upnphttp * h)
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";
263 h->respflags = FLAG_HTML;
264 BuildResp2_upnphttp(h, 501, "Not Implemented",
265 body501, sizeof(body501) - 1);
266 SendRespAndClose_upnphttp(h);
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)
275 while(len-->3)
277 if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
278 return s;
279 s++;
281 return NULL;
284 #ifdef HAS_DUMMY_SERVICE
285 static void
286 sendDummyDesc(struct upnphttp * h)
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);
300 #endif
302 /* Sends the description generated by the parameter */
303 static void
304 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
306 char * desc;
307 int len;
308 desc = f(&len);
309 if(!desc)
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);
318 else
320 BuildResp_upnphttp(h, desc, len);
322 SendRespAndClose_upnphttp(h);
323 free(desc);
326 /* ProcessHTTPPOST_upnphttp()
327 * executes the SOAP query if it is possible */
328 static void
329 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
331 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
333 /* the request body is received */
334 if(h->req_soapActionOff > 0)
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);
343 else
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);
354 else if(h->respflags & FLAG_CONTINUE)
356 /* Sending the 100 Continue response */
357 if(!h->res_buf) {
358 h->res_buf = malloc(256);
359 h->res_buf_alloclen = 256;
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;
368 else
370 /* waiting for remaining data */
371 h->state = EWaitingForHttpContent;
375 #ifdef ENABLE_EVENTS
377 * returns 0 if the callback header value is not valid
378 * 1 if it is valid.
380 static int
381 checkCallbackURL(struct upnphttp * h)
383 char addrstr[48];
384 int ipv6;
385 const char * p;
386 unsigned int i;
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++);
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;
432 #else
433 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
434 return 0;
435 #endif
437 return 1;
440 static void
441 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
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;
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);
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);
512 SendRespAndClose_upnphttp(h);
516 static void
517 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
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);
536 SendRespAndClose_upnphttp(h);
538 #endif
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)
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}
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)
598 h->req_command = EPost;
599 ProcessHTTPPOST_upnphttp(h);
601 else if(strcmp("GET", HttpCommand) == 0)
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;
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;
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;
632 #endif
633 syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
634 Send404(h);
636 #ifdef ENABLE_EVENTS
637 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
639 h->req_command = ESubscribe;
640 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
642 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
644 h->req_command = EUnSubscribe;
645 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
647 #else
648 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
650 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
651 Send501(h);
653 #endif
654 else
656 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
657 Send501(h);
662 void
663 Process_upnphttp(struct upnphttp * h)
665 char * h_tmp;
666 char buf[2048];
667 int n;
668 if(!h)
669 return;
670 switch(h->state)
672 case EWaitingForHttpRequest:
673 n = recv(h->socket, buf, sizeof(buf), 0);
674 if(n<0)
676 if(errno != EAGAIN &&
677 errno != EWOULDBLOCK &&
678 errno != EINTR)
680 syslog(LOG_ERR, "recv (state0): %m");
681 h->state = EToDelete;
683 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
685 else if(n==0)
687 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
688 inet_ntoa(h->clientaddr)); //!!TB - added client address
689 h->state = EToDelete;
691 else
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)
699 syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
700 h->state = EToDelete;
702 else
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';
709 /* search for the string "\r\n\r\n" */
710 endheaders = findendheaders(h->req_buf, h->req_buflen);
711 if(endheaders)
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);
719 break;
720 case EWaitingForHttpContent:
721 n = recv(h->socket, buf, sizeof(buf), 0);
722 if(n<0)
724 if(errno != EAGAIN &&
725 errno != EWOULDBLOCK &&
726 errno != EINTR)
728 syslog(LOG_ERR, "recv (state1): %m");
729 h->state = EToDelete;
731 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
733 else if(n==0)
735 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
736 inet_ntoa(h->clientaddr)); //!!TB - added client address
737 h->state = EToDelete;
739 else
741 void * tmp = realloc(h->req_buf, n + h->req_buflen);
742 if(!tmp)
744 syslog(LOG_ERR, "memory allocation error %m");
745 h->state = EToDelete;
747 else
749 h->req_buf = tmp;
750 memcpy(h->req_buf + h->req_buflen, buf, n);
751 h->req_buflen += n;
752 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
754 ProcessHTTPPOST_upnphttp(h);
758 break;
759 case ESendingContinue:
760 if(SendResp_upnphttp(h))
761 h->state = EWaitingForHttpContent;
762 break;
763 case ESendingAndClosing:
764 SendRespAndClose_upnphttp(h);
765 break;
766 default:
767 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
771 static const char httpresphead[] =
772 "%s %d %s\r\n"
773 "Content-Type: %s\r\n"
774 "Connection: close\r\n"
775 "Content-Length: %d\r\n"
776 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
777 "Ext:\r\n"
778 ; /*"\r\n";*/
780 "<?xml version=\"1.0\"?>\n"
781 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
782 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
783 "<s:Body>"
785 "</s:Body>"
786 "</s:Envelope>";
788 /* with response code and response message
789 * also allocate enough memory */
791 void
792 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
793 const char * respmsg,
794 int bodylen)
796 int templen;
797 if(!h->res_buf ||
798 h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
799 if(h->res_buf)
800 free(h->res_buf);
801 templen = sizeof(httpresphead) + 256 + bodylen;
802 h->res_buf = (char *)malloc(templen);
803 if(!h->res_buf) {
804 syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
805 return;
807 h->res_buf_alloclen = templen;
809 h->res_sent = 0;
810 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
811 httpresphead, h->HttpVer,
812 respcode, respmsg,
813 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
814 bodylen);
815 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
816 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
817 /* Additional headers */
818 #ifdef ENABLE_HTTP_DATE
820 char http_date[64];
821 time_t t;
822 struct tm tm;
823 time(&t);
824 gmtime_r(&t, &tm);
825 /* %a and %b depend on locale */
826 strftime(http_date, sizeof(http_date),
827 "%a, %d %b %Y %H:%M:%S GMT", &tm);
828 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
829 h->res_buf_alloclen - h->res_buflen,
830 "Date: %s\r\n", http_date);
832 #endif
833 #ifdef ENABLE_EVENTS
834 if(h->respflags & FLAG_TIMEOUT) {
835 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
836 h->res_buf_alloclen - h->res_buflen,
837 "Timeout: Second-");
838 if(h->req_Timeout) {
839 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
840 h->res_buf_alloclen - h->res_buflen,
841 "%d\r\n", h->req_Timeout);
842 } else {
843 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
844 h->res_buf_alloclen - h->res_buflen,
845 "infinite\r\n");
848 if(h->respflags & FLAG_SID) {
849 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
850 h->res_buf_alloclen - h->res_buflen,
851 "SID: %s\r\n", h->res_SID);
853 #endif
854 if(h->respflags & FLAG_ALLOW_POST) {
855 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
856 h->res_buf_alloclen - h->res_buflen,
857 "Allow: %s\r\n", "POST");
858 } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
859 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
860 h->res_buf_alloclen - h->res_buflen,
861 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
863 if(h->accept_language[0] != '\0') {
864 /* defaulting to "en" */
865 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
866 h->res_buf_alloclen - h->res_buflen,
867 "Content-Language: %s\r\n",
868 h->accept_language[0] == '*' ? "en" : h->accept_language);
870 h->res_buf[h->res_buflen++] = '\r';
871 h->res_buf[h->res_buflen++] = '\n';
872 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
874 char * tmp;
875 tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
876 if(tmp)
878 h->res_buf = tmp;
879 h->res_buf_alloclen = h->res_buflen + bodylen;
881 else
883 syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
888 void
889 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
890 const char * respmsg,
891 const char * body, int bodylen)
893 BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
894 if(body)
895 memcpy(h->res_buf + h->res_buflen, body, bodylen);
896 h->res_buflen += bodylen;
899 /* responding 200 OK ! */
900 void
901 BuildResp_upnphttp(struct upnphttp * h,
902 const char * body, int bodylen)
904 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
908 SendResp_upnphttp(struct upnphttp * h)
910 ssize_t n;
912 while (h->res_sent < h->res_buflen)
914 n = send(h->socket, h->res_buf + h->res_sent,
915 h->res_buflen - h->res_sent, 0);
916 if(n<0)
918 if(errno == EINTR)
919 continue; /* try again immediatly */
920 if(errno == EAGAIN || errno == EWOULDBLOCK)
922 /* try again later */
923 return 0;
925 syslog(LOG_ERR, "send(res_buf): %m");
926 break; /* avoid infinite loop */
928 else if(n == 0)
930 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
931 h->res_sent, h->res_buflen);
932 break;
934 else
936 h->res_sent += n;
939 return 1; /* finished */
942 void
943 SendRespAndClose_upnphttp(struct upnphttp * h)
945 ssize_t n;
947 while (h->res_sent < h->res_buflen)
949 n = send(h->socket, h->res_buf + h->res_sent,
950 h->res_buflen - h->res_sent, 0);
951 if(n<0)
953 if(errno == EINTR)
954 continue; /* try again immediatly */
955 if(errno == EAGAIN || errno == EWOULDBLOCK)
957 /* try again later */
958 h->state = ESendingAndClosing;
959 return;
961 syslog(LOG_ERR, "send(res_buf): %m");
962 break; /* avoid infinite loop */
964 else if(n == 0)
966 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
967 h->res_sent, h->res_buflen);
968 break;
970 else
972 h->res_sent += n;
975 CloseSocket_upnphttp(h);