miniupnpd 1.9 (20160113)
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blob42f6e4083c1514d3fefce598deed311e87818176
1 /* $Id: upnphttp.c,v 1.103 2015/12/16 10:21:49 nanard Exp $ */
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab */
3 /* Project : miniupnp
4 * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * Author : Thomas Bernard
6 * Copyright (c) 2005-2015 Thomas Bernard
7 * This software is subject to the conditions detailed in the
8 * LICENCE file included in this distribution.
9 * */
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/param.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <syslog.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include "config.h"
23 #ifdef ENABLE_HTTP_DATE
24 #include <time.h>
25 #endif
26 #include "upnphttp.h"
27 #include "upnpdescgen.h"
28 #include "miniupnpdpath.h"
29 #include "upnpsoap.h"
30 #include "upnpevents.h"
31 #include "upnputils.h"
33 #ifdef ENABLE_HTTPS
34 #include <openssl/err.h>
35 #include <openssl/engine.h>
36 #include <openssl/conf.h>
37 static SSL_CTX *ssl_ctx = NULL;
39 #ifndef HTTPS_CERTFILE
40 #define HTTPS_CERTFILE "/etc/ssl/certs/ssl-cert-snakeoil.pem"
41 #endif
42 #ifndef HTTPS_KEYFILE
43 #define HTTPS_KEYFILE "/etc/ssl/private/ssl-cert-snakeoil.key"
44 #endif
46 static void
47 syslogsslerr(void)
49 unsigned long err;
50 char buffer[256];
51 while((err = ERR_get_error()) != 0) {
52 syslog(LOG_ERR, "%s", ERR_error_string(err, buffer));
56 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
58 syslog(LOG_DEBUG, "verify_callback(%d, %p)", preverify_ok, ctx);
59 return preverify_ok;
62 int init_ssl(void)
64 const SSL_METHOD *method;
65 SSL_library_init();
66 SSL_load_error_strings();
67 method = TLSv1_server_method();
68 if(method == NULL) {
69 syslog(LOG_ERR, "TLSv1_server_method() failed");
70 syslogsslerr();
71 return -1;
73 ssl_ctx = SSL_CTX_new(method);
74 if(ssl_ctx == NULL) {
75 syslog(LOG_ERR, "SSL_CTX_new() failed");
76 syslogsslerr();
77 return -1;
79 /* set the local certificate */
80 if(!SSL_CTX_use_certificate_file(ssl_ctx, HTTPS_CERTFILE, SSL_FILETYPE_PEM)) {
81 syslog(LOG_ERR, "SSL_CTX_use_certificate_file(%s) failed", HTTPS_CERTFILE);
82 syslogsslerr();
83 return -1;
85 /* set the private key */
86 if(!SSL_CTX_use_PrivateKey_file(ssl_ctx, HTTPS_KEYFILE, SSL_FILETYPE_PEM)) {
87 syslog(LOG_ERR, "SSL_CTX_use_PrivateKey_file(%s) failed", HTTPS_KEYFILE);
88 syslogsslerr();
89 return -1;
91 /* verify private key */
92 if(!SSL_CTX_check_private_key(ssl_ctx)) {
93 syslog(LOG_ERR, "SSL_CTX_check_private_key() failed");
94 syslogsslerr();
95 return -1;
97 /*SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, verify_callback);*/
98 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, verify_callback);
99 /*SSL_CTX_set_verify_depth(depth);*/
100 syslog(LOG_INFO, "using %s", SSLeay_version(SSLEAY_VERSION));
101 return 0;
104 void free_ssl(void)
106 /* free context */
107 if(ssl_ctx != NULL) {
108 SSL_CTX_free(ssl_ctx);
109 ssl_ctx = NULL;
111 ERR_remove_state(0);
112 ENGINE_cleanup();
113 CONF_modules_unload(1);
114 ERR_free_strings();
115 EVP_cleanup();
116 CRYPTO_cleanup_all_ex_data();
118 #endif /* ENABLE_HTTPS */
120 struct upnphttp *
121 New_upnphttp(int s)
123 struct upnphttp * ret;
124 if(s<0)
125 return NULL;
126 ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
127 if(ret == NULL)
128 return NULL;
129 memset(ret, 0, sizeof(struct upnphttp));
130 ret->socket = s;
131 if(!set_non_blocking(s))
132 syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
133 return ret;
136 #ifdef ENABLE_HTTPS
137 void
138 InitSSL_upnphttp(struct upnphttp * h)
140 int r;
141 h->ssl = SSL_new(ssl_ctx);
142 if(h->ssl == NULL) {
143 syslog(LOG_ERR, "SSL_new() failed");
144 syslogsslerr();
145 abort();
147 if(!SSL_set_fd(h->ssl, h->socket)) {
148 syslog(LOG_ERR, "SSL_set_fd() failed");
149 syslogsslerr();
150 abort();
152 r = SSL_accept(h->ssl); /* start the handshaking */
153 if(r < 0) {
154 int err;
155 err = SSL_get_error(h->ssl, r);
156 syslog(LOG_DEBUG, "SSL_accept() returned %d, SSL_get_error() %d", r, err);
157 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
158 syslog(LOG_ERR, "SSL_accept() failed");
159 syslogsslerr();
160 abort();
164 #endif /* ENABLE_HTTPS */
166 void
167 CloseSocket_upnphttp(struct upnphttp * h)
169 /* SSL_shutdown() ? */
170 if(close(h->socket) < 0)
172 syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
174 h->socket = -1;
175 h->state = EToDelete;
178 void
179 Delete_upnphttp(struct upnphttp * h)
181 if(h)
183 #ifdef ENABLE_HTTPS
184 if(h->ssl)
185 SSL_free(h->ssl);
186 #endif
187 if(h->socket >= 0)
188 CloseSocket_upnphttp(h);
189 if(h->req_buf)
190 free(h->req_buf);
191 if(h->res_buf)
192 free(h->res_buf);
193 free(h);
197 /* parse HttpHeaders of the REQUEST
198 * This function is called after the \r\n\r\n character
199 * sequence has been found in h->req_buf */
200 static void
201 ParseHttpHeaders(struct upnphttp * h)
203 char * line;
204 char * colon;
205 char * p;
206 int n;
207 if((h->req_buf == NULL) || (h->req_contentoff <= 0))
208 return;
209 line = h->req_buf;
210 while(line < (h->req_buf + h->req_contentoff))
212 colon = line;
213 while(*colon != ':')
215 if(*colon == '\r' || *colon == '\n')
217 colon = NULL; /* no ':' character found on the line */
218 break;
220 colon++;
222 if(colon)
224 if(strncasecmp(line, "Content-Length:", 15)==0)
226 p = colon;
227 while((*p < '0' || *p > '9') && (*p != '\r') && (*p != '\n'))
228 p++;
229 h->req_contentlen = atoi(p);
230 if(h->req_contentlen < 0) {
231 syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
232 h->req_contentlen = 0;
234 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
235 printf(" readbufflen=%d contentoff = %d\n",
236 h->req_buflen, h->req_contentoff);*/
238 else if(strncasecmp(line, "Host:", 5)==0)
240 p = colon;
241 n = 0;
242 while(*p == ':' || *p == ' ' || *p == '\t')
243 p++;
244 while(p[n]>' ')
245 n++;
246 h->req_HostOff = p - h->req_buf;
247 h->req_HostLen = n;
249 else if(strncasecmp(line, "SOAPAction:", 11)==0)
251 p = colon;
252 n = 0;
253 while(*p == ':' || *p == ' ' || *p == '\t')
254 p++;
255 while(p[n]>=' ')
256 n++;
257 if((p[0] == '"' && p[n-1] == '"')
258 || (p[0] == '\'' && p[n-1] == '\''))
260 p++; n -= 2;
262 h->req_soapActionOff = p - h->req_buf;
263 h->req_soapActionLen = n;
265 else if(strncasecmp(line, "accept-language:", 16) == 0)
267 p = colon;
268 n = 0;
269 while(*p == ':' || *p == ' ' || *p == '\t')
270 p++;
271 while(p[n]>=' ')
272 n++;
273 syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p);
274 /* keep only the 1st accepted language */
275 n = 0;
276 while(p[n]>' ' && p[n] != ',')
277 n++;
278 if(n >= (int)sizeof(h->accept_language))
279 n = (int)sizeof(h->accept_language) - 1;
280 memcpy(h->accept_language, p, n);
281 h->accept_language[n] = '\0';
283 else if(strncasecmp(line, "expect:", 7) == 0)
285 p = colon;
286 n = 0;
287 while(*p == ':' || *p == ' ' || *p == '\t')
288 p++;
289 while(p[n]>=' ')
290 n++;
291 if(strncasecmp(p, "100-continue", 12) == 0) {
292 h->respflags |= FLAG_CONTINUE;
293 syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
296 #ifdef ENABLE_EVENTS
297 else if(strncasecmp(line, "Callback:", 9)==0)
299 /* The Callback can contain several urls :
300 * If there is more than one URL, when the service sends
301 * events, it will try these URLs in order until one
302 * succeeds. One or more URLs each enclosed by angle
303 * brackets ("<" and ">") */
304 p = colon;
305 while(*p != '<' && *p != '\r' )
306 p++;
307 n = 0;
308 while(p[n] != '\r')
309 n++;
310 while(n > 0 && p[n] != '>')
311 n--;
312 /* found last > character */
313 h->req_CallbackOff = p - h->req_buf;
314 h->req_CallbackLen = MAX(0, n + 1);
316 else if(strncasecmp(line, "SID:", 4)==0)
318 p = colon + 1;
319 while((*p == ' ') || (*p == '\t'))
320 p++;
321 n = 0;
322 while(!isspace(p[n]))
323 n++;
324 h->req_SIDOff = p - h->req_buf;
325 h->req_SIDLen = n;
327 /* Timeout: Seconds-nnnn */
328 /* TIMEOUT
329 Recommended. Requested duration until subscription expires,
330 either number of seconds or infinite. Recommendation
331 by a UPnP Forum working committee. Defined by UPnP vendor.
332 Consists of the keyword "Second-" followed (without an
333 intervening space) by either an integer or the keyword "infinite". */
334 else if(strncasecmp(line, "Timeout:", 8)==0)
336 p = colon + 1;
337 while((*p == ' ') || (*p == '\t'))
338 p++;
339 if(strncasecmp(p, "Second-", 7)==0) {
340 h->req_Timeout = atoi(p+7);
343 #ifdef UPNP_STRICT
344 else if(strncasecmp(line, "nt:", 3)==0)
346 p = colon + 1;
347 while((*p == ' ') || (*p == '\t'))
348 p++;
349 n = 0;
350 while(!isspace(p[n]))
351 n++;
352 h->req_NTOff = p - h->req_buf;
353 h->req_NTLen = n;
355 #endif /* UPNP_STRICT */
356 #endif /* ENABLE_EVENTS */
358 /* the loop below won't run off the end of the buffer
359 * because the buffer is guaranteed to contain the \r\n\r\n
360 * character sequence */
361 while(!(line[0] == '\r' && line[1] == '\n'))
362 line++;
363 line += 2;
367 /* very minimalistic 404 error message */
368 static void
369 Send404(struct upnphttp * h)
371 static const char body404[] =
372 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
373 "<BODY><H1>Not Found</H1>The requested URL was not found"
374 " on this server.</BODY></HTML>\r\n";
376 h->respflags = FLAG_HTML;
377 BuildResp2_upnphttp(h, 404, "Not Found",
378 body404, sizeof(body404) - 1);
379 SendRespAndClose_upnphttp(h);
382 static void
383 Send405(struct upnphttp * h)
385 static const char body405[] =
386 "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
387 "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
388 "is not allowed on this resource.</BODY></HTML>\r\n";
390 h->respflags |= FLAG_HTML;
391 BuildResp2_upnphttp(h, 405, "Method Not Allowed",
392 body405, sizeof(body405) - 1);
393 SendRespAndClose_upnphttp(h);
396 /* very minimalistic 501 error message */
397 static void
398 Send501(struct upnphttp * h)
400 static const char body501[] =
401 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
402 "<BODY><H1>Not Implemented</H1>The HTTP Method "
403 "is not implemented by this server.</BODY></HTML>\r\n";
405 h->respflags = FLAG_HTML;
406 BuildResp2_upnphttp(h, 501, "Not Implemented",
407 body501, sizeof(body501) - 1);
408 SendRespAndClose_upnphttp(h);
411 /* findendheaders() find the \r\n\r\n character sequence and
412 * return a pointer to it.
413 * It returns NULL if not found */
414 static const char *
415 findendheaders(const char * s, int len)
417 while(len-->3)
419 if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
420 return s;
421 s++;
423 return NULL;
426 #ifdef HAS_DUMMY_SERVICE
427 static void
428 sendDummyDesc(struct upnphttp * h)
430 static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
431 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
432 " <specVersion>"
433 " <major>1</major>"
434 " <minor>0</minor>"
435 " </specVersion>"
436 " <actionList />"
437 " <serviceStateTable />"
438 "</scpd>\r\n";
439 BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
440 SendRespAndClose_upnphttp(h);
442 #endif
444 /* Sends the description generated by the parameter */
445 static void
446 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
448 char * desc;
449 int len;
450 desc = f(&len);
451 if(!desc)
453 static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
454 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
455 syslog(LOG_ERR, "Failed to generate XML description");
456 h->respflags = FLAG_HTML;
457 BuildResp2_upnphttp(h, 500, "Internal Server Error",
458 error500, sizeof(error500)-1);
460 else
462 BuildResp_upnphttp(h, desc, len);
464 SendRespAndClose_upnphttp(h);
465 free(desc);
468 /* ProcessHTTPPOST_upnphttp()
469 * executes the SOAP query if it is possible */
470 static void
471 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
473 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
475 /* the request body is received */
476 if(h->req_soapActionOff > 0)
478 /* we can process the request */
479 syslog(LOG_INFO, "SOAPAction: %.*s",
480 h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
481 ExecuteSoapAction(h,
482 h->req_buf + h->req_soapActionOff,
483 h->req_soapActionLen);
485 else
487 static const char err400str[] =
488 "<html><body>Bad request</body></html>";
489 syslog(LOG_INFO, "No SOAPAction in HTTP headers");
490 h->respflags = FLAG_HTML;
491 BuildResp2_upnphttp(h, 400, "Bad Request",
492 err400str, sizeof(err400str) - 1);
493 SendRespAndClose_upnphttp(h);
496 else if(h->respflags & FLAG_CONTINUE)
498 /* Sending the 100 Continue response */
499 if(!h->res_buf) {
500 h->res_buf = malloc(256);
501 h->res_buf_alloclen = 256;
503 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
504 "%s 100 Continue\r\n\r\n", h->HttpVer);
505 h->res_sent = 0;
506 h->state = ESendingContinue;
507 if(SendResp_upnphttp(h))
508 h->state = EWaitingForHttpContent;
510 else
512 /* waiting for remaining data */
513 h->state = EWaitingForHttpContent;
517 #ifdef ENABLE_EVENTS
519 * checkCallbackURL()
520 * check that url is on originating IP
521 * extract first correct URL
522 * returns 0 if the callback header value is not valid
523 * 1 if it is valid.
525 static int
526 checkCallbackURL(struct upnphttp * h)
528 char addrstr[48];
529 int ipv6;
530 const char * p;
531 unsigned int i;
533 start_again:
534 if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 10)
535 return 0;
536 if(memcmp(h->req_buf + h->req_CallbackOff, "<http://", 8) != 0) {
537 p = h->req_buf + h->req_CallbackOff + 1;
538 goto invalid;
540 /* extract host from url to addrstr[] */
541 i = 0;
542 p = h->req_buf + h->req_CallbackOff + 8;
543 if(*p == '[') {
544 p++;
545 ipv6 = 1;
546 while(*p != ']' && *p != '>' && i < (sizeof(addrstr)-1)
547 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
548 addrstr[i++] = *(p++);
549 } else {
550 ipv6 = 0;
551 while(*p != '/' && *p != ':' && *p != '>' && i < (sizeof(addrstr)-1)
552 && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
553 addrstr[i++] = *(p++);
555 addrstr[i] = '\0';
556 /* check addrstr */
557 if(ipv6) {
558 #ifdef ENABLE_IPV6
559 struct in6_addr addr;
560 if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
561 goto invalid;
562 if(!h->ipv6
563 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
564 goto invalid;
565 #else
566 goto invalid;
567 #endif
568 } else {
569 struct in_addr addr;
570 if(inet_pton(AF_INET, addrstr, &addr) <= 0)
571 goto invalid;
572 #ifdef ENABLE_IPV6
573 if(h->ipv6) {
574 if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
575 goto invalid;
576 if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
577 goto invalid;
578 } else {
579 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
580 goto invalid;
582 #else
583 if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
584 goto invalid;
585 #endif
587 /* select only the good callback url */
588 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>')
589 p++;
590 h->req_CallbackOff++; /* skip initial '<' */
591 h->req_CallbackLen = (int)(p - h->req_buf - h->req_CallbackOff);
592 return 1;
593 invalid:
594 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>')
595 p++;
596 if(*p != '>') return 0;
597 while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '<')
598 p++;
599 if(*p != '<') return 0;
600 h->req_CallbackLen -= (int)(p - h->req_buf - h->req_CallbackOff);
601 h->req_CallbackOff = (int)(p - h->req_buf);
602 goto start_again;
605 static void
606 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
608 const char * sid;
609 syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
610 syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
611 h->req_CallbackLen, h->req_buf + h->req_CallbackOff,
612 h->req_Timeout);
613 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
614 if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
615 /* Missing or invalid CALLBACK : 412 Precondition Failed.
616 * If CALLBACK header is missing or does not contain a valid HTTP URL,
617 * the publisher must respond with HTTP error 412 Precondition Failed*/
618 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
619 SendRespAndClose_upnphttp(h);
620 } else {
621 /* - add to the subscriber list
622 * - respond HTTP/x.x 200 OK
623 * - Send the initial event message */
624 /* Server:, SID:; Timeout: Second-(xx|infinite) */
625 /* Check that the callback URL is on the same IP as
626 * the request, and not on the internet, nor on ourself (DOS attack ?) */
627 if(h->req_CallbackOff > 0) {
628 #ifdef UPNP_STRICT
629 /* SID: and Callback: are incompatible */
630 if(h->req_SIDOff > 0) {
631 syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
632 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
633 /* "NT: upnp:event" header is mandatory */
634 } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
635 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
636 syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
637 h->req_NTLen, h->req_buf + h->req_NTOff);
638 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
639 } else
640 #endif
641 if(checkCallbackURL(h)) {
642 sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
643 h->req_CallbackLen, h->req_Timeout);
644 h->respflags = FLAG_TIMEOUT;
645 if(sid) {
646 syslog(LOG_DEBUG, "generated sid=%s", sid);
647 h->respflags |= FLAG_SID;
648 h->res_SID = sid;
650 BuildResp_upnphttp(h, 0, 0);
651 } else {
652 syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
653 h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
654 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
656 } else {
657 /* subscription renew */
658 /* Invalid SID
659 412 Precondition Failed. If a SID does not correspond to a known,
660 un-expired subscription, the publisher must respond
661 with HTTP error 412 Precondition Failed. */
662 #ifdef UPNP_STRICT
663 /* SID: and NT: headers are incompatibles */
664 if(h->req_NTOff > 0) {
665 syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
666 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
667 } else
668 #endif
669 sid = upnpevents_renewSubscription(h->req_buf + h->req_SIDOff,
670 h->req_SIDLen, h->req_Timeout);
671 if(!sid) {
672 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
673 } else {
674 h->respflags = FLAG_TIMEOUT | FLAG_SID;
675 h->res_SID = sid;
676 BuildResp_upnphttp(h, 0, 0);
679 SendRespAndClose_upnphttp(h);
683 static void
684 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
686 syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
687 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
688 /* Remove from the list */
689 #ifdef UPNP_STRICT
690 if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
691 /* SID: header missing or empty */
692 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
693 } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
694 /* no NT: or Callback: header must be present */
695 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
696 } else
697 #endif
698 if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
699 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
700 } else {
701 BuildResp_upnphttp(h, 0, 0);
703 SendRespAndClose_upnphttp(h);
705 #endif
707 /* Parse and process Http Query
708 * called once all the HTTP headers have been received,
709 * so it is guaranteed that h->req_buf contains the \r\n\r\n
710 * character sequence */
711 static void
712 ProcessHttpQuery_upnphttp(struct upnphttp * h)
714 static const struct {
715 const char * path;
716 char * (* f)(int *);
717 } path_desc[] = {
718 { ROOTDESC_PATH, genRootDesc},
719 { WANIPC_PATH, genWANIPCn},
720 { WANCFG_PATH, genWANCfg},
721 #ifdef HAS_DUMMY_SERVICE
722 { DUMMY_PATH, NULL},
723 #endif
724 #ifdef ENABLE_L3F_SERVICE
725 { L3F_PATH, genL3F},
726 #endif
727 #ifdef ENABLE_6FC_SERVICE
728 { WANIP6FC_PATH, gen6FC},
729 #endif
730 #ifdef ENABLE_DP_SERVICE
731 { DP_PATH, genDP},
732 #endif
733 { NULL, NULL}
735 char HttpCommand[16];
736 char HttpUrl[128];
737 char * HttpVer;
738 char * p;
739 int i;
741 p = h->req_buf;
742 if(!p)
743 return;
744 /* note : checking (*p != '\r') is enough to avoid runing off the
745 * end of the buffer, because h->req_buf is guaranteed to contain
746 * the \r\n\r\n character sequence */
747 for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
748 HttpCommand[i] = *(p++);
749 HttpCommand[i] = '\0';
750 while(*p==' ')
751 p++;
752 for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
753 HttpUrl[i] = *(p++);
754 HttpUrl[i] = '\0';
755 while(*p==' ')
756 p++;
757 HttpVer = h->HttpVer;
758 for(i = 0; i<15 && *p != '\r'; i++)
759 HttpVer[i] = *(p++);
760 HttpVer[i] = '\0';
761 syslog(LOG_INFO, "HTTP REQUEST from %s : %s %s (%s)",
762 h->clientaddr_str, HttpCommand, HttpUrl, HttpVer);
763 ParseHttpHeaders(h);
764 if(h->req_HostOff > 0 && h->req_HostLen > 0) {
765 syslog(LOG_DEBUG, "Host: %.*s", h->req_HostLen, h->req_buf + h->req_HostOff);
766 p = h->req_buf + h->req_HostOff;
767 if(*p == '[') {
768 /* IPv6 */
769 p++;
770 while(p < h->req_buf + h->req_HostOff + h->req_HostLen) {
771 if(*p == ']') break;
772 /* TODO check *p in [0-9a-f:.] */
773 p++;
775 if(*p != ']') {
776 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
777 Send404(h);/* 403 */
778 return;
780 p++;
781 /* TODO : Check port */
782 } else {
783 for(i = 0; i < h->req_HostLen; i++) {
784 if(*p != ':' && *p != '.' && (*p > '9' || *p < '0')) {
785 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
786 Send404(h);/* 403 */
787 return;
789 p++;
793 if(strcmp("POST", HttpCommand) == 0)
795 h->req_command = EPost;
796 ProcessHTTPPOST_upnphttp(h);
798 else if(strcmp("GET", HttpCommand) == 0)
800 h->req_command = EGet;
801 for(i=0; path_desc[i].path; i++) {
802 if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
803 if(path_desc[i].f)
804 sendXMLdesc(h, path_desc[i].f);
805 else
806 #ifdef HAS_DUMMY_SERVICE
807 sendDummyDesc(h);
808 #else
809 continue;
810 #endif
811 return;
814 if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
815 /* 405 Method Not Allowed
816 * Allow: POST */
817 h->respflags = FLAG_ALLOW_POST;
818 Send405(h);
819 return;
821 #ifdef ENABLE_EVENTS
822 if(0 == memcmp(HttpUrl, "/evt/", 5)) {
823 /* 405 Method Not Allowed
824 * Allow: SUBSCRIBE, UNSUBSCRIBE */
825 h->respflags = FLAG_ALLOW_SUB_UNSUB;
826 Send405(h);
827 return;
829 #endif
830 syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
831 Send404(h);
833 #ifdef ENABLE_EVENTS
834 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
836 h->req_command = ESubscribe;
837 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
839 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
841 h->req_command = EUnSubscribe;
842 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
844 #else
845 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
847 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
848 Send501(h);
850 #endif
851 else
853 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
854 Send501(h);
859 void
860 Process_upnphttp(struct upnphttp * h)
862 char * h_tmp;
863 char buf[2048];
864 int n;
866 if(!h)
867 return;
868 switch(h->state)
870 case EWaitingForHttpRequest:
871 #ifdef ENABLE_HTTPS
872 if(h->ssl) {
873 n = SSL_read(h->ssl, buf, sizeof(buf));
874 } else {
875 n = recv(h->socket, buf, sizeof(buf), 0);
877 #else
878 n = recv(h->socket, buf, sizeof(buf), 0);
879 #endif
880 if(n<0)
882 #ifdef ENABLE_HTTPS
883 if(h->ssl) {
884 int err;
885 err = SSL_get_error(h->ssl, n);
886 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
888 syslog(LOG_ERR, "SSL_read() failed");
889 syslogsslerr();
890 h->state = EToDelete;
892 } else {
893 #endif
894 if(errno != EAGAIN &&
895 errno != EWOULDBLOCK &&
896 errno != EINTR)
898 syslog(LOG_ERR, "recv (state0): %m");
899 h->state = EToDelete;
901 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
902 #ifdef ENABLE_HTTPS
904 #endif
906 else if(n==0)
908 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly", inet_ntoa(h->clientaddr));
909 h->state = EToDelete;
911 else
913 const char * endheaders;
914 /* if 1st arg of realloc() is null,
915 * realloc behaves the same as malloc() */
916 h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
917 if (h_tmp == NULL)
919 syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
920 h->state = EToDelete;
922 else
924 h->req_buf = h_tmp;
925 memcpy(h->req_buf + h->req_buflen, buf, n);
926 h->req_buflen += n;
927 h->req_buf[h->req_buflen] = '\0';
929 /* search for the string "\r\n\r\n" */
930 endheaders = findendheaders(h->req_buf, h->req_buflen);
931 if(endheaders)
933 /* at this point, the request buffer (h->req_buf)
934 * is guaranteed to contain the \r\n\r\n character sequence */
935 h->req_contentoff = endheaders - h->req_buf + 4;
936 ProcessHttpQuery_upnphttp(h);
939 break;
940 case EWaitingForHttpContent:
941 #ifdef ENABLE_HTTPS
942 if(h->ssl) {
943 n = SSL_read(h->ssl, buf, sizeof(buf));
944 } else {
945 n = recv(h->socket, buf, sizeof(buf), 0);
947 #else
948 n = recv(h->socket, buf, sizeof(buf), 0);
949 #endif
950 if(n<0)
952 #ifdef ENABLE_HTTPS
953 if(h->ssl) {
954 int err;
955 err = SSL_get_error(h->ssl, n);
956 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
958 syslog(LOG_ERR, "SSL_read() failed");
959 syslogsslerr();
960 h->state = EToDelete;
962 } else {
963 #endif
964 if(errno != EAGAIN &&
965 errno != EWOULDBLOCK &&
966 errno != EINTR)
968 syslog(LOG_ERR, "recv (state1): %m");
969 h->state = EToDelete;
971 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
972 #ifdef ENABLE_HTTPS
974 #endif
976 else if(n==0)
978 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly", inet_ntoa(h->clientaddr));
979 h->state = EToDelete;
981 else
983 void * tmp = realloc(h->req_buf, n + h->req_buflen);
984 if(!tmp)
986 syslog(LOG_ERR, "memory allocation error %m");
987 h->state = EToDelete;
989 else
991 h->req_buf = tmp;
992 memcpy(h->req_buf + h->req_buflen, buf, n);
993 h->req_buflen += n;
994 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
996 ProcessHTTPPOST_upnphttp(h);
1000 break;
1001 case ESendingContinue:
1002 if(SendResp_upnphttp(h))
1003 h->state = EWaitingForHttpContent;
1004 break;
1005 case ESendingAndClosing:
1006 SendRespAndClose_upnphttp(h);
1007 break;
1008 default:
1009 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
1013 static const char httpresphead[] =
1014 "%s %d %s\r\n"
1015 "Content-Type: %s\r\n"
1016 "Connection: close\r\n"
1017 "Content-Length: %d\r\n"
1018 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
1019 "Ext:\r\n"
1020 ; /*"\r\n";*/
1022 "<?xml version=\"1.0\"?>\n"
1023 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1024 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1025 "<s:Body>"
1027 "</s:Body>"
1028 "</s:Envelope>";
1030 /* with response code and response message
1031 * also allocate enough memory */
1034 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
1035 const char * respmsg,
1036 int bodylen)
1038 int templen;
1039 if(!h->res_buf ||
1040 h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
1041 if(h->res_buf)
1042 free(h->res_buf);
1043 templen = sizeof(httpresphead) + 256 + bodylen;
1044 h->res_buf = (char *)malloc(templen);
1045 if(!h->res_buf) {
1046 syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
1047 return -1;
1049 h->res_buf_alloclen = templen;
1051 h->res_sent = 0;
1052 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
1053 httpresphead, h->HttpVer,
1054 respcode, respmsg,
1055 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1056 bodylen);
1057 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
1058 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
1059 /* Additional headers */
1060 #ifdef ENABLE_HTTP_DATE
1062 char http_date[64];
1063 time_t t;
1064 struct tm tm;
1065 time(&t);
1066 gmtime_r(&t, &tm);
1067 /* %a and %b depend on locale */
1068 strftime(http_date, sizeof(http_date),
1069 "%a, %d %b %Y %H:%M:%S GMT", &tm);
1070 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1071 h->res_buf_alloclen - h->res_buflen,
1072 "Date: %s\r\n", http_date);
1074 #endif
1075 #ifdef ENABLE_EVENTS
1076 if(h->respflags & FLAG_TIMEOUT) {
1077 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1078 h->res_buf_alloclen - h->res_buflen,
1079 "Timeout: Second-");
1080 if(h->req_Timeout) {
1081 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1082 h->res_buf_alloclen - h->res_buflen,
1083 "%d\r\n", h->req_Timeout);
1084 } else {
1085 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1086 h->res_buf_alloclen - h->res_buflen,
1087 "infinite\r\n");
1090 if(h->respflags & FLAG_SID) {
1091 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1092 h->res_buf_alloclen - h->res_buflen,
1093 "SID: %s\r\n", h->res_SID);
1095 #endif
1096 if(h->respflags & FLAG_ALLOW_POST) {
1097 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1098 h->res_buf_alloclen - h->res_buflen,
1099 "Allow: %s\r\n", "POST");
1100 } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
1101 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1102 h->res_buf_alloclen - h->res_buflen,
1103 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
1105 if(h->accept_language[0] != '\0') {
1106 /* defaulting to "en" */
1107 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1108 h->res_buf_alloclen - h->res_buflen,
1109 "Content-Language: %s\r\n",
1110 h->accept_language[0] == '*' ? "en" : h->accept_language);
1112 h->res_buf[h->res_buflen++] = '\r';
1113 h->res_buf[h->res_buflen++] = '\n';
1114 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
1116 char * tmp;
1117 tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
1118 if(tmp)
1120 h->res_buf = tmp;
1121 h->res_buf_alloclen = h->res_buflen + bodylen;
1123 else
1125 syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
1126 return -1;
1129 return 0;
1132 void
1133 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
1134 const char * respmsg,
1135 const char * body, int bodylen)
1137 int r = BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
1138 if(body && (r >= 0)) {
1139 memcpy(h->res_buf + h->res_buflen, body, bodylen);
1140 h->res_buflen += bodylen;
1144 /* responding 200 OK ! */
1145 void
1146 BuildResp_upnphttp(struct upnphttp * h,
1147 const char * body, int bodylen)
1149 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
1153 SendResp_upnphttp(struct upnphttp * h)
1155 ssize_t n;
1157 while (h->res_sent < h->res_buflen)
1159 #ifdef ENABLE_HTTPS
1160 if(h->ssl) {
1161 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1162 h->res_buflen - h->res_sent);
1163 } else {
1164 n = send(h->socket, h->res_buf + h->res_sent,
1165 h->res_buflen - h->res_sent, 0);
1167 #else
1168 n = send(h->socket, h->res_buf + h->res_sent,
1169 h->res_buflen - h->res_sent, 0);
1170 #endif
1171 if(n<0)
1173 #ifdef ENABLE_HTTPS
1174 if(h->ssl) {
1175 int err;
1176 err = SSL_get_error(h->ssl, n);
1177 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1178 /* try again later */
1179 return 0;
1181 syslog(LOG_ERR, "SSL_write() failed");
1182 syslogsslerr();
1183 break;
1184 } else {
1185 #endif
1186 if(errno == EINTR)
1187 continue; /* try again immediatly */
1188 if(errno == EAGAIN || errno == EWOULDBLOCK)
1190 /* try again later */
1191 return 0;
1193 syslog(LOG_ERR, "send(res_buf): %m");
1194 break; /* avoid infinite loop */
1195 #ifdef ENABLE_HTTPS
1197 #endif
1199 else if(n == 0)
1201 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1202 h->res_sent, h->res_buflen);
1203 break;
1205 else
1207 h->res_sent += n;
1210 return 1; /* finished */
1213 void
1214 SendRespAndClose_upnphttp(struct upnphttp * h)
1216 ssize_t n;
1218 while (h->res_sent < h->res_buflen)
1220 #ifdef ENABLE_HTTPS
1221 if(h->ssl) {
1222 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1223 h->res_buflen - h->res_sent);
1224 } else {
1225 n = send(h->socket, h->res_buf + h->res_sent,
1226 h->res_buflen - h->res_sent, 0);
1228 #else
1229 n = send(h->socket, h->res_buf + h->res_sent,
1230 h->res_buflen - h->res_sent, 0);
1231 #endif
1232 if(n<0)
1234 #ifdef ENABLE_HTTPS
1235 if(h->ssl) {
1236 int err;
1237 err = SSL_get_error(h->ssl, n);
1238 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1239 /* try again later */
1240 h->state = ESendingAndClosing;
1241 return;
1243 syslog(LOG_ERR, "SSL_write() failed");
1244 syslogsslerr();
1245 break; /* avoid infinite loop */
1246 } else {
1247 #endif
1248 if(errno == EINTR)
1249 continue; /* try again immediatly */
1250 if(errno == EAGAIN || errno == EWOULDBLOCK)
1252 /* try again later */
1253 h->state = ESendingAndClosing;
1254 return;
1256 syslog(LOG_ERR, "send(res_buf): %m");
1257 break; /* avoid infinite loop */
1258 #ifdef ENABLE_HTTPS
1260 #endif
1262 else if(n == 0)
1264 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1265 h->res_sent, h->res_buflen);
1266 break;
1268 else
1270 h->res_sent += n;
1273 CloseSocket_upnphttp(h);