Stealth Mode script
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blob63cff274ac066bacfefd0fd9085b79a1f0823f02
1 /* $Id: upnphttp.c,v 1.81 2012/10/04 22:09:34 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 static void
76 ParseHttpHeaders(struct upnphttp * h)
78 char * line;
79 char * colon;
80 char * p;
81 int n;
82 line = h->req_buf;
83 /* TODO : check if req_buf, contentoff are ok */
84 while(line < (h->req_buf + h->req_contentoff))
86 colon = strchr(line, ':');
87 if(colon)
89 if(strncasecmp(line, "Content-Length", 14)==0)
91 p = colon;
92 while(*p < '0' || *p > '9')
93 p++;
94 h->req_contentlen = atoi(p);
95 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
96 printf(" readbufflen=%d contentoff = %d\n",
97 h->req_buflen, h->req_contentoff);*/
99 else if(strncasecmp(line, "SOAPAction", 10)==0)
101 p = colon;
102 n = 0;
103 while(*p == ':' || *p == ' ' || *p == '\t')
104 p++;
105 while(p[n]>=' ')
107 n++;
109 if((p[0] == '"' && p[n-1] == '"')
110 || (p[0] == '\'' && p[n-1] == '\''))
112 p++; n -= 2;
114 h->req_soapActionOff = p - h->req_buf;
115 h->req_soapActionLen = n;
117 else if(strncasecmp(line, "accept-language", 15) == 0)
119 p = colon;
120 n = 0;
121 while(*p == ':' || *p == ' ' || *p == '\t')
122 p++;
123 while(p[n]>=' ')
124 n++;
125 syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p);
126 /* keep only the 1st accepted language */
127 n = 0;
128 while(p[n]>' ' && p[n] != ',')
129 n++;
130 if(n >= (int)sizeof(h->accept_language))
131 n = (int)sizeof(h->accept_language) - 1;
132 memcpy(h->accept_language, p, n);
133 h->accept_language[n] = '\0';
135 else if(strncasecmp(line, "expect", 6) == 0)
137 p = colon;
138 n = 0;
139 while(*p == ':' || *p == ' ' || *p == '\t')
140 p++;
141 while(p[n]>=' ')
142 n++;
143 if(strncasecmp(p, "100-continue", 12) == 0) {
144 h->respflags |= FLAG_CONTINUE;
145 syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
148 #ifdef ENABLE_EVENTS
149 else if(strncasecmp(line, "Callback", 8)==0)
151 p = colon;
152 while(*p != '<' && *p != '\r' )
153 p++;
154 n = 0;
155 while(p[n] != '>' && p[n] != '\r' )
156 n++;
157 h->req_CallbackOff = p + 1 - h->req_buf;
158 h->req_CallbackLen = MAX(0, n - 1);
160 else if(strncasecmp(line, "SID", 3)==0)
162 p = colon + 1;
163 while(isspace(*p))
164 p++;
165 n = 0;
166 while(!isspace(p[n]))
167 n++;
168 h->req_SIDOff = p - h->req_buf;
169 h->req_SIDLen = n;
171 /* Timeout: Seconds-nnnn */
172 /* TIMEOUT
173 Recommended. Requested duration until subscription expires,
174 either number of seconds or infinite. Recommendation
175 by a UPnP Forum working committee. Defined by UPnP vendor.
176 Consists of the keyword "Second-" followed (without an
177 intervening space) by either an integer or the keyword "infinite". */
178 else if(strncasecmp(line, "Timeout", 7)==0)
180 p = colon + 1;
181 while(isspace(*p))
182 p++;
183 if(strncasecmp(p, "Second-", 7)==0) {
184 h->req_Timeout = atoi(p+7);
187 #ifdef UPNP_STRICT
188 else if(strncasecmp(line, "nt", 2)==0)
190 p = colon + 1;
191 while(isspace(*p))
192 p++;
193 n = 0;
194 while(!isspace(p[n]))
195 n++;
196 h->req_NTOff = p - h->req_buf;
197 h->req_NTLen = n;
199 #endif
200 #endif
202 while(!(line[0] == '\r' && line[1] == '\n'))
203 line++;
204 line += 2;
208 /* very minimalistic 404 error message */
209 static void
210 Send404(struct upnphttp * h)
212 static const char body404[] =
213 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
214 "<BODY><H1>Not Found</H1>The requested URL was not found"
215 " on this server.</BODY></HTML>\r\n";
217 h->respflags = FLAG_HTML;
218 BuildResp2_upnphttp(h, 404, "Not Found",
219 body404, sizeof(body404) - 1);
220 SendRespAndClose_upnphttp(h);
223 static void
224 Send405(struct upnphttp * h)
226 static const char body405[] =
227 "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
228 "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
229 "is not allowed on this resource.</BODY></HTML>\r\n";
231 h->respflags |= FLAG_HTML;
232 BuildResp2_upnphttp(h, 405, "Method Not Allowed",
233 body405, sizeof(body405) - 1);
234 SendRespAndClose_upnphttp(h);
237 /* very minimalistic 501 error message */
238 static void
239 Send501(struct upnphttp * h)
241 static const char body501[] =
242 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
243 "<BODY><H1>Not Implemented</H1>The HTTP Method "
244 "is not implemented by this server.</BODY></HTML>\r\n";
246 h->respflags = FLAG_HTML;
247 BuildResp2_upnphttp(h, 501, "Not Implemented",
248 body501, sizeof(body501) - 1);
249 SendRespAndClose_upnphttp(h);
252 static const char *
253 findendheaders(const char * s, int len)
255 while(len-->0)
257 if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
258 return s;
259 s++;
261 return NULL;
264 #ifdef HAS_DUMMY_SERVICE
265 static void
266 sendDummyDesc(struct upnphttp * h)
268 static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
269 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
270 " <specVersion>"
271 " <major>1</major>"
272 " <minor>0</minor>"
273 " </specVersion>"
274 " <actionList />"
275 " <serviceStateTable />"
276 "</scpd>\r\n";
277 BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
278 SendRespAndClose_upnphttp(h);
280 #endif
282 /* Sends the description generated by the parameter */
283 static void
284 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
286 char * desc;
287 int len;
288 desc = f(&len);
289 if(!desc)
291 static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
292 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
293 syslog(LOG_ERR, "Failed to generate XML description");
294 h->respflags = FLAG_HTML;
295 BuildResp2_upnphttp(h, 500, "Internal Server Error",
296 error500, sizeof(error500)-1);
298 else
300 BuildResp_upnphttp(h, desc, len);
302 SendRespAndClose_upnphttp(h);
303 free(desc);
306 /* ProcessHTTPPOST_upnphttp()
307 * executes the SOAP query if it is possible */
308 static void
309 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
311 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
313 /* the request body is received */
314 if(h->req_soapActionOff > 0)
316 /* we can process the request */
317 syslog(LOG_INFO, "SOAPAction: %.*s",
318 h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
319 ExecuteSoapAction(h,
320 h->req_buf + h->req_soapActionOff,
321 h->req_soapActionLen);
323 else
325 static const char err400str[] =
326 "<html><body>Bad request</body></html>";
327 syslog(LOG_INFO, "No SOAPAction in HTTP headers");
328 h->respflags = FLAG_HTML;
329 BuildResp2_upnphttp(h, 400, "Bad Request",
330 err400str, sizeof(err400str) - 1);
331 SendRespAndClose_upnphttp(h);
334 else if(h->respflags & FLAG_CONTINUE)
336 /* Sending the 100 Continue response */
337 if(!h->res_buf) {
338 h->res_buf = malloc(256);
339 h->res_buf_alloclen = 256;
341 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
342 "%s 100 Continue\r\n\r\n", h->HttpVer);
343 h->res_sent = 0;
344 h->state = ESendingContinue;
345 if(SendResp_upnphttp(h))
346 h->state = EWaitingForHttpContent;
348 else
350 /* waiting for remaining data */
351 h->state = EWaitingForHttpContent;
355 #ifdef ENABLE_EVENTS
357 * returns 0 if the callback header value is not valid
358 * 1 if it is valid.
360 static int
361 checkCallbackURL(struct upnphttp * h)
363 char addrstr[48];
364 int ipv6;
365 const char * p;
366 unsigned int i;
368 if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8)
369 return 0;
370 if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0)
371 return 0;
372 ipv6 = 0;
373 i = 0;
374 p = h->req_buf + h->req_CallbackOff + 7;
375 if(*p == '[') {
376 p++;
377 ipv6 = 1;
378 while(*p != ']' && i < (sizeof(addrstr)-1)
379 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
380 addrstr[i++] = *(p++);
381 } else {
382 while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
383 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
384 addrstr[i++] = *(p++);
386 addrstr[i] = '\0';
387 if(ipv6) {
388 struct in6_addr addr;
389 if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
390 return 0;
391 #ifdef ENABLE_IPV6
392 if(!h->ipv6
393 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
394 return 0;
395 #else
396 return 0;
397 #endif
398 } else {
399 struct in_addr addr;
400 if(inet_pton(AF_INET, addrstr, &addr) <= 0)
401 return 0;
402 #ifdef ENABLE_IPV6
403 if(h->ipv6) {
404 if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
405 return 0;
406 if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
407 return 0;
408 } else {
409 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
410 return 0;
412 #else
413 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
414 return 0;
415 #endif
417 return 1;
420 static void
421 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
423 const char * sid;
424 syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
425 syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
426 h->req_CallbackLen, h->req_buf + h->req_CallbackOff,
427 h->req_Timeout);
428 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
429 if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
430 /* Missing or invalid CALLBACK : 412 Precondition Failed.
431 * If CALLBACK header is missing or does not contain a valid HTTP URL,
432 * the publisher must respond with HTTP error 412 Precondition Failed*/
433 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
434 SendRespAndClose_upnphttp(h);
435 } else {
436 /* - add to the subscriber list
437 * - respond HTTP/x.x 200 OK
438 * - Send the initial event message */
439 /* Server:, SID:; Timeout: Second-(xx|infinite) */
440 /* Check that the callback URL is on the same IP as
441 * the request, and not on the internet, nor on ourself (DOS attack ?) */
442 if(h->req_CallbackOff > 0) {
443 #ifdef UPNP_STRICT
444 /* SID: and Callback: are incompatible */
445 if(h->req_SIDOff > 0) {
446 syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
447 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
448 /* "NT: upnp:event" header is mandatory */
449 } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
450 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
451 syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
452 h->req_NTLen, h->req_buf + h->req_NTOff);
453 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
454 } else
455 #endif
456 if(checkCallbackURL(h)) {
457 sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
458 h->req_CallbackLen, h->req_Timeout);
459 h->respflags = FLAG_TIMEOUT;
460 if(sid) {
461 syslog(LOG_DEBUG, "generated sid=%s", sid);
462 h->respflags |= FLAG_SID;
463 h->res_SID = sid;
465 BuildResp_upnphttp(h, 0, 0);
466 } else {
467 syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
468 h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
469 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
471 } else {
472 /* subscription renew */
473 /* Invalid SID
474 412 Precondition Failed. If a SID does not correspond to a known,
475 un-expired subscription, the publisher must respond
476 with HTTP error 412 Precondition Failed. */
477 #ifdef UPNP_STRICT
478 /* SID: and NT: headers are incompatibles */
479 if(h->req_NTOff > 0) {
480 syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
481 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
482 } else
483 #endif
484 if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen,
485 h->req_Timeout) < 0) {
486 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
487 } else {
488 h->respflags = FLAG_TIMEOUT;
489 BuildResp_upnphttp(h, 0, 0);
492 SendRespAndClose_upnphttp(h);
496 static void
497 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
499 syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
500 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
501 /* Remove from the list */
502 #ifdef UPNP_STRICT
503 if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
504 /* SID: header missing or empty */
505 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
506 } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
507 /* no NT: or Callback: header must be present */
508 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
509 } else
510 #endif
511 if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
512 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
513 } else {
514 BuildResp_upnphttp(h, 0, 0);
516 SendRespAndClose_upnphttp(h);
518 #endif
520 /* Parse and process Http Query
521 * called once all the HTTP headers have been received. */
522 static void
523 ProcessHttpQuery_upnphttp(struct upnphttp * h)
525 static const struct {
526 const char * path;
527 char * (* f)(int *);
528 } path_desc[] = {
529 { ROOTDESC_PATH, genRootDesc},
530 { WANIPC_PATH, genWANIPCn},
531 { WANCFG_PATH, genWANCfg},
532 #ifdef HAS_DUMMY_SERVICE
533 { DUMMY_PATH, NULL},
534 #endif
535 #ifdef ENABLE_L3F_SERVICE
536 { L3F_PATH, genL3F},
537 #endif
538 #ifdef ENABLE_6FC_SERVICE
539 { WANIP6FC_PATH, gen6FC},
540 #endif
541 #ifdef ENABLE_DP_SERVICE
542 { DP_PATH, genDP},
543 #endif
544 { NULL, NULL}
546 char HttpCommand[16];
547 char HttpUrl[128];
548 char * HttpVer;
549 char * p;
550 int i;
551 p = h->req_buf;
552 if(!p)
553 return;
554 for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
555 HttpCommand[i] = *(p++);
556 HttpCommand[i] = '\0';
557 while(*p==' ')
558 p++;
559 for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
560 HttpUrl[i] = *(p++);
561 HttpUrl[i] = '\0';
562 while(*p==' ')
563 p++;
564 HttpVer = h->HttpVer;
565 for(i = 0; i<15 && *p != '\r'; i++)
566 HttpVer[i] = *(p++);
567 HttpVer[i] = '\0';
568 syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
569 HttpCommand, HttpUrl, HttpVer);
570 ParseHttpHeaders(h);
571 if(strcmp("POST", HttpCommand) == 0)
573 h->req_command = EPost;
574 ProcessHTTPPOST_upnphttp(h);
576 else if(strcmp("GET", HttpCommand) == 0)
578 h->req_command = EGet;
579 for(i=0; path_desc[i].path; i++) {
580 if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
581 if(path_desc[i].f)
582 sendXMLdesc(h, path_desc[i].f);
583 else
584 #ifdef HAS_DUMMY_SERVICE
585 sendDummyDesc(h);
586 #else
587 continue;
588 #endif
589 return;
592 if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
593 /* 405 Method Not Allowed
594 * Allow: POST */
595 h->respflags = FLAG_ALLOW_POST;
596 Send405(h);
597 return;
599 #ifdef ENABLE_EVENTS
600 if(0 == memcmp(HttpUrl, "/evt/", 5)) {
601 /* 405 Method Not Allowed
602 * Allow: SUBSCRIBE, UNSUBSCRIBE */
603 h->respflags = FLAG_ALLOW_SUB_UNSUB;
604 Send405(h);
605 return;
607 #endif
608 syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
609 Send404(h);
611 #ifdef ENABLE_EVENTS
612 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
614 h->req_command = ESubscribe;
615 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
617 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
619 h->req_command = EUnSubscribe;
620 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
622 #else
623 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
625 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
626 Send501(h);
628 #endif
629 else
631 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
632 Send501(h);
637 void
638 Process_upnphttp(struct upnphttp * h)
640 char buf[2048];
641 int n;
642 if(!h)
643 return;
644 switch(h->state)
646 case EWaitingForHttpRequest:
647 n = recv(h->socket, buf, sizeof(buf), 0);
648 if(n<0)
650 if(errno != EAGAIN &&
651 errno != EWOULDBLOCK &&
652 errno != EINTR)
654 syslog(LOG_ERR, "recv (state0): %m");
655 h->state = EToDelete;
657 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
659 else if(n==0)
661 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
662 inet_ntoa(h->clientaddr)); //!!TB - added client address
663 h->state = EToDelete;
665 else
667 const char * endheaders;
668 /* if 1st arg of realloc() is null,
669 * realloc behaves the same as malloc() */
670 h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
671 memcpy(h->req_buf + h->req_buflen, buf, n);
672 h->req_buflen += n;
673 h->req_buf[h->req_buflen] = '\0';
674 /* search for the string "\r\n\r\n" */
675 endheaders = findendheaders(h->req_buf, h->req_buflen);
676 if(endheaders)
678 h->req_contentoff = endheaders - h->req_buf + 4;
679 ProcessHttpQuery_upnphttp(h);
682 break;
683 case EWaitingForHttpContent:
684 n = recv(h->socket, buf, sizeof(buf), 0);
685 if(n<0)
687 if(errno != EAGAIN &&
688 errno != EWOULDBLOCK &&
689 errno != EINTR)
691 syslog(LOG_ERR, "recv (state1): %m");
692 h->state = EToDelete;
694 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
696 else if(n==0)
698 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
699 inet_ntoa(h->clientaddr)); //!!TB - added client address
700 h->state = EToDelete;
702 else
704 void * tmp = realloc(h->req_buf, n + h->req_buflen);
705 if(!tmp)
707 syslog(LOG_ERR, "memory allocation error %m");
708 h->state = EToDelete;
710 else
712 h->req_buf = tmp;
713 memcpy(h->req_buf + h->req_buflen, buf, n);
714 h->req_buflen += n;
715 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
717 ProcessHTTPPOST_upnphttp(h);
721 break;
722 case ESendingContinue:
723 if(SendResp_upnphttp(h))
724 h->state = EWaitingForHttpContent;
725 break;
726 case ESendingAndClosing:
727 SendRespAndClose_upnphttp(h);
728 break;
729 default:
730 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
734 static const char httpresphead[] =
735 "%s %d %s\r\n"
736 "Content-Type: %s\r\n"
737 "Connection: close\r\n"
738 "Content-Length: %d\r\n"
739 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
740 ; /*"\r\n";*/
742 "<?xml version=\"1.0\"?>\n"
743 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
744 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
745 "<s:Body>"
747 "</s:Body>"
748 "</s:Envelope>";
750 /* with response code and response message
751 * also allocate enough memory */
753 void
754 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
755 const char * respmsg,
756 int bodylen)
758 int templen;
759 if(!h->res_buf ||
760 h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
761 if(h->res_buf)
762 free(h->res_buf);
763 templen = sizeof(httpresphead) + 256 + bodylen;
764 h->res_buf = (char *)malloc(templen);
765 if(!h->res_buf) {
766 syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
767 return;
769 h->res_buf_alloclen = templen;
771 h->res_sent = 0;
772 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
773 httpresphead, h->HttpVer,
774 respcode, respmsg,
775 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
776 bodylen);
777 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
778 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
779 /* Additional headers */
780 #ifdef ENABLE_HTTP_DATE
782 char http_date[64];
783 time_t t;
784 struct tm tm;
785 time(&t);
786 gmtime_r(&t, &tm);
787 /* %a and %b depend on locale */
788 strftime(http_date, sizeof(http_date),
789 "%a, %d %b %Y %H:%M:%S GMT", &tm);
790 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
791 h->res_buf_alloclen - h->res_buflen,
792 "Date: %s\r\n", http_date);
794 #endif
795 #ifdef ENABLE_EVENTS
796 if(h->respflags & FLAG_TIMEOUT) {
797 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
798 h->res_buf_alloclen - h->res_buflen,
799 "Timeout: Second-");
800 if(h->req_Timeout) {
801 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
802 h->res_buf_alloclen - h->res_buflen,
803 "%d\r\n", h->req_Timeout);
804 } else {
805 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
806 h->res_buf_alloclen - h->res_buflen,
807 "infinite\r\n");
810 if(h->respflags & FLAG_SID) {
811 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
812 h->res_buf_alloclen - h->res_buflen,
813 "SID: %s\r\n", h->res_SID);
815 #endif
816 if(h->respflags & FLAG_ALLOW_POST) {
817 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
818 h->res_buf_alloclen - h->res_buflen,
819 "Allow: %s\r\n", "POST");
820 } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
821 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
822 h->res_buf_alloclen - h->res_buflen,
823 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
825 if(h->accept_language[0] != '\0') {
826 /* defaulting to "en" */
827 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
828 h->res_buf_alloclen - h->res_buflen,
829 "Content-Language: %s\r\n",
830 h->accept_language[0] == '*' ? "en" : h->accept_language);
832 h->res_buf[h->res_buflen++] = '\r';
833 h->res_buf[h->res_buflen++] = '\n';
834 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
836 char * tmp;
837 tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
838 if(tmp)
840 h->res_buf = tmp;
841 h->res_buf_alloclen = h->res_buflen + bodylen;
843 else
845 syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
850 void
851 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
852 const char * respmsg,
853 const char * body, int bodylen)
855 BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
856 if(body)
857 memcpy(h->res_buf + h->res_buflen, body, bodylen);
858 h->res_buflen += bodylen;
861 /* responding 200 OK ! */
862 void
863 BuildResp_upnphttp(struct upnphttp * h,
864 const char * body, int bodylen)
866 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
870 SendResp_upnphttp(struct upnphttp * h)
872 ssize_t n;
874 while (h->res_sent < h->res_buflen)
876 n = send(h->socket, h->res_buf + h->res_sent,
877 h->res_buflen - h->res_sent, 0);
878 if(n<0)
880 if(errno == EINTR)
881 continue; /* try again immediatly */
882 if(errno == EAGAIN || errno == EWOULDBLOCK)
884 /* try again later */
885 return 0;
887 syslog(LOG_ERR, "send(res_buf): %m");
888 break; /* avoid infinite loop */
890 else if(n == 0)
892 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
893 h->res_sent, h->res_buflen);
894 break;
896 else
898 h->res_sent += n;
901 return 1; /* finished */
904 void
905 SendRespAndClose_upnphttp(struct upnphttp * h)
907 ssize_t n;
909 while (h->res_sent < h->res_buflen)
911 n = send(h->socket, h->res_buf + h->res_sent,
912 h->res_buflen - h->res_sent, 0);
913 if(n<0)
915 if(errno == EINTR)
916 continue; /* try again immediatly */
917 if(errno == EAGAIN || errno == EWOULDBLOCK)
919 /* try again later */
920 h->state = ESendingAndClosing;
921 return;
923 syslog(LOG_ERR, "send(res_buf): %m");
924 break; /* avoid infinite loop */
926 else if(n == 0)
928 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
929 h->res_sent, h->res_buflen);
930 break;
932 else
934 h->res_sent += n;
937 CloseSocket_upnphttp(h);