Miniupnpd: update to 1.9 (20150430)
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blob958b404bab2770ceced27462c1571f68f793bf04
1 /* $Id: upnphttp.c,v 1.99 2014/12/09 17:25:30 nanard Exp $ */
2 /* Project : miniupnp
3 * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2014 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 #ifdef ENABLE_HTTPS
33 #include <openssl/err.h>
34 #include <openssl/engine.h>
35 #include <openssl/conf.h>
36 static SSL_CTX *ssl_ctx = NULL;
38 #ifndef HTTPS_CERTFILE
39 #define HTTPS_CERTFILE "/etc/ssl/certs/ssl-cert-snakeoil.pem"
40 #endif
41 #ifndef HTTPS_KEYFILE
42 #define HTTPS_KEYFILE "/etc/ssl/private/ssl-cert-snakeoil.key"
43 #endif
45 static void
46 syslogsslerr(void)
48 unsigned long err;
49 char buffer[256];
50 while((err = ERR_get_error()) != 0) {
51 syslog(LOG_ERR, "%s", ERR_error_string(err, buffer));
55 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
57 syslog(LOG_DEBUG, "verify_callback(%d, %p)", preverify_ok, ctx);
58 return preverify_ok;
61 int init_ssl(void)
63 SSL_METHOD *method;
64 SSL_library_init();
65 SSL_load_error_strings();
66 method = TLSv1_server_method();
67 if(method == NULL) {
68 syslog(LOG_ERR, "TLSv1_server_method() failed");
69 syslogsslerr();
70 return -1;
72 ssl_ctx = SSL_CTX_new(method);
73 if(ssl_ctx == NULL) {
74 syslog(LOG_ERR, "SSL_CTX_new() failed");
75 syslogsslerr();
76 return -1;
78 /* set the local certificate */
79 if(!SSL_CTX_use_certificate_file(ssl_ctx, HTTPS_CERTFILE, SSL_FILETYPE_PEM)) {
80 syslog(LOG_ERR, "SSL_CTX_use_certificate_file(%s) failed", HTTPS_CERTFILE);
81 syslogsslerr();
82 return -1;
84 /* set the private key */
85 if(!SSL_CTX_use_PrivateKey_file(ssl_ctx, HTTPS_KEYFILE, SSL_FILETYPE_PEM)) {
86 syslog(LOG_ERR, "SSL_CTX_use_PrivateKey_file(%s) failed", HTTPS_KEYFILE);
87 syslogsslerr();
88 return -1;
90 /* verify private key */
91 if(!SSL_CTX_check_private_key(ssl_ctx)) {
92 syslog(LOG_ERR, "SSL_CTX_check_private_key() failed");
93 syslogsslerr();
94 return -1;
96 /*SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, verify_callback);*/
97 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, verify_callback);
98 /*SSL_CTX_set_verify_depth(depth);*/
99 syslog(LOG_INFO, "using %s", SSLeay_version(SSLEAY_VERSION));
100 return 0;
103 void free_ssl(void)
105 /* free context */
106 if(ssl_ctx != NULL) {
107 SSL_CTX_free(ssl_ctx);
108 ssl_ctx = NULL;
110 ERR_remove_state(0);
111 ENGINE_cleanup();
112 CONF_modules_unload(1);
113 ERR_free_strings();
114 EVP_cleanup();
115 CRYPTO_cleanup_all_ex_data();
117 #endif /* ENABLE_HTTPS */
119 struct upnphttp *
120 New_upnphttp(int s)
122 struct upnphttp * ret;
123 if(s<0)
124 return NULL;
125 ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
126 if(ret == NULL)
127 return NULL;
128 memset(ret, 0, sizeof(struct upnphttp));
129 ret->socket = s;
130 if(!set_non_blocking(s))
131 syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
132 return ret;
135 #ifdef ENABLE_HTTPS
136 void
137 InitSSL_upnphttp(struct upnphttp * h)
139 int r;
140 h->ssl = SSL_new(ssl_ctx);
141 if(h->ssl == NULL) {
142 syslog(LOG_ERR, "SSL_new() failed");
143 syslogsslerr();
144 abort();
146 if(!SSL_set_fd(h->ssl, h->socket)) {
147 syslog(LOG_ERR, "SSL_set_fd() failed");
148 syslogsslerr();
149 abort();
151 r = SSL_accept(h->ssl); /* start the handshaking */
152 if(r < 0) {
153 int err;
154 err = SSL_get_error(h->ssl, r);
155 syslog(LOG_DEBUG, "SSL_accept() returned %d, SSL_get_error() %d", r, err);
156 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
157 syslog(LOG_ERR, "SSL_accept() failed");
158 syslogsslerr();
159 abort();
163 #endif /* ENABLE_HTTPS */
165 void
166 CloseSocket_upnphttp(struct upnphttp * h)
168 /* SSL_shutdown() ? */
169 if(close(h->socket) < 0)
171 syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
173 h->socket = -1;
174 h->state = EToDelete;
177 void
178 Delete_upnphttp(struct upnphttp * h)
180 if(h)
182 #ifdef ENABLE_HTTPS
183 if(h->ssl)
184 SSL_free(h->ssl);
185 #endif
186 if(h->socket >= 0)
187 CloseSocket_upnphttp(h);
188 if(h->req_buf)
189 free(h->req_buf);
190 if(h->res_buf)
191 free(h->res_buf);
192 free(h);
196 /* parse HttpHeaders of the REQUEST
197 * This function is called after the \r\n\r\n character
198 * sequence has been found in h->req_buf */
199 static void
200 ParseHttpHeaders(struct upnphttp * h)
202 char * line;
203 char * colon;
204 char * p;
205 int n;
206 if((h->req_buf == NULL) || (h->req_contentoff <= 0))
207 return;
208 line = h->req_buf;
209 while(line < (h->req_buf + h->req_contentoff))
211 colon = line;
212 while(*colon != ':')
214 if(*colon == '\r' || *colon == '\n')
216 colon = NULL; /* no ':' character found on the line */
217 break;
219 colon++;
221 if(colon)
223 if(strncasecmp(line, "Content-Length:", 15)==0)
225 p = colon;
226 while((*p < '0' || *p > '9') && (*p != '\r') && (*p != '\n'))
227 p++;
228 h->req_contentlen = atoi(p);
229 if(h->req_contentlen < 0) {
230 syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
231 h->req_contentlen = 0;
233 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
234 printf(" readbufflen=%d contentoff = %d\n",
235 h->req_buflen, h->req_contentoff);*/
237 else if(strncasecmp(line, "Host:", 5)==0)
239 p = colon;
240 n = 0;
241 while(*p == ':' || *p == ' ' || *p == '\t')
242 p++;
243 while(p[n]>' ')
244 n++;
245 h->req_HostOff = p - h->req_buf;
246 h->req_HostLen = n;
248 else if(strncasecmp(line, "SOAPAction:", 11)==0)
250 p = colon;
251 n = 0;
252 while(*p == ':' || *p == ' ' || *p == '\t')
253 p++;
254 while(p[n]>=' ')
255 n++;
256 if((p[0] == '"' && p[n-1] == '"')
257 || (p[0] == '\'' && p[n-1] == '\''))
259 p++; n -= 2;
261 h->req_soapActionOff = p - h->req_buf;
262 h->req_soapActionLen = n;
264 else if(strncasecmp(line, "accept-language:", 16) == 0)
266 p = colon;
267 n = 0;
268 while(*p == ':' || *p == ' ' || *p == '\t')
269 p++;
270 while(p[n]>=' ')
271 n++;
272 syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p);
273 /* keep only the 1st accepted language */
274 n = 0;
275 while(p[n]>' ' && p[n] != ',')
276 n++;
277 if(n >= (int)sizeof(h->accept_language))
278 n = (int)sizeof(h->accept_language) - 1;
279 memcpy(h->accept_language, p, n);
280 h->accept_language[n] = '\0';
282 else if(strncasecmp(line, "expect:", 7) == 0)
284 p = colon;
285 n = 0;
286 while(*p == ':' || *p == ' ' || *p == '\t')
287 p++;
288 while(p[n]>=' ')
289 n++;
290 if(strncasecmp(p, "100-continue", 12) == 0) {
291 h->respflags |= FLAG_CONTINUE;
292 syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
295 #ifdef ENABLE_EVENTS
296 else if(strncasecmp(line, "Callback:", 9)==0)
298 /* The Callback can contain several urls :
299 * If there is more than one URL, when the service sends
300 * events, it will try these URLs in order until one
301 * succeeds. One or more URLs each enclosed by angle
302 * brackets ("<" and ">") */
303 p = colon;
304 while(*p != '<' && *p != '\r' )
305 p++;
306 n = 0;
307 while(p[n] != '\r')
308 n++;
309 while(n > 0 && p[n] != '>')
310 n--;
311 /* found last > character */
312 h->req_CallbackOff = p - h->req_buf;
313 h->req_CallbackLen = MAX(0, n + 1);
315 else if(strncasecmp(line, "SID:", 4)==0)
317 p = colon + 1;
318 while((*p == ' ') || (*p == '\t'))
319 p++;
320 n = 0;
321 while(!isspace(p[n]))
322 n++;
323 h->req_SIDOff = p - h->req_buf;
324 h->req_SIDLen = n;
326 /* Timeout: Seconds-nnnn */
327 /* TIMEOUT
328 Recommended. Requested duration until subscription expires,
329 either number of seconds or infinite. Recommendation
330 by a UPnP Forum working committee. Defined by UPnP vendor.
331 Consists of the keyword "Second-" followed (without an
332 intervening space) by either an integer or the keyword "infinite". */
333 else if(strncasecmp(line, "Timeout:", 8)==0)
335 p = colon + 1;
336 while((*p == ' ') || (*p == '\t'))
337 p++;
338 if(strncasecmp(p, "Second-", 7)==0) {
339 h->req_Timeout = atoi(p+7);
342 #ifdef UPNP_STRICT
343 else if(strncasecmp(line, "nt:", 3)==0)
345 p = colon + 1;
346 while((*p == ' ') || (*p == '\t'))
347 p++;
348 n = 0;
349 while(!isspace(p[n]))
350 n++;
351 h->req_NTOff = p - h->req_buf;
352 h->req_NTLen = n;
354 #endif /* UPNP_STRICT */
355 #endif /* ENABLE_EVENTS */
357 /* the loop below won't run off the end of the buffer
358 * because the buffer is guaranteed to contain the \r\n\r\n
359 * character sequence */
360 while(!(line[0] == '\r' && line[1] == '\n'))
361 line++;
362 line += 2;
366 /* very minimalistic 404 error message */
367 static void
368 Send404(struct upnphttp * h)
370 static const char body404[] =
371 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
372 "<BODY><H1>Not Found</H1>The requested URL was not found"
373 " on this server.</BODY></HTML>\r\n";
375 h->respflags = FLAG_HTML;
376 BuildResp2_upnphttp(h, 404, "Not Found",
377 body404, sizeof(body404) - 1);
378 SendRespAndClose_upnphttp(h);
381 static void
382 Send405(struct upnphttp * h)
384 static const char body405[] =
385 "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
386 "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
387 "is not allowed on this resource.</BODY></HTML>\r\n";
389 h->respflags |= FLAG_HTML;
390 BuildResp2_upnphttp(h, 405, "Method Not Allowed",
391 body405, sizeof(body405) - 1);
392 SendRespAndClose_upnphttp(h);
395 /* very minimalistic 501 error message */
396 static void
397 Send501(struct upnphttp * h)
399 static const char body501[] =
400 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
401 "<BODY><H1>Not Implemented</H1>The HTTP Method "
402 "is not implemented by this server.</BODY></HTML>\r\n";
404 h->respflags = FLAG_HTML;
405 BuildResp2_upnphttp(h, 501, "Not Implemented",
406 body501, sizeof(body501) - 1);
407 SendRespAndClose_upnphttp(h);
410 /* findendheaders() find the \r\n\r\n character sequence and
411 * return a pointer to it.
412 * It returns NULL if not found */
413 static const char *
414 findendheaders(const char * s, int len)
416 while(len-->3)
418 if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
419 return s;
420 s++;
422 return NULL;
425 #ifdef HAS_DUMMY_SERVICE
426 static void
427 sendDummyDesc(struct upnphttp * h)
429 static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
430 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
431 " <specVersion>"
432 " <major>1</major>"
433 " <minor>0</minor>"
434 " </specVersion>"
435 " <actionList />"
436 " <serviceStateTable />"
437 "</scpd>\r\n";
438 BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
439 SendRespAndClose_upnphttp(h);
441 #endif
443 /* Sends the description generated by the parameter */
444 static void
445 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
447 char * desc;
448 int len;
449 desc = f(&len);
450 if(!desc)
452 static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
453 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
454 syslog(LOG_ERR, "Failed to generate XML description");
455 h->respflags = FLAG_HTML;
456 BuildResp2_upnphttp(h, 500, "Internal Server Error",
457 error500, sizeof(error500)-1);
459 else
461 BuildResp_upnphttp(h, desc, len);
463 SendRespAndClose_upnphttp(h);
464 free(desc);
467 /* ProcessHTTPPOST_upnphttp()
468 * executes the SOAP query if it is possible */
469 static void
470 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
472 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
474 /* the request body is received */
475 if(h->req_soapActionOff > 0)
477 /* we can process the request */
478 syslog(LOG_INFO, "SOAPAction: %.*s",
479 h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
480 ExecuteSoapAction(h,
481 h->req_buf + h->req_soapActionOff,
482 h->req_soapActionLen);
484 else
486 static const char err400str[] =
487 "<html><body>Bad request</body></html>";
488 syslog(LOG_INFO, "No SOAPAction in HTTP headers");
489 h->respflags = FLAG_HTML;
490 BuildResp2_upnphttp(h, 400, "Bad Request",
491 err400str, sizeof(err400str) - 1);
492 SendRespAndClose_upnphttp(h);
495 else if(h->respflags & FLAG_CONTINUE)
497 /* Sending the 100 Continue response */
498 if(!h->res_buf) {
499 h->res_buf = malloc(256);
500 h->res_buf_alloclen = 256;
502 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
503 "%s 100 Continue\r\n\r\n", h->HttpVer);
504 h->res_sent = 0;
505 h->state = ESendingContinue;
506 if(SendResp_upnphttp(h))
507 h->state = EWaitingForHttpContent;
509 else
511 /* waiting for remaining data */
512 h->state = EWaitingForHttpContent;
516 #ifdef ENABLE_EVENTS
518 * checkCallbackURL()
519 * check that url is on originating IP
520 * extract first correct URL
521 * returns 0 if the callback header value is not valid
522 * 1 if it is valid.
524 static int
525 checkCallbackURL(struct upnphttp * h)
527 char addrstr[48];
528 int ipv6;
529 const char * p;
530 unsigned int i;
532 start_again:
533 if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 10)
534 return 0;
535 if(memcmp(h->req_buf + h->req_CallbackOff, "<http://", 8) != 0) {
536 p = h->req_buf + h->req_CallbackOff + 1;
537 goto invalid;
539 /* extract host from url to addrstr[] */
540 i = 0;
541 p = h->req_buf + h->req_CallbackOff + 8;
542 if(*p == '[') {
543 p++;
544 ipv6 = 1;
545 while(*p != ']' && *p != '>' && i < (sizeof(addrstr)-1)
546 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
547 addrstr[i++] = *(p++);
548 } else {
549 ipv6 = 0;
550 while(*p != '/' && *p != ':' && *p != '>' && i < (sizeof(addrstr)-1)
551 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
552 addrstr[i++] = *(p++);
554 addrstr[i] = '\0';
555 /* check addrstr */
556 if(ipv6) {
557 #ifdef ENABLE_IPV6
558 struct in6_addr addr;
559 if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
560 goto invalid;
561 if(!h->ipv6
562 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
563 goto invalid;
564 #else
565 goto invalid;
566 #endif
567 } else {
568 struct in_addr addr;
569 if(inet_pton(AF_INET, addrstr, &addr) <= 0)
570 goto invalid;
571 #ifdef ENABLE_IPV6
572 if(h->ipv6) {
573 if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
574 goto invalid;
575 if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
576 goto invalid;
577 } else {
578 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
579 goto invalid;
581 #else
582 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
583 goto invalid;
584 #endif
586 /* select only the good callback url */
587 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>')
588 p++;
589 h->req_CallbackOff++; /* skip initial '<' */
590 h->req_CallbackLen = (int)(p - h->req_buf - h->req_CallbackOff);
591 return 1;
592 invalid:
593 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>')
594 p++;
595 if(*p != '>') return 0;
596 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '<')
597 p++;
598 if(*p != '<') return 0;
599 h->req_CallbackLen -= (int)(p - h->req_buf - h->req_CallbackOff);
600 h->req_CallbackOff = (int)(p - h->req_buf);
601 goto start_again;
604 static void
605 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
607 const char * sid;
608 syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
609 syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
610 h->req_CallbackLen, h->req_buf + h->req_CallbackOff,
611 h->req_Timeout);
612 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
613 if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
614 /* Missing or invalid CALLBACK : 412 Precondition Failed.
615 * If CALLBACK header is missing or does not contain a valid HTTP URL,
616 * the publisher must respond with HTTP error 412 Precondition Failed*/
617 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
618 SendRespAndClose_upnphttp(h);
619 } else {
620 /* - add to the subscriber list
621 * - respond HTTP/x.x 200 OK
622 * - Send the initial event message */
623 /* Server:, SID:; Timeout: Second-(xx|infinite) */
624 /* Check that the callback URL is on the same IP as
625 * the request, and not on the internet, nor on ourself (DOS attack ?) */
626 if(h->req_CallbackOff > 0) {
627 #ifdef UPNP_STRICT
628 /* SID: and Callback: are incompatible */
629 if(h->req_SIDOff > 0) {
630 syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
631 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
632 /* "NT: upnp:event" header is mandatory */
633 } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
634 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
635 syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
636 h->req_NTLen, h->req_buf + h->req_NTOff);
637 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
638 } else
639 #endif
640 if(checkCallbackURL(h)) {
641 sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
642 h->req_CallbackLen, h->req_Timeout);
643 h->respflags = FLAG_TIMEOUT;
644 if(sid) {
645 syslog(LOG_DEBUG, "generated sid=%s", sid);
646 h->respflags |= FLAG_SID;
647 h->res_SID = sid;
649 BuildResp_upnphttp(h, 0, 0);
650 } else {
651 syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
652 h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
653 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
655 } else {
656 /* subscription renew */
657 /* Invalid SID
658 412 Precondition Failed. If a SID does not correspond to a known,
659 un-expired subscription, the publisher must respond
660 with HTTP error 412 Precondition Failed. */
661 #ifdef UPNP_STRICT
662 /* SID: and NT: headers are incompatibles */
663 if(h->req_NTOff > 0) {
664 syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
665 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
666 } else
667 #endif
668 if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen,
669 h->req_Timeout) < 0) {
670 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
671 } else {
672 h->respflags = FLAG_TIMEOUT;
673 BuildResp_upnphttp(h, 0, 0);
676 SendRespAndClose_upnphttp(h);
680 static void
681 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
683 syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
684 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
685 /* Remove from the list */
686 #ifdef UPNP_STRICT
687 if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
688 /* SID: header missing or empty */
689 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
690 } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
691 /* no NT: or Callback: header must be present */
692 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
693 } else
694 #endif
695 if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
696 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
697 } else {
698 BuildResp_upnphttp(h, 0, 0);
700 SendRespAndClose_upnphttp(h);
702 #endif
704 /* Parse and process Http Query
705 * called once all the HTTP headers have been received,
706 * so it is guaranteed that h->req_buf contains the \r\n\r\n
707 * character sequence */
708 static void
709 ProcessHttpQuery_upnphttp(struct upnphttp * h)
711 static const struct {
712 const char * path;
713 char * (* f)(int *);
714 } path_desc[] = {
715 { ROOTDESC_PATH, genRootDesc},
716 { WANIPC_PATH, genWANIPCn},
717 { WANCFG_PATH, genWANCfg},
718 #ifdef HAS_DUMMY_SERVICE
719 { DUMMY_PATH, NULL},
720 #endif
721 #ifdef ENABLE_L3F_SERVICE
722 { L3F_PATH, genL3F},
723 #endif
724 #ifdef ENABLE_6FC_SERVICE
725 { WANIP6FC_PATH, gen6FC},
726 #endif
727 #ifdef ENABLE_DP_SERVICE
728 { DP_PATH, genDP},
729 #endif
730 { NULL, NULL}
732 char HttpCommand[16];
733 char HttpUrl[128];
734 char * HttpVer;
735 char * p;
736 int i;
737 p = h->req_buf;
738 if(!p)
739 return;
740 /* note : checking (*p != '\r') is enough to avoid runing off the
741 * end of the buffer, because h->req_buf is guaranteed to contain
742 * the \r\n\r\n character sequence */
743 for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
744 HttpCommand[i] = *(p++);
745 HttpCommand[i] = '\0';
746 while(*p==' ')
747 p++;
748 for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
749 HttpUrl[i] = *(p++);
750 HttpUrl[i] = '\0';
751 while(*p==' ')
752 p++;
753 HttpVer = h->HttpVer;
754 for(i = 0; i<15 && *p != '\r'; i++)
755 HttpVer[i] = *(p++);
756 HttpVer[i] = '\0';
757 syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
758 HttpCommand, HttpUrl, HttpVer);
759 ParseHttpHeaders(h);
760 if(h->req_HostOff > 0 && h->req_HostLen > 0) {
761 syslog(LOG_DEBUG, "Host: %.*s", h->req_HostLen, h->req_buf + h->req_HostOff);
762 p = h->req_buf + h->req_HostOff;
763 if(*p == '[') {
764 /* IPv6 */
765 p++;
766 while(p < h->req_buf + h->req_HostOff + h->req_HostLen) {
767 if(*p == ']') break;
768 /* TODO check *p in [0-9a-f:.] */
769 p++;
771 if(*p != ']') {
772 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
773 Send404(h);/* 403 */
774 return;
776 p++;
777 /* TODO : Check port */
778 } else {
779 for(i = 0; i < h->req_HostLen; i++) {
780 if(*p != ':' && *p != '.' && (*p > '9' || *p < '0')) {
781 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
782 Send404(h);/* 403 */
783 return;
785 p++;
789 if(strcmp("POST", HttpCommand) == 0)
791 h->req_command = EPost;
792 ProcessHTTPPOST_upnphttp(h);
794 else if(strcmp("GET", HttpCommand) == 0)
796 h->req_command = EGet;
797 for(i=0; path_desc[i].path; i++) {
798 if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
799 if(path_desc[i].f)
800 sendXMLdesc(h, path_desc[i].f);
801 else
802 #ifdef HAS_DUMMY_SERVICE
803 sendDummyDesc(h);
804 #else
805 continue;
806 #endif
807 return;
810 if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
811 /* 405 Method Not Allowed
812 * Allow: POST */
813 h->respflags = FLAG_ALLOW_POST;
814 Send405(h);
815 return;
817 #ifdef ENABLE_EVENTS
818 if(0 == memcmp(HttpUrl, "/evt/", 5)) {
819 /* 405 Method Not Allowed
820 * Allow: SUBSCRIBE, UNSUBSCRIBE */
821 h->respflags = FLAG_ALLOW_SUB_UNSUB;
822 Send405(h);
823 return;
825 #endif
826 syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
827 Send404(h);
829 #ifdef ENABLE_EVENTS
830 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
832 h->req_command = ESubscribe;
833 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
835 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
837 h->req_command = EUnSubscribe;
838 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
840 #else
841 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
843 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
844 Send501(h);
846 #endif
847 else
849 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
850 Send501(h);
855 void
856 Process_upnphttp(struct upnphttp * h)
858 char * h_tmp;
859 char buf[2048];
860 int n;
861 if(!h)
862 return;
863 switch(h->state)
865 case EWaitingForHttpRequest:
866 #ifdef ENABLE_HTTPS
867 if(h->ssl) {
868 n = SSL_read(h->ssl, buf, sizeof(buf));
869 } else {
870 n = recv(h->socket, buf, sizeof(buf), 0);
872 #else
873 n = recv(h->socket, buf, sizeof(buf), 0);
874 #endif
875 if(n<0)
877 #ifdef ENABLE_HTTPS
878 if(h->ssl) {
879 int err;
880 err = SSL_get_error(h->ssl, n);
881 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
883 syslog(LOG_ERR, "SSL_read() failed");
884 syslogsslerr();
885 h->state = EToDelete;
887 } else {
888 #endif
889 if(errno != EAGAIN &&
890 errno != EWOULDBLOCK &&
891 errno != EINTR)
893 syslog(LOG_ERR, "recv (state0): %m");
894 h->state = EToDelete;
896 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
897 #ifdef ENABLE_HTTPS
899 #endif
901 else if(n==0)
903 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
904 inet_ntoa(h->clientaddr)); //!!TB - added client address
905 h->state = EToDelete;
907 else
909 const char * endheaders;
910 /* if 1st arg of realloc() is null,
911 * realloc behaves the same as malloc() */
912 h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
913 if (h_tmp == NULL)
915 syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
916 h->state = EToDelete;
918 else
920 h->req_buf = h_tmp;
921 memcpy(h->req_buf + h->req_buflen, buf, n);
922 h->req_buflen += n;
923 h->req_buf[h->req_buflen] = '\0';
925 /* search for the string "\r\n\r\n" */
926 endheaders = findendheaders(h->req_buf, h->req_buflen);
927 if(endheaders)
929 /* at this point, the request buffer (h->req_buf)
930 * is guaranteed to contain the \r\n\r\n character sequence */
931 h->req_contentoff = endheaders - h->req_buf + 4;
932 ProcessHttpQuery_upnphttp(h);
935 break;
936 case EWaitingForHttpContent:
937 #ifdef ENABLE_HTTPS
938 if(h->ssl) {
939 n = SSL_read(h->ssl, buf, sizeof(buf));
940 } else {
941 n = recv(h->socket, buf, sizeof(buf), 0);
943 #else
944 n = recv(h->socket, buf, sizeof(buf), 0);
945 #endif
946 if(n<0)
948 #ifdef ENABLE_HTTPS
949 if(h->ssl) {
950 int err;
951 err = SSL_get_error(h->ssl, n);
952 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
954 syslog(LOG_ERR, "SSL_read() failed");
955 syslogsslerr();
956 h->state = EToDelete;
958 } else {
959 #endif
960 if(errno != EAGAIN &&
961 errno != EWOULDBLOCK &&
962 errno != EINTR)
964 syslog(LOG_ERR, "recv (state1): %m");
965 h->state = EToDelete;
967 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
968 #ifdef ENABLE_HTTPS
970 #endif
972 else if(n==0)
974 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
975 inet_ntoa(h->clientaddr)); //!!TB - added client address
976 h->state = EToDelete;
978 else
980 void * tmp = realloc(h->req_buf, n + h->req_buflen);
981 if(!tmp)
983 syslog(LOG_ERR, "memory allocation error %m");
984 h->state = EToDelete;
986 else
988 h->req_buf = tmp;
989 memcpy(h->req_buf + h->req_buflen, buf, n);
990 h->req_buflen += n;
991 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
993 ProcessHTTPPOST_upnphttp(h);
997 break;
998 case ESendingContinue:
999 if(SendResp_upnphttp(h))
1000 h->state = EWaitingForHttpContent;
1001 break;
1002 case ESendingAndClosing:
1003 SendRespAndClose_upnphttp(h);
1004 break;
1005 default:
1006 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
1010 static const char httpresphead[] =
1011 "%s %d %s\r\n"
1012 "Content-Type: %s\r\n"
1013 "Connection: close\r\n"
1014 "Content-Length: %d\r\n"
1015 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
1016 "Ext:\r\n"
1017 ; /*"\r\n";*/
1019 "<?xml version=\"1.0\"?>\n"
1020 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1021 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1022 "<s:Body>"
1024 "</s:Body>"
1025 "</s:Envelope>";
1027 /* with response code and response message
1028 * also allocate enough memory */
1031 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
1032 const char * respmsg,
1033 int bodylen)
1035 int templen;
1036 if(!h->res_buf ||
1037 h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
1038 if(h->res_buf)
1039 free(h->res_buf);
1040 templen = sizeof(httpresphead) + 256 + bodylen;
1041 h->res_buf = (char *)malloc(templen);
1042 if(!h->res_buf) {
1043 syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
1044 return -1;
1046 h->res_buf_alloclen = templen;
1048 h->res_sent = 0;
1049 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
1050 httpresphead, h->HttpVer,
1051 respcode, respmsg,
1052 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1053 bodylen);
1054 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
1055 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
1056 /* Additional headers */
1057 #ifdef ENABLE_HTTP_DATE
1059 char http_date[64];
1060 time_t t;
1061 struct tm tm;
1062 time(&t);
1063 gmtime_r(&t, &tm);
1064 /* %a and %b depend on locale */
1065 strftime(http_date, sizeof(http_date),
1066 "%a, %d %b %Y %H:%M:%S GMT", &tm);
1067 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1068 h->res_buf_alloclen - h->res_buflen,
1069 "Date: %s\r\n", http_date);
1071 #endif
1072 #ifdef ENABLE_EVENTS
1073 if(h->respflags & FLAG_TIMEOUT) {
1074 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1075 h->res_buf_alloclen - h->res_buflen,
1076 "Timeout: Second-");
1077 if(h->req_Timeout) {
1078 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1079 h->res_buf_alloclen - h->res_buflen,
1080 "%d\r\n", h->req_Timeout);
1081 } else {
1082 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1083 h->res_buf_alloclen - h->res_buflen,
1084 "infinite\r\n");
1087 if(h->respflags & FLAG_SID) {
1088 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1089 h->res_buf_alloclen - h->res_buflen,
1090 "SID: %s\r\n", h->res_SID);
1092 #endif
1093 if(h->respflags & FLAG_ALLOW_POST) {
1094 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1095 h->res_buf_alloclen - h->res_buflen,
1096 "Allow: %s\r\n", "POST");
1097 } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
1098 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1099 h->res_buf_alloclen - h->res_buflen,
1100 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
1102 if(h->accept_language[0] != '\0') {
1103 /* defaulting to "en" */
1104 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1105 h->res_buf_alloclen - h->res_buflen,
1106 "Content-Language: %s\r\n",
1107 h->accept_language[0] == '*' ? "en" : h->accept_language);
1109 h->res_buf[h->res_buflen++] = '\r';
1110 h->res_buf[h->res_buflen++] = '\n';
1111 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
1113 char * tmp;
1114 tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
1115 if(tmp)
1117 h->res_buf = tmp;
1118 h->res_buf_alloclen = h->res_buflen + bodylen;
1120 else
1122 syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
1123 return -1;
1126 return 0;
1129 void
1130 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
1131 const char * respmsg,
1132 const char * body, int bodylen)
1134 int r = BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
1135 if(body && (r >= 0)) {
1136 memcpy(h->res_buf + h->res_buflen, body, bodylen);
1137 h->res_buflen += bodylen;
1141 /* responding 200 OK ! */
1142 void
1143 BuildResp_upnphttp(struct upnphttp * h,
1144 const char * body, int bodylen)
1146 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
1150 SendResp_upnphttp(struct upnphttp * h)
1152 ssize_t n;
1154 while (h->res_sent < h->res_buflen)
1156 #ifdef ENABLE_HTTPS
1157 if(h->ssl) {
1158 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1159 h->res_buflen - h->res_sent);
1160 } else {
1161 n = send(h->socket, h->res_buf + h->res_sent,
1162 h->res_buflen - h->res_sent, 0);
1164 #else
1165 n = send(h->socket, h->res_buf + h->res_sent,
1166 h->res_buflen - h->res_sent, 0);
1167 #endif
1168 if(n<0)
1170 #ifdef ENABLE_HTTPS
1171 if(h->ssl) {
1172 int err;
1173 err = SSL_get_error(h->ssl, n);
1174 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1175 /* try again later */
1176 return 0;
1178 syslog(LOG_ERR, "SSL_write() failed");
1179 syslogsslerr();
1180 break;
1181 } else {
1182 #endif
1183 if(errno == EINTR)
1184 continue; /* try again immediatly */
1185 if(errno == EAGAIN || errno == EWOULDBLOCK)
1187 /* try again later */
1188 return 0;
1190 syslog(LOG_ERR, "send(res_buf): %m");
1191 break; /* avoid infinite loop */
1192 #ifdef ENABLE_HTTPS
1194 #endif
1196 else if(n == 0)
1198 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1199 h->res_sent, h->res_buflen);
1200 break;
1202 else
1204 h->res_sent += n;
1207 return 1; /* finished */
1210 void
1211 SendRespAndClose_upnphttp(struct upnphttp * h)
1213 ssize_t n;
1215 while (h->res_sent < h->res_buflen)
1217 #ifdef ENABLE_HTTPS
1218 if(h->ssl) {
1219 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1220 h->res_buflen - h->res_sent);
1221 } else {
1222 n = send(h->socket, h->res_buf + h->res_sent,
1223 h->res_buflen - h->res_sent, 0);
1225 #else
1226 n = send(h->socket, h->res_buf + h->res_sent,
1227 h->res_buflen - h->res_sent, 0);
1228 #endif
1229 if(n<0)
1231 #ifdef ENABLE_HTTPS
1232 if(h->ssl) {
1233 int err;
1234 err = SSL_get_error(h->ssl, n);
1235 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1236 /* try again later */
1237 h->state = ESendingAndClosing;
1238 return;
1240 syslog(LOG_ERR, "SSL_write() failed");
1241 syslogsslerr();
1242 break; /* avoid infinite loop */
1243 } else {
1244 #endif
1245 if(errno == EINTR)
1246 continue; /* try again immediatly */
1247 if(errno == EAGAIN || errno == EWOULDBLOCK)
1249 /* try again later */
1250 h->state = ESendingAndClosing;
1251 return;
1253 syslog(LOG_ERR, "send(res_buf): %m");
1254 break; /* avoid infinite loop */
1255 #ifdef ENABLE_HTTPS
1257 #endif
1259 else if(n == 0)
1261 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1262 h->res_sent, h->res_buflen);
1263 break;
1265 else
1267 h->res_sent += n;
1270 CloseSocket_upnphttp(h);