1 /* $Id: upnphttp.c,v 1.99 2014/12/09 17:25:30 nanard Exp $ */
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.
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>
22 #ifdef ENABLE_HTTP_DATE
26 #include "upnpdescgen.h"
27 #include "miniupnpdpath.h"
29 #include "upnpevents.h"
30 #include "upnputils.h"
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"
42 #define HTTPS_KEYFILE "/etc/ssl/private/ssl-cert-snakeoil.key"
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
);
65 SSL_load_error_strings();
66 method
= TLSv1_server_method();
68 syslog(LOG_ERR
, "TLSv1_server_method() failed");
72 ssl_ctx
= SSL_CTX_new(method
);
74 syslog(LOG_ERR
, "SSL_CTX_new() failed");
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
);
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
);
90 /* verify private key */
91 if(!SSL_CTX_check_private_key(ssl_ctx
)) {
92 syslog(LOG_ERR
, "SSL_CTX_check_private_key() failed");
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
));
106 if(ssl_ctx
!= NULL
) {
107 SSL_CTX_free(ssl_ctx
);
112 CONF_modules_unload(1);
115 CRYPTO_cleanup_all_ex_data();
117 #endif /* ENABLE_HTTPS */
122 struct upnphttp
* ret
;
125 ret
= (struct upnphttp
*)malloc(sizeof(struct upnphttp
));
128 memset(ret
, 0, sizeof(struct upnphttp
));
130 if(!set_non_blocking(s
))
131 syslog(LOG_WARNING
, "New_upnphttp::set_non_blocking(): %m");
137 InitSSL_upnphttp(struct upnphttp
* h
)
140 h
->ssl
= SSL_new(ssl_ctx
);
142 syslog(LOG_ERR
, "SSL_new() failed");
146 if(!SSL_set_fd(h
->ssl
, h
->socket
)) {
147 syslog(LOG_ERR
, "SSL_set_fd() failed");
151 r
= SSL_accept(h
->ssl
); /* start the handshaking */
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");
163 #endif /* ENABLE_HTTPS */
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
);
174 h
->state
= EToDelete
;
178 Delete_upnphttp(struct upnphttp
* h
)
187 CloseSocket_upnphttp(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 */
200 ParseHttpHeaders(struct upnphttp
* h
)
206 if((h
->req_buf
== NULL
) || (h
->req_contentoff
<= 0))
209 while(line
< (h
->req_buf
+ h
->req_contentoff
))
214 if(*colon
== '\r' || *colon
== '\n')
216 colon
= NULL
; /* no ':' character found on the line */
223 if(strncasecmp(line
, "Content-Length:", 15)==0)
226 while((*p
< '0' || *p
> '9') && (*p
!= '\r') && (*p
!= '\n'))
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)
241 while(*p
== ':' || *p
== ' ' || *p
== '\t')
245 h
->req_HostOff
= p
- h
->req_buf
;
248 else if(strncasecmp(line
, "SOAPAction:", 11)==0)
252 while(*p
== ':' || *p
== ' ' || *p
== '\t')
256 if((p
[0] == '"' && p
[n
-1] == '"')
257 || (p
[0] == '\'' && p
[n
-1] == '\''))
261 h
->req_soapActionOff
= p
- h
->req_buf
;
262 h
->req_soapActionLen
= n
;
264 else if(strncasecmp(line
, "accept-language:", 16) == 0)
268 while(*p
== ':' || *p
== ' ' || *p
== '\t')
272 syslog(LOG_DEBUG
, "accept-language HTTP header : '%.*s'", n
, p
);
273 /* keep only the 1st accepted language */
275 while(p
[n
]>' ' && p
[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)
286 while(*p
== ':' || *p
== ' ' || *p
== '\t')
290 if(strncasecmp(p
, "100-continue", 12) == 0) {
291 h
->respflags
|= FLAG_CONTINUE
;
292 syslog(LOG_DEBUG
, "\"Expect: 100-Continue\" header detected");
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 ">") */
304 while(*p
!= '<' && *p
!= '\r' )
309 while(n
> 0 && p
[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)
318 while((*p
== ' ') || (*p
== '\t'))
321 while(!isspace(p
[n
]))
323 h
->req_SIDOff
= p
- h
->req_buf
;
326 /* Timeout: Seconds-nnnn */
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)
336 while((*p
== ' ') || (*p
== '\t'))
338 if(strncasecmp(p
, "Second-", 7)==0) {
339 h
->req_Timeout
= atoi(p
+7);
343 else if(strncasecmp(line
, "nt:", 3)==0)
346 while((*p
== ' ') || (*p
== '\t'))
349 while(!isspace(p
[n
]))
351 h
->req_NTOff
= p
- h
->req_buf
;
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'))
366 /* very minimalistic 404 error message */
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
);
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 */
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 */
414 findendheaders(const char * s
, int len
)
418 if(s
[0]=='\r' && s
[1]=='\n' && s
[2]=='\r' && s
[3]=='\n')
425 #ifdef HAS_DUMMY_SERVICE
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\">"
436 " <serviceStateTable />"
438 BuildResp_upnphttp(h
, xml_desc
, sizeof(xml_desc
)-1);
439 SendRespAndClose_upnphttp(h
);
443 /* Sends the description generated by the parameter */
445 sendXMLdesc(struct upnphttp
* h
, char * (f
)(int *))
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);
461 BuildResp_upnphttp(h
, desc
, len
);
463 SendRespAndClose_upnphttp(h
);
467 /* ProcessHTTPPOST_upnphttp()
468 * executes the SOAP query if it is possible */
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
);
481 h
->req_buf
+ h
->req_soapActionOff
,
482 h
->req_soapActionLen
);
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 */
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
);
505 h
->state
= ESendingContinue
;
506 if(SendResp_upnphttp(h
))
507 h
->state
= EWaitingForHttpContent
;
511 /* waiting for remaining data */
512 h
->state
= EWaitingForHttpContent
;
519 * check that url is on originating IP
520 * extract first correct URL
521 * returns 0 if the callback header value is not valid
525 checkCallbackURL(struct upnphttp
* h
)
533 if(h
->req_CallbackOff
<= 0 || h
->req_CallbackLen
< 10)
535 if(memcmp(h
->req_buf
+ h
->req_CallbackOff
, "<http://", 8) != 0) {
536 p
= h
->req_buf
+ h
->req_CallbackOff
+ 1;
539 /* extract host from url to addrstr[] */
541 p
= h
->req_buf
+ h
->req_CallbackOff
+ 8;
545 while(*p
!= ']' && *p
!= '>' && i
< (sizeof(addrstr
)-1)
546 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
547 addrstr
[i
++] = *(p
++);
550 while(*p
!= '/' && *p
!= ':' && *p
!= '>' && i
< (sizeof(addrstr
)-1)
551 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
552 addrstr
[i
++] = *(p
++);
558 struct in6_addr addr
;
559 if(inet_pton(AF_INET6
, addrstr
, &addr
) <= 0)
562 || (0!=memcmp(&addr
, &(h
->clientaddr_v6
), sizeof(struct in6_addr
))))
569 if(inet_pton(AF_INET
, addrstr
, &addr
) <= 0)
573 if(!IN6_IS_ADDR_V4MAPPED(&(h
->clientaddr_v6
)))
575 if(0!=memcmp(&addr
, ((const char *)&(h
->clientaddr_v6
) + 12), 4))
578 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
582 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
586 /* select only the good callback url */
587 while(p
< h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
&& *p
!= '>')
589 h
->req_CallbackOff
++; /* skip initial '<' */
590 h
->req_CallbackLen
= (int)(p
- h
->req_buf
- h
->req_CallbackOff
);
593 while(p
< h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
&& *p
!= '>')
595 if(*p
!= '>') return 0;
596 while(p
< h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
&& *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
);
605 ProcessHTTPSubscribe_upnphttp(struct upnphttp
* h
, const char * path
)
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
,
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
);
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) {
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);
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
;
645 syslog(LOG_DEBUG
, "generated sid=%s", sid
);
646 h
->respflags
|= FLAG_SID
;
649 BuildResp_upnphttp(h
, 0, 0);
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);
656 /* subscription renew */
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. */
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);
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);
672 h
->respflags
= FLAG_TIMEOUT
;
673 BuildResp_upnphttp(h
, 0, 0);
676 SendRespAndClose_upnphttp(h
);
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 */
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);
695 if(upnpevents_removeSubscriber(h
->req_buf
+ h
->req_SIDOff
, h
->req_SIDLen
) < 0) {
696 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
698 BuildResp_upnphttp(h
, 0, 0);
700 SendRespAndClose_upnphttp(h
);
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 */
709 ProcessHttpQuery_upnphttp(struct upnphttp
* h
)
711 static const struct {
715 { ROOTDESC_PATH
, genRootDesc
},
716 { WANIPC_PATH
, genWANIPCn
},
717 { WANCFG_PATH
, genWANCfg
},
718 #ifdef HAS_DUMMY_SERVICE
721 #ifdef ENABLE_L3F_SERVICE
724 #ifdef ENABLE_6FC_SERVICE
725 { WANIP6FC_PATH
, gen6FC
},
727 #ifdef ENABLE_DP_SERVICE
732 char HttpCommand
[16];
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';
748 for(i
= 0; i
<127 && *p
!= ' ' && *p
!= '\r'; i
++)
753 HttpVer
= h
->HttpVer
;
754 for(i
= 0; i
<15 && *p
!= '\r'; i
++)
757 syslog(LOG_INFO
, "HTTP REQUEST : %s %s (%s)",
758 HttpCommand
, HttpUrl
, HttpVer
);
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
;
766 while(p
< h
->req_buf
+ h
->req_HostOff
+ h
->req_HostLen
) {
768 /* TODO check *p in [0-9a-f:.] */
772 syslog(LOG_NOTICE
, "DNS rebinding attack suspected (Host: %.*s)", h
->req_HostLen
, h
->req_buf
+ h
->req_HostOff
);
777 /* TODO : Check port */
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
);
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) {
800 sendXMLdesc(h
, path_desc
[i
].f
);
802 #ifdef HAS_DUMMY_SERVICE
810 if(0 == memcmp(HttpUrl
, "/ctl/", 5)) {
811 /* 405 Method Not Allowed
813 h
->respflags
= FLAG_ALLOW_POST
;
818 if(0 == memcmp(HttpUrl
, "/evt/", 5)) {
819 /* 405 Method Not Allowed
820 * Allow: SUBSCRIBE, UNSUBSCRIBE */
821 h
->respflags
= FLAG_ALLOW_SUB_UNSUB
;
826 syslog(LOG_NOTICE
, "%s not found, responding ERROR 404", HttpUrl
);
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
);
841 else if(strcmp("SUBSCRIBE", HttpCommand
) == 0)
843 syslog(LOG_NOTICE
, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
849 syslog(LOG_NOTICE
, "Unsupported HTTP Command %s", HttpCommand
);
856 Process_upnphttp(struct upnphttp
* h
)
865 case EWaitingForHttpRequest
:
868 n
= SSL_read(h
->ssl
, buf
, sizeof(buf
));
870 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
873 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
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");
885 h
->state
= EToDelete
;
889 if(errno
!= EAGAIN
&&
890 errno
!= EWOULDBLOCK
&&
893 syslog(LOG_ERR
, "recv (state0): %m");
894 h
->state
= EToDelete
;
896 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
903 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
904 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
905 h
->state
= EToDelete
;
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);
915 syslog(LOG_WARNING
, "Unable to allocate new memory for h->req_buf)");
916 h
->state
= EToDelete
;
921 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, 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
);
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
);
936 case EWaitingForHttpContent
:
939 n
= SSL_read(h
->ssl
, buf
, sizeof(buf
));
941 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
944 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
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");
956 h
->state
= EToDelete
;
960 if(errno
!= EAGAIN
&&
961 errno
!= EWOULDBLOCK
&&
964 syslog(LOG_ERR
, "recv (state1): %m");
965 h
->state
= EToDelete
;
967 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
974 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
975 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
976 h
->state
= EToDelete
;
980 void * tmp
= realloc(h
->req_buf
, n
+ h
->req_buflen
);
983 syslog(LOG_ERR
, "memory allocation error %m");
984 h
->state
= EToDelete
;
989 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, n
);
991 if((h
->req_buflen
- h
->req_contentoff
) >= h
->req_contentlen
)
993 ProcessHTTPPOST_upnphttp(h
);
998 case ESendingContinue
:
999 if(SendResp_upnphttp(h
))
1000 h
->state
= EWaitingForHttpContent
;
1002 case ESendingAndClosing
:
1003 SendRespAndClose_upnphttp(h
);
1006 syslog(LOG_WARNING
, "Unexpected state: %d", h
->state
);
1010 static const char httpresphead
[] =
1012 "Content-Type: %s\r\n"
1013 "Connection: close\r\n"
1014 "Content-Length: %d\r\n"
1015 "Server: " MINIUPNPD_SERVER_STRING
"\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/\">"
1027 /* with response code and response message
1028 * also allocate enough memory */
1031 BuildHeader_upnphttp(struct upnphttp
* h
, int respcode
,
1032 const char * respmsg
,
1037 h
->res_buf_alloclen
< ((int)sizeof(httpresphead
) + 256 + bodylen
)) {
1040 templen
= sizeof(httpresphead
) + 256 + bodylen
;
1041 h
->res_buf
= (char *)malloc(templen
);
1043 syslog(LOG_ERR
, "malloc error in BuildHeader_upnphttp()");
1046 h
->res_buf_alloclen
= templen
;
1049 h
->res_buflen
= snprintf(h
->res_buf
, h
->res_buf_alloclen
,
1050 httpresphead
, h
->HttpVer
,
1052 (h
->respflags
&FLAG_HTML
)?"text/html":"text/xml; charset=\"utf-8\"",
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
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
);
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
);
1082 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
1083 h
->res_buf_alloclen
- h
->res_buflen
,
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
);
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
))
1114 tmp
= (char *)realloc(h
->res_buf
, (h
->res_buflen
+ bodylen
));
1118 h
->res_buf_alloclen
= h
->res_buflen
+ bodylen
;
1122 syslog(LOG_ERR
, "realloc error in BuildHeader_upnphttp()");
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 ! */
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
)
1154 while (h
->res_sent
< h
->res_buflen
)
1158 n
= SSL_write(h
->ssl
, h
->res_buf
+ h
->res_sent
,
1159 h
->res_buflen
- h
->res_sent
);
1161 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
1162 h
->res_buflen
- h
->res_sent
, 0);
1165 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
1166 h
->res_buflen
- h
->res_sent
, 0);
1173 err
= SSL_get_error(h
->ssl
, n
);
1174 if(err
== SSL_ERROR_WANT_READ
|| err
== SSL_ERROR_WANT_WRITE
) {
1175 /* try again later */
1178 syslog(LOG_ERR
, "SSL_write() failed");
1184 continue; /* try again immediatly */
1185 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
1187 /* try again later */
1190 syslog(LOG_ERR
, "send(res_buf): %m");
1191 break; /* avoid infinite loop */
1198 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
1199 h
->res_sent
, h
->res_buflen
);
1207 return 1; /* finished */
1211 SendRespAndClose_upnphttp(struct upnphttp
* h
)
1215 while (h
->res_sent
< h
->res_buflen
)
1219 n
= SSL_write(h
->ssl
, h
->res_buf
+ h
->res_sent
,
1220 h
->res_buflen
- h
->res_sent
);
1222 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
1223 h
->res_buflen
- h
->res_sent
, 0);
1226 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
1227 h
->res_buflen
- h
->res_sent
, 0);
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
;
1240 syslog(LOG_ERR
, "SSL_write() failed");
1242 break; /* avoid infinite loop */
1246 continue; /* try again immediatly */
1247 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
1249 /* try again later */
1250 h
->state
= ESendingAndClosing
;
1253 syslog(LOG_ERR
, "send(res_buf): %m");
1254 break; /* avoid infinite loop */
1261 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
1262 h
->res_sent
, h
->res_buflen
);
1270 CloseSocket_upnphttp(h
);