TOR: fix compilation
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blob2a8e452b089deecae7ce16de5990c7ff17dcefab
1 /* $Id: upnphttp.c,v 1.105 2016/02/16 12:15:02 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 defined(UPNP_STRICT) && (UPNP_VERSION_MAJOR > 1) || (UPNP_VERSION_MINOR > 0)
615 /*if(h->req_Timeout < 1800) {*/
616 if(h->req_Timeout == 0) {
617 /* Second-infinite is forbidden with UDA v1.1 and later :
618 * (UDA 1.1 : 4.1.1 Subscription)
619 * UPnP 1.1 control points MUST NOT subscribe using keyword infinite,
620 * UPnP 1.1 devices MUST NOT set actual subscription durations to
621 * "infinite". The presence of infinite in a request MUST be silently
622 * ignored by a UPnP 1.1 device (the presence of infinite is handled
623 * by the device as if the TIMEOUT header field in a request was not
624 * present) . The keyword infinite MUST NOT be returned by a UPnP 1.1
625 * device. */
626 h->req_Timeout = 1800; /* default to 30 minutes */
628 #endif /* UPNP_STRICT */
629 if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
630 /* Missing or invalid CALLBACK : 412 Precondition Failed.
631 * If CALLBACK header is missing or does not contain a valid HTTP URL,
632 * the publisher must respond with HTTP error 412 Precondition Failed*/
633 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
634 SendRespAndClose_upnphttp(h);
635 } else {
636 /* - add to the subscriber list
637 * - respond HTTP/x.x 200 OK
638 * - Send the initial event message */
639 /* Server:, SID:; Timeout: Second-(xx|infinite) */
640 /* Check that the callback URL is on the same IP as
641 * the request, and not on the internet, nor on ourself (DOS attack ?) */
642 if(h->req_CallbackOff > 0) {
643 #ifdef UPNP_STRICT
644 /* SID: and Callback: are incompatible */
645 if(h->req_SIDOff > 0) {
646 syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
647 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
648 /* "NT: upnp:event" header is mandatory */
649 } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
650 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
651 syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
652 h->req_NTLen, h->req_buf + h->req_NTOff);
653 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
654 } else
655 #endif
656 if(checkCallbackURL(h)) {
657 sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
658 h->req_CallbackLen, h->req_Timeout);
659 h->respflags = FLAG_TIMEOUT;
660 if(sid) {
661 syslog(LOG_DEBUG, "generated sid=%s", sid);
662 h->respflags |= FLAG_SID;
663 h->res_SID = sid;
665 BuildResp_upnphttp(h, 0, 0);
666 } else {
667 syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
668 h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
669 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
671 } else {
672 /* subscription renew */
673 /* Invalid SID
674 412 Precondition Failed. If a SID does not correspond to a known,
675 un-expired subscription, the publisher must respond
676 with HTTP error 412 Precondition Failed. */
677 #ifdef UPNP_STRICT
678 /* SID: and NT: headers are incompatibles */
679 if(h->req_NTOff > 0) {
680 syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
681 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
682 } else {
683 #endif /* UPNP_STRICT */
684 sid = upnpevents_renewSubscription(h->req_buf + h->req_SIDOff,
685 h->req_SIDLen, h->req_Timeout);
686 if(!sid) {
687 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
688 } else {
689 h->respflags = FLAG_TIMEOUT | FLAG_SID;
690 h->res_SID = sid;
691 BuildResp_upnphttp(h, 0, 0);
693 #ifdef UPNP_STRICT
695 #endif /* UPNP_STRICT */
697 SendRespAndClose_upnphttp(h);
701 static void
702 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
704 syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
705 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
706 /* Remove from the list */
707 #ifdef UPNP_STRICT
708 if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
709 /* SID: header missing or empty */
710 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
711 } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
712 /* no NT: or Callback: header must be present */
713 BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
714 } else
715 #endif
716 if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
717 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
718 } else {
719 BuildResp_upnphttp(h, 0, 0);
721 SendRespAndClose_upnphttp(h);
723 #endif
725 /* Parse and process Http Query
726 * called once all the HTTP headers have been received,
727 * so it is guaranteed that h->req_buf contains the \r\n\r\n
728 * character sequence */
729 static void
730 ProcessHttpQuery_upnphttp(struct upnphttp * h)
732 static const struct {
733 const char * path;
734 char * (* f)(int *);
735 } path_desc[] = {
736 { ROOTDESC_PATH, genRootDesc},
737 { WANIPC_PATH, genWANIPCn},
738 { WANCFG_PATH, genWANCfg},
739 #ifdef HAS_DUMMY_SERVICE
740 { DUMMY_PATH, NULL},
741 #endif
742 #ifdef ENABLE_L3F_SERVICE
743 { L3F_PATH, genL3F},
744 #endif
745 #ifdef ENABLE_6FC_SERVICE
746 { WANIP6FC_PATH, gen6FC},
747 #endif
748 #ifdef ENABLE_DP_SERVICE
749 { DP_PATH, genDP},
750 #endif
751 { NULL, NULL}
753 char HttpCommand[16];
754 char HttpUrl[128];
755 char * HttpVer;
756 char * p;
757 int i;
759 p = h->req_buf;
760 if(!p)
761 return;
762 /* note : checking (*p != '\r') is enough to avoid runing off the
763 * end of the buffer, because h->req_buf is guaranteed to contain
764 * the \r\n\r\n character sequence */
765 for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
766 HttpCommand[i] = *(p++);
767 HttpCommand[i] = '\0';
768 while(*p==' ')
769 p++;
770 for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
771 HttpUrl[i] = *(p++);
772 HttpUrl[i] = '\0';
773 while(*p==' ')
774 p++;
775 HttpVer = h->HttpVer;
776 for(i = 0; i<15 && *p != '\r'; i++)
777 HttpVer[i] = *(p++);
778 HttpVer[i] = '\0';
779 syslog(LOG_INFO, "HTTP REQUEST from %s : %s %s (%s)",
780 h->clientaddr_str, HttpCommand, HttpUrl, HttpVer);
781 ParseHttpHeaders(h);
782 if(h->req_HostOff > 0 && h->req_HostLen > 0) {
783 syslog(LOG_DEBUG, "Host: %.*s", h->req_HostLen, h->req_buf + h->req_HostOff);
784 p = h->req_buf + h->req_HostOff;
785 if(*p == '[') {
786 /* IPv6 */
787 p++;
788 while(p < h->req_buf + h->req_HostOff + h->req_HostLen) {
789 if(*p == ']') break;
790 /* TODO check *p in [0-9a-f:.] */
791 p++;
793 if(*p != ']') {
794 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
795 Send404(h);/* 403 */
796 return;
798 p++;
799 /* TODO : Check port */
800 } else {
801 for(i = 0; i < h->req_HostLen; i++) {
802 if(*p != ':' && *p != '.' && (*p > '9' || *p < '0')) {
803 syslog(LOG_NOTICE, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_buf + h->req_HostOff);
804 Send404(h);/* 403 */
805 return;
807 p++;
811 if(strcmp("POST", HttpCommand) == 0)
813 h->req_command = EPost;
814 ProcessHTTPPOST_upnphttp(h);
816 else if(strcmp("GET", HttpCommand) == 0)
818 h->req_command = EGet;
819 for(i=0; path_desc[i].path; i++) {
820 if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
821 if(path_desc[i].f)
822 sendXMLdesc(h, path_desc[i].f);
823 else
824 #ifdef HAS_DUMMY_SERVICE
825 sendDummyDesc(h);
826 #else
827 continue;
828 #endif
829 return;
832 if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
833 /* 405 Method Not Allowed
834 * Allow: POST */
835 h->respflags = FLAG_ALLOW_POST;
836 Send405(h);
837 return;
839 #ifdef ENABLE_EVENTS
840 if(0 == memcmp(HttpUrl, "/evt/", 5)) {
841 /* 405 Method Not Allowed
842 * Allow: SUBSCRIBE, UNSUBSCRIBE */
843 h->respflags = FLAG_ALLOW_SUB_UNSUB;
844 Send405(h);
845 return;
847 #endif
848 syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
849 Send404(h);
851 #ifdef ENABLE_EVENTS
852 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
854 h->req_command = ESubscribe;
855 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
857 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
859 h->req_command = EUnSubscribe;
860 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
862 #else
863 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
865 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
866 Send501(h);
868 #endif
869 else
871 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
872 Send501(h);
877 void
878 Process_upnphttp(struct upnphttp * h)
880 char * h_tmp;
881 char buf[2048];
882 int n;
884 if(!h)
885 return;
886 switch(h->state)
888 case EWaitingForHttpRequest:
889 #ifdef ENABLE_HTTPS
890 if(h->ssl) {
891 n = SSL_read(h->ssl, buf, sizeof(buf));
892 } else {
893 n = recv(h->socket, buf, sizeof(buf), 0);
895 #else
896 n = recv(h->socket, buf, sizeof(buf), 0);
897 #endif
898 if(n<0)
900 #ifdef ENABLE_HTTPS
901 if(h->ssl) {
902 int err;
903 err = SSL_get_error(h->ssl, n);
904 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
906 syslog(LOG_ERR, "SSL_read() failed");
907 syslogsslerr();
908 h->state = EToDelete;
910 } else {
911 #endif
912 if(errno != EAGAIN &&
913 errno != EWOULDBLOCK &&
914 errno != EINTR)
916 syslog(LOG_ERR, "recv (state0): %m");
917 h->state = EToDelete;
919 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
920 #ifdef ENABLE_HTTPS
922 #endif
924 else if(n==0)
926 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly", inet_ntoa(h->clientaddr));
927 h->state = EToDelete;
929 else
931 const char * endheaders;
932 /* if 1st arg of realloc() is null,
933 * realloc behaves the same as malloc() */
934 h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
935 if (h_tmp == NULL)
937 syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
938 h->state = EToDelete;
940 else
942 h->req_buf = h_tmp;
943 memcpy(h->req_buf + h->req_buflen, buf, n);
944 h->req_buflen += n;
945 h->req_buf[h->req_buflen] = '\0';
947 /* search for the string "\r\n\r\n" */
948 endheaders = findendheaders(h->req_buf, h->req_buflen);
949 if(endheaders)
951 /* at this point, the request buffer (h->req_buf)
952 * is guaranteed to contain the \r\n\r\n character sequence */
953 h->req_contentoff = endheaders - h->req_buf + 4;
954 ProcessHttpQuery_upnphttp(h);
957 break;
958 case EWaitingForHttpContent:
959 #ifdef ENABLE_HTTPS
960 if(h->ssl) {
961 n = SSL_read(h->ssl, buf, sizeof(buf));
962 } else {
963 n = recv(h->socket, buf, sizeof(buf), 0);
965 #else
966 n = recv(h->socket, buf, sizeof(buf), 0);
967 #endif
968 if(n<0)
970 #ifdef ENABLE_HTTPS
971 if(h->ssl) {
972 int err;
973 err = SSL_get_error(h->ssl, n);
974 if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
976 syslog(LOG_ERR, "SSL_read() failed");
977 syslogsslerr();
978 h->state = EToDelete;
980 } else {
981 #endif
982 if(errno != EAGAIN &&
983 errno != EWOULDBLOCK &&
984 errno != EINTR)
986 syslog(LOG_ERR, "recv (state1): %m");
987 h->state = EToDelete;
989 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
990 #ifdef ENABLE_HTTPS
992 #endif
994 else if(n==0)
996 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly", inet_ntoa(h->clientaddr));
997 h->state = EToDelete;
999 else
1001 void * tmp = realloc(h->req_buf, n + h->req_buflen);
1002 if(!tmp)
1004 syslog(LOG_ERR, "memory allocation error %m");
1005 h->state = EToDelete;
1007 else
1009 h->req_buf = tmp;
1010 memcpy(h->req_buf + h->req_buflen, buf, n);
1011 h->req_buflen += n;
1012 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
1014 ProcessHTTPPOST_upnphttp(h);
1018 break;
1019 case ESendingContinue:
1020 if(SendResp_upnphttp(h))
1021 h->state = EWaitingForHttpContent;
1022 break;
1023 case ESendingAndClosing:
1024 SendRespAndClose_upnphttp(h);
1025 break;
1026 default:
1027 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
1031 static const char httpresphead[] =
1032 "%s %d %s\r\n"
1033 "Content-Type: %s\r\n"
1034 "Connection: close\r\n"
1035 "Content-Length: %d\r\n"
1036 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
1037 "Ext:\r\n"
1038 ; /*"\r\n";*/
1040 "<?xml version=\"1.0\"?>\n"
1041 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1042 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1043 "<s:Body>"
1045 "</s:Body>"
1046 "</s:Envelope>";
1048 /* with response code and response message
1049 * also allocate enough memory */
1052 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
1053 const char * respmsg,
1054 int bodylen)
1056 int templen;
1057 if(!h->res_buf ||
1058 h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
1059 if(h->res_buf)
1060 free(h->res_buf);
1061 templen = sizeof(httpresphead) + 256 + bodylen;
1062 h->res_buf = (char *)malloc(templen);
1063 if(!h->res_buf) {
1064 syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
1065 return -1;
1067 h->res_buf_alloclen = templen;
1069 h->res_sent = 0;
1070 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
1071 httpresphead, h->HttpVer,
1072 respcode, respmsg,
1073 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1074 bodylen);
1075 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
1076 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
1077 /* Additional headers */
1078 #ifdef ENABLE_HTTP_DATE
1080 char http_date[64];
1081 time_t t;
1082 struct tm tm;
1083 time(&t);
1084 gmtime_r(&t, &tm);
1085 /* %a and %b depend on locale */
1086 strftime(http_date, sizeof(http_date),
1087 "%a, %d %b %Y %H:%M:%S GMT", &tm);
1088 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1089 h->res_buf_alloclen - h->res_buflen,
1090 "Date: %s\r\n", http_date);
1092 #endif
1093 #ifdef ENABLE_EVENTS
1094 if(h->respflags & FLAG_TIMEOUT) {
1095 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1096 h->res_buf_alloclen - h->res_buflen,
1097 "Timeout: Second-");
1098 if(h->req_Timeout) {
1099 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1100 h->res_buf_alloclen - h->res_buflen,
1101 "%d\r\n", h->req_Timeout);
1102 } else {
1103 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1104 h->res_buf_alloclen - h->res_buflen,
1105 "infinite\r\n");
1108 if(h->respflags & FLAG_SID) {
1109 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1110 h->res_buf_alloclen - h->res_buflen,
1111 "SID: %s\r\n", h->res_SID);
1113 #endif
1114 if(h->respflags & FLAG_ALLOW_POST) {
1115 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1116 h->res_buf_alloclen - h->res_buflen,
1117 "Allow: %s\r\n", "POST");
1118 } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
1119 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1120 h->res_buf_alloclen - h->res_buflen,
1121 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
1123 if(h->accept_language[0] != '\0') {
1124 /* defaulting to "en" */
1125 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
1126 h->res_buf_alloclen - h->res_buflen,
1127 "Content-Language: %s\r\n",
1128 h->accept_language[0] == '*' ? "en" : h->accept_language);
1130 h->res_buf[h->res_buflen++] = '\r';
1131 h->res_buf[h->res_buflen++] = '\n';
1132 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
1134 char * tmp;
1135 tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
1136 if(tmp)
1138 h->res_buf = tmp;
1139 h->res_buf_alloclen = h->res_buflen + bodylen;
1141 else
1143 syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
1144 return -1;
1147 return 0;
1150 void
1151 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
1152 const char * respmsg,
1153 const char * body, int bodylen)
1155 int r = BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
1156 if(body && (r >= 0)) {
1157 memcpy(h->res_buf + h->res_buflen, body, bodylen);
1158 h->res_buflen += bodylen;
1162 /* responding 200 OK ! */
1163 void
1164 BuildResp_upnphttp(struct upnphttp * h,
1165 const char * body, int bodylen)
1167 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
1171 SendResp_upnphttp(struct upnphttp * h)
1173 ssize_t n;
1175 while (h->res_sent < h->res_buflen)
1177 #ifdef ENABLE_HTTPS
1178 if(h->ssl) {
1179 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1180 h->res_buflen - h->res_sent);
1181 } else {
1182 n = send(h->socket, h->res_buf + h->res_sent,
1183 h->res_buflen - h->res_sent, 0);
1185 #else
1186 n = send(h->socket, h->res_buf + h->res_sent,
1187 h->res_buflen - h->res_sent, 0);
1188 #endif
1189 if(n<0)
1191 #ifdef ENABLE_HTTPS
1192 if(h->ssl) {
1193 int err;
1194 err = SSL_get_error(h->ssl, n);
1195 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1196 /* try again later */
1197 return 0;
1199 syslog(LOG_ERR, "SSL_write() failed");
1200 syslogsslerr();
1201 break;
1202 } else {
1203 #endif
1204 if(errno == EINTR)
1205 continue; /* try again immediatly */
1206 if(errno == EAGAIN || errno == EWOULDBLOCK)
1208 /* try again later */
1209 return 0;
1211 syslog(LOG_ERR, "send(res_buf): %m");
1212 break; /* avoid infinite loop */
1213 #ifdef ENABLE_HTTPS
1215 #endif
1217 else if(n == 0)
1219 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1220 h->res_sent, h->res_buflen);
1221 break;
1223 else
1225 h->res_sent += n;
1228 return 1; /* finished */
1231 void
1232 SendRespAndClose_upnphttp(struct upnphttp * h)
1234 ssize_t n;
1236 while (h->res_sent < h->res_buflen)
1238 #ifdef ENABLE_HTTPS
1239 if(h->ssl) {
1240 n = SSL_write(h->ssl, h->res_buf + h->res_sent,
1241 h->res_buflen - h->res_sent);
1242 } else {
1243 n = send(h->socket, h->res_buf + h->res_sent,
1244 h->res_buflen - h->res_sent, 0);
1246 #else
1247 n = send(h->socket, h->res_buf + h->res_sent,
1248 h->res_buflen - h->res_sent, 0);
1249 #endif
1250 if(n<0)
1252 #ifdef ENABLE_HTTPS
1253 if(h->ssl) {
1254 int err;
1255 err = SSL_get_error(h->ssl, n);
1256 if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
1257 /* try again later */
1258 h->state = ESendingAndClosing;
1259 return;
1261 syslog(LOG_ERR, "SSL_write() failed");
1262 syslogsslerr();
1263 break; /* avoid infinite loop */
1264 } else {
1265 #endif
1266 if(errno == EINTR)
1267 continue; /* try again immediatly */
1268 if(errno == EAGAIN || errno == EWOULDBLOCK)
1270 /* try again later */
1271 h->state = ESendingAndClosing;
1272 return;
1274 syslog(LOG_ERR, "send(res_buf): %m");
1275 break; /* avoid infinite loop */
1276 #ifdef ENABLE_HTTPS
1278 #endif
1280 else if(n == 0)
1282 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
1283 h->res_sent, h->res_buflen);
1284 break;
1286 else
1288 h->res_sent += n;
1291 CloseSocket_upnphttp(h);