1 /* $Id: upnphttp.c,v 1.86 2013/02/07 10:26:07 nanard Exp $ */
3 * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2012 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file included in this distribution.
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"
35 struct upnphttp
* ret
;
38 ret
= (struct upnphttp
*)malloc(sizeof(struct upnphttp
));
41 memset(ret
, 0, sizeof(struct upnphttp
));
43 if(!set_non_blocking(s
))
44 syslog(LOG_WARNING
, "New_upnphttp::set_non_blocking(): %m");
49 CloseSocket_upnphttp(struct upnphttp
* h
)
51 if(close(h
->socket
) < 0)
53 syslog(LOG_ERR
, "CloseSocket_upnphttp: close(%d): %m", h
->socket
);
60 Delete_upnphttp(struct upnphttp
* h
)
65 CloseSocket_upnphttp(h
);
74 /* parse HttpHeaders of the REQUEST
75 * This function is called after the \r\n\r\n character
76 * sequence has been found in h->req_buf */
78 ParseHttpHeaders(struct upnphttp
* h
)
84 if((h
->req_buf
== NULL
) || (h
->req_contentoff
<= 0))
87 while(line
< (h
->req_buf
+ h
->req_contentoff
))
92 if(*colon
== '\r' || *colon
== '\n')
94 colon
= NULL
; /* no ':' character found on the line */
101 if(strncasecmp(line
, "Content-Length", 14)==0)
104 while(*p
< '0' || *p
> '9')
106 h
->req_contentlen
= atoi(p
);
107 if(h
->req_contentlen
< 0) {
108 syslog(LOG_WARNING
, "ParseHttpHeaders() invalid Content-Length %d", h
->req_contentlen
);
109 h
->req_contentlen
= 0;
111 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
112 printf(" readbufflen=%d contentoff = %d\n",
113 h->req_buflen, h->req_contentoff);*/
115 else if(strncasecmp(line
, "SOAPAction", 10)==0)
119 while(*p
== ':' || *p
== ' ' || *p
== '\t')
123 if((p
[0] == '"' && p
[n
-1] == '"')
124 || (p
[0] == '\'' && p
[n
-1] == '\''))
128 h
->req_soapActionOff
= p
- h
->req_buf
;
129 h
->req_soapActionLen
= n
;
131 else if(strncasecmp(line
, "accept-language", 15) == 0)
135 while(*p
== ':' || *p
== ' ' || *p
== '\t')
139 syslog(LOG_DEBUG
, "accept-language HTTP header : '%.*s'", n
, p
);
140 /* keep only the 1st accepted language */
142 while(p
[n
]>' ' && p
[n
] != ',')
144 if(n
>= (int)sizeof(h
->accept_language
))
145 n
= (int)sizeof(h
->accept_language
) - 1;
146 memcpy(h
->accept_language
, p
, n
);
147 h
->accept_language
[n
] = '\0';
149 else if(strncasecmp(line
, "expect", 6) == 0)
153 while(*p
== ':' || *p
== ' ' || *p
== '\t')
157 if(strncasecmp(p
, "100-continue", 12) == 0) {
158 h
->respflags
|= FLAG_CONTINUE
;
159 syslog(LOG_DEBUG
, "\"Expect: 100-Continue\" header detected");
163 else if(strncasecmp(line
, "Callback", 8)==0)
166 while(*p
!= '<' && *p
!= '\r' )
169 while(p
[n
] != '>' && p
[n
] != '\r' )
171 h
->req_CallbackOff
= p
+ 1 - h
->req_buf
;
172 h
->req_CallbackLen
= MAX(0, n
- 1);
174 else if(strncasecmp(line
, "SID", 3)==0)
180 while(!isspace(p
[n
]))
182 h
->req_SIDOff
= p
- h
->req_buf
;
185 /* Timeout: Seconds-nnnn */
187 Recommended. Requested duration until subscription expires,
188 either number of seconds or infinite. Recommendation
189 by a UPnP Forum working committee. Defined by UPnP vendor.
190 Consists of the keyword "Second-" followed (without an
191 intervening space) by either an integer or the keyword "infinite". */
192 else if(strncasecmp(line
, "Timeout", 7)==0)
197 if(strncasecmp(p
, "Second-", 7)==0) {
198 h
->req_Timeout
= atoi(p
+7);
202 else if(strncasecmp(line
, "nt", 2)==0)
208 while(!isspace(p
[n
]))
210 h
->req_NTOff
= p
- h
->req_buf
;
216 /* the loop below won't run off the end of the buffer
217 * because the buffer is guaranteed to contain the \r\n\r\n
218 * character sequence */
219 while(!(line
[0] == '\r' && line
[1] == '\n'))
225 /* very minimalistic 404 error message */
227 Send404(struct upnphttp
* h
)
229 static const char body404
[] =
230 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
231 "<BODY><H1>Not Found</H1>The requested URL was not found"
232 " on this server.</BODY></HTML>\r\n";
234 h
->respflags
= FLAG_HTML
;
235 BuildResp2_upnphttp(h
, 404, "Not Found",
236 body404
, sizeof(body404
) - 1);
237 SendRespAndClose_upnphttp(h
);
241 Send405(struct upnphttp
* h
)
243 static const char body405
[] =
244 "<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
245 "<BODY><H1>Method Not Allowed</H1>The HTTP Method "
246 "is not allowed on this resource.</BODY></HTML>\r\n";
248 h
->respflags
|= FLAG_HTML
;
249 BuildResp2_upnphttp(h
, 405, "Method Not Allowed",
250 body405
, sizeof(body405
) - 1);
251 SendRespAndClose_upnphttp(h
);
254 /* very minimalistic 501 error message */
256 Send501(struct upnphttp
* h
)
258 static const char body501
[] =
259 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
260 "<BODY><H1>Not Implemented</H1>The HTTP Method "
261 "is not implemented by this server.</BODY></HTML>\r\n";
263 h
->respflags
= FLAG_HTML
;
264 BuildResp2_upnphttp(h
, 501, "Not Implemented",
265 body501
, sizeof(body501
) - 1);
266 SendRespAndClose_upnphttp(h
);
269 /* findendheaders() find the \r\n\r\n character sequence and
270 * return a pointer to it.
271 * It returns NULL if not found */
273 findendheaders(const char * s
, int len
)
277 if(s
[0]=='\r' && s
[1]=='\n' && s
[2]=='\r' && s
[3]=='\n')
284 #ifdef HAS_DUMMY_SERVICE
286 sendDummyDesc(struct upnphttp
* h
)
288 static const char xml_desc
[] = "<?xml version=\"1.0\"?>\r\n"
289 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
295 " <serviceStateTable />"
297 BuildResp_upnphttp(h
, xml_desc
, sizeof(xml_desc
)-1);
298 SendRespAndClose_upnphttp(h
);
302 /* Sends the description generated by the parameter */
304 sendXMLdesc(struct upnphttp
* h
, char * (f
)(int *))
311 static const char error500
[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
312 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
313 syslog(LOG_ERR
, "Failed to generate XML description");
314 h
->respflags
= FLAG_HTML
;
315 BuildResp2_upnphttp(h
, 500, "Internal Server Error",
316 error500
, sizeof(error500
)-1);
320 BuildResp_upnphttp(h
, desc
, len
);
322 SendRespAndClose_upnphttp(h
);
326 /* ProcessHTTPPOST_upnphttp()
327 * executes the SOAP query if it is possible */
329 ProcessHTTPPOST_upnphttp(struct upnphttp
* h
)
331 if((h
->req_buflen
- h
->req_contentoff
) >= h
->req_contentlen
)
333 /* the request body is received */
334 if(h
->req_soapActionOff
> 0)
336 /* we can process the request */
337 syslog(LOG_INFO
, "SOAPAction: %.*s",
338 h
->req_soapActionLen
, h
->req_buf
+ h
->req_soapActionOff
);
340 h
->req_buf
+ h
->req_soapActionOff
,
341 h
->req_soapActionLen
);
345 static const char err400str
[] =
346 "<html><body>Bad request</body></html>";
347 syslog(LOG_INFO
, "No SOAPAction in HTTP headers");
348 h
->respflags
= FLAG_HTML
;
349 BuildResp2_upnphttp(h
, 400, "Bad Request",
350 err400str
, sizeof(err400str
) - 1);
351 SendRespAndClose_upnphttp(h
);
354 else if(h
->respflags
& FLAG_CONTINUE
)
356 /* Sending the 100 Continue response */
358 h
->res_buf
= malloc(256);
359 h
->res_buf_alloclen
= 256;
361 h
->res_buflen
= snprintf(h
->res_buf
, h
->res_buf_alloclen
,
362 "%s 100 Continue\r\n\r\n", h
->HttpVer
);
364 h
->state
= ESendingContinue
;
365 if(SendResp_upnphttp(h
))
366 h
->state
= EWaitingForHttpContent
;
370 /* waiting for remaining data */
371 h
->state
= EWaitingForHttpContent
;
377 * returns 0 if the callback header value is not valid
381 checkCallbackURL(struct upnphttp
* h
)
388 if(h
->req_CallbackOff
<= 0 || h
->req_CallbackLen
< 8)
390 if(memcmp(h
->req_buf
+ h
->req_CallbackOff
, "http://", 7) != 0)
394 p
= h
->req_buf
+ h
->req_CallbackOff
+ 7;
398 while(*p
!= ']' && i
< (sizeof(addrstr
)-1)
399 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
400 addrstr
[i
++] = *(p
++);
402 while(*p
!= '/' && *p
!= ':' && i
< (sizeof(addrstr
)-1)
403 && p
< (h
->req_buf
+ h
->req_CallbackOff
+ h
->req_CallbackLen
))
404 addrstr
[i
++] = *(p
++);
408 struct in6_addr addr
;
409 if(inet_pton(AF_INET6
, addrstr
, &addr
) <= 0)
413 || (0!=memcmp(&addr
, &(h
->clientaddr_v6
), sizeof(struct in6_addr
))))
420 if(inet_pton(AF_INET
, addrstr
, &addr
) <= 0)
424 if(!IN6_IS_ADDR_V4MAPPED(&(h
->clientaddr_v6
)))
426 if(0!=memcmp(&addr
, ((const char *)&(h
->clientaddr_v6
) + 12), 4))
429 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
433 if(0!=memcmp(&addr
, &(h
->clientaddr
), sizeof(struct in_addr
)))
441 ProcessHTTPSubscribe_upnphttp(struct upnphttp
* h
, const char * path
)
444 syslog(LOG_DEBUG
, "ProcessHTTPSubscribe %s", path
);
445 syslog(LOG_DEBUG
, "Callback '%.*s' Timeout=%d",
446 h
->req_CallbackLen
, h
->req_buf
+ h
->req_CallbackOff
,
448 syslog(LOG_DEBUG
, "SID '%.*s'", h
->req_SIDLen
, h
->req_buf
+ h
->req_SIDOff
);
449 if((h
->req_CallbackOff
<= 0) && (h
->req_SIDOff
<= 0)) {
450 /* Missing or invalid CALLBACK : 412 Precondition Failed.
451 * If CALLBACK header is missing or does not contain a valid HTTP URL,
452 * the publisher must respond with HTTP error 412 Precondition Failed*/
453 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
454 SendRespAndClose_upnphttp(h
);
456 /* - add to the subscriber list
457 * - respond HTTP/x.x 200 OK
458 * - Send the initial event message */
459 /* Server:, SID:; Timeout: Second-(xx|infinite) */
460 /* Check that the callback URL is on the same IP as
461 * the request, and not on the internet, nor on ourself (DOS attack ?) */
462 if(h
->req_CallbackOff
> 0) {
464 /* SID: and Callback: are incompatible */
465 if(h
->req_SIDOff
> 0) {
466 syslog(LOG_WARNING
, "Both Callback: and SID: in SUBSCRIBE");
467 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
468 /* "NT: upnp:event" header is mandatory */
469 } else if(h
->req_NTOff
<= 0 || h
->req_NTLen
!= 10 ||
470 0 != memcmp("upnp:event", h
->req_buf
+ h
->req_NTOff
, 10)) {
471 syslog(LOG_WARNING
, "Invalid NT in SUBSCRIBE %.*s",
472 h
->req_NTLen
, h
->req_buf
+ h
->req_NTOff
);
473 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
476 if(checkCallbackURL(h
)) {
477 sid
= upnpevents_addSubscriber(path
, h
->req_buf
+ h
->req_CallbackOff
,
478 h
->req_CallbackLen
, h
->req_Timeout
);
479 h
->respflags
= FLAG_TIMEOUT
;
481 syslog(LOG_DEBUG
, "generated sid=%s", sid
);
482 h
->respflags
|= FLAG_SID
;
485 BuildResp_upnphttp(h
, 0, 0);
487 syslog(LOG_WARNING
, "Invalid Callback in SUBSCRIBE %.*s",
488 h
->req_CallbackLen
, h
->req_buf
+ h
->req_CallbackOff
);
489 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
492 /* subscription renew */
494 412 Precondition Failed. If a SID does not correspond to a known,
495 un-expired subscription, the publisher must respond
496 with HTTP error 412 Precondition Failed. */
498 /* SID: and NT: headers are incompatibles */
499 if(h
->req_NTOff
> 0) {
500 syslog(LOG_WARNING
, "Both NT: and SID: in SUBSCRIBE");
501 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
504 if(renewSubscription(h
->req_buf
+ h
->req_SIDOff
, h
->req_SIDLen
,
505 h
->req_Timeout
) < 0) {
506 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
508 h
->respflags
= FLAG_TIMEOUT
;
509 BuildResp_upnphttp(h
, 0, 0);
512 SendRespAndClose_upnphttp(h
);
517 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp
* h
, const char * path
)
519 syslog(LOG_DEBUG
, "ProcessHTTPUnSubscribe %s", path
);
520 syslog(LOG_DEBUG
, "SID '%.*s'", h
->req_SIDLen
, h
->req_buf
+ h
->req_SIDOff
);
521 /* Remove from the list */
523 if(h
->req_SIDOff
<= 0 || h
->req_SIDLen
== 0) {
524 /* SID: header missing or empty */
525 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
526 } else if(h
->req_CallbackOff
> 0 || h
->req_NTOff
> 0) {
527 /* no NT: or Callback: header must be present */
528 BuildResp2_upnphttp(h
, 400, "Incompatible header fields", 0, 0);
531 if(upnpevents_removeSubscriber(h
->req_buf
+ h
->req_SIDOff
, h
->req_SIDLen
) < 0) {
532 BuildResp2_upnphttp(h
, 412, "Precondition Failed", 0, 0);
534 BuildResp_upnphttp(h
, 0, 0);
536 SendRespAndClose_upnphttp(h
);
540 /* Parse and process Http Query
541 * called once all the HTTP headers have been received,
542 * so it is guaranteed that h->req_buf contains the \r\n\r\n
543 * character sequence */
545 ProcessHttpQuery_upnphttp(struct upnphttp
* h
)
547 static const struct {
551 { ROOTDESC_PATH
, genRootDesc
},
552 { WANIPC_PATH
, genWANIPCn
},
553 { WANCFG_PATH
, genWANCfg
},
554 #ifdef HAS_DUMMY_SERVICE
557 #ifdef ENABLE_L3F_SERVICE
560 #ifdef ENABLE_6FC_SERVICE
561 { WANIP6FC_PATH
, gen6FC
},
563 #ifdef ENABLE_DP_SERVICE
568 char HttpCommand
[16];
576 /* note : checking (*p != '\r') is enough to avoid runing off the
577 * end of the buffer, because h->req_buf is guaranteed to contain
578 * the \r\n\r\n character sequence */
579 for(i
= 0; i
<15 && *p
!= ' ' && *p
!= '\r'; i
++)
580 HttpCommand
[i
] = *(p
++);
581 HttpCommand
[i
] = '\0';
584 for(i
= 0; i
<127 && *p
!= ' ' && *p
!= '\r'; i
++)
589 HttpVer
= h
->HttpVer
;
590 for(i
= 0; i
<15 && *p
!= '\r'; i
++)
593 syslog(LOG_INFO
, "HTTP REQUEST : %s %s (%s)",
594 HttpCommand
, HttpUrl
, HttpVer
);
596 if(strcmp("POST", HttpCommand
) == 0)
598 h
->req_command
= EPost
;
599 ProcessHTTPPOST_upnphttp(h
);
601 else if(strcmp("GET", HttpCommand
) == 0)
603 h
->req_command
= EGet
;
604 for(i
=0; path_desc
[i
].path
; i
++) {
605 if(strcasecmp(path_desc
[i
].path
, HttpUrl
) == 0) {
607 sendXMLdesc(h
, path_desc
[i
].f
);
609 #ifdef HAS_DUMMY_SERVICE
617 if(0 == memcmp(HttpUrl
, "/ctl/", 5)) {
618 /* 405 Method Not Allowed
620 h
->respflags
= FLAG_ALLOW_POST
;
625 if(0 == memcmp(HttpUrl
, "/evt/", 5)) {
626 /* 405 Method Not Allowed
627 * Allow: SUBSCRIBE, UNSUBSCRIBE */
628 h
->respflags
= FLAG_ALLOW_SUB_UNSUB
;
633 syslog(LOG_NOTICE
, "%s not found, responding ERROR 404", HttpUrl
);
637 else if(strcmp("SUBSCRIBE", HttpCommand
) == 0)
639 h
->req_command
= ESubscribe
;
640 ProcessHTTPSubscribe_upnphttp(h
, HttpUrl
);
642 else if(strcmp("UNSUBSCRIBE", HttpCommand
) == 0)
644 h
->req_command
= EUnSubscribe
;
645 ProcessHTTPUnSubscribe_upnphttp(h
, HttpUrl
);
648 else if(strcmp("SUBSCRIBE", HttpCommand
) == 0)
650 syslog(LOG_NOTICE
, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
656 syslog(LOG_NOTICE
, "Unsupported HTTP Command %s", HttpCommand
);
663 Process_upnphttp(struct upnphttp
* h
)
672 case EWaitingForHttpRequest
:
673 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
676 if(errno
!= EAGAIN
&&
677 errno
!= EWOULDBLOCK
&&
680 syslog(LOG_ERR
, "recv (state0): %m");
681 h
->state
= EToDelete
;
683 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
687 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
688 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
689 h
->state
= EToDelete
;
693 const char * endheaders
;
694 /* if 1st arg of realloc() is null,
695 * realloc behaves the same as malloc() */
696 h_tmp
= (char *)realloc(h
->req_buf
, n
+ h
->req_buflen
+ 1);
699 syslog(LOG_WARNING
, "Unable to allocate new memory for h->req_buf)");
700 h
->state
= EToDelete
;
705 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, n
);
707 h
->req_buf
[h
->req_buflen
] = '\0';
709 /* search for the string "\r\n\r\n" */
710 endheaders
= findendheaders(h
->req_buf
, h
->req_buflen
);
713 /* at this point, the request buffer (h->req_buf)
714 * is guaranteed to contain the \r\n\r\n character sequence */
715 h
->req_contentoff
= endheaders
- h
->req_buf
+ 4;
716 ProcessHttpQuery_upnphttp(h
);
720 case EWaitingForHttpContent
:
721 n
= recv(h
->socket
, buf
, sizeof(buf
), 0);
724 if(errno
!= EAGAIN
&&
725 errno
!= EWOULDBLOCK
&&
728 syslog(LOG_ERR
, "recv (state1): %m");
729 h
->state
= EToDelete
;
731 /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
735 syslog(LOG_WARNING
, "HTTP Connection from %s closed unexpectedly",
736 inet_ntoa(h
->clientaddr
)); //!!TB - added client address
737 h
->state
= EToDelete
;
741 void * tmp
= realloc(h
->req_buf
, n
+ h
->req_buflen
);
744 syslog(LOG_ERR
, "memory allocation error %m");
745 h
->state
= EToDelete
;
750 memcpy(h
->req_buf
+ h
->req_buflen
, buf
, n
);
752 if((h
->req_buflen
- h
->req_contentoff
) >= h
->req_contentlen
)
754 ProcessHTTPPOST_upnphttp(h
);
759 case ESendingContinue
:
760 if(SendResp_upnphttp(h
))
761 h
->state
= EWaitingForHttpContent
;
763 case ESendingAndClosing
:
764 SendRespAndClose_upnphttp(h
);
767 syslog(LOG_WARNING
, "Unexpected state: %d", h
->state
);
771 static const char httpresphead
[] =
773 "Content-Type: %s\r\n"
774 "Connection: close\r\n"
775 "Content-Length: %d\r\n"
776 "Server: " MINIUPNPD_SERVER_STRING
"\r\n"
780 "<?xml version=\"1.0\"?>\n"
781 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
782 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
788 /* with response code and response message
789 * also allocate enough memory */
792 BuildHeader_upnphttp(struct upnphttp
* h
, int respcode
,
793 const char * respmsg
,
798 h
->res_buf_alloclen
< ((int)sizeof(httpresphead
) + 256 + bodylen
)) {
801 templen
= sizeof(httpresphead
) + 256 + bodylen
;
802 h
->res_buf
= (char *)malloc(templen
);
804 syslog(LOG_ERR
, "malloc error in BuildHeader_upnphttp()");
807 h
->res_buf_alloclen
= templen
;
810 h
->res_buflen
= snprintf(h
->res_buf
, h
->res_buf_alloclen
,
811 httpresphead
, h
->HttpVer
,
813 (h
->respflags
&FLAG_HTML
)?"text/html":"text/xml; charset=\"utf-8\"",
815 /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
816 /* Content-Type MUST be 'text/xml' according to UDA v1.0 */
817 /* Additional headers */
818 #ifdef ENABLE_HTTP_DATE
825 /* %a and %b depend on locale */
826 strftime(http_date
, sizeof(http_date
),
827 "%a, %d %b %Y %H:%M:%S GMT", &tm
);
828 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
829 h
->res_buf_alloclen
- h
->res_buflen
,
830 "Date: %s\r\n", http_date
);
834 if(h
->respflags
& FLAG_TIMEOUT
) {
835 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
836 h
->res_buf_alloclen
- h
->res_buflen
,
839 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
840 h
->res_buf_alloclen
- h
->res_buflen
,
841 "%d\r\n", h
->req_Timeout
);
843 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
844 h
->res_buf_alloclen
- h
->res_buflen
,
848 if(h
->respflags
& FLAG_SID
) {
849 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
850 h
->res_buf_alloclen
- h
->res_buflen
,
851 "SID: %s\r\n", h
->res_SID
);
854 if(h
->respflags
& FLAG_ALLOW_POST
) {
855 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
856 h
->res_buf_alloclen
- h
->res_buflen
,
857 "Allow: %s\r\n", "POST");
858 } else if(h
->respflags
& FLAG_ALLOW_SUB_UNSUB
) {
859 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
860 h
->res_buf_alloclen
- h
->res_buflen
,
861 "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
863 if(h
->accept_language
[0] != '\0') {
864 /* defaulting to "en" */
865 h
->res_buflen
+= snprintf(h
->res_buf
+ h
->res_buflen
,
866 h
->res_buf_alloclen
- h
->res_buflen
,
867 "Content-Language: %s\r\n",
868 h
->accept_language
[0] == '*' ? "en" : h
->accept_language
);
870 h
->res_buf
[h
->res_buflen
++] = '\r';
871 h
->res_buf
[h
->res_buflen
++] = '\n';
872 if(h
->res_buf_alloclen
< (h
->res_buflen
+ bodylen
))
875 tmp
= (char *)realloc(h
->res_buf
, (h
->res_buflen
+ bodylen
));
879 h
->res_buf_alloclen
= h
->res_buflen
+ bodylen
;
883 syslog(LOG_ERR
, "realloc error in BuildHeader_upnphttp()");
889 BuildResp2_upnphttp(struct upnphttp
* h
, int respcode
,
890 const char * respmsg
,
891 const char * body
, int bodylen
)
893 BuildHeader_upnphttp(h
, respcode
, respmsg
, bodylen
);
895 memcpy(h
->res_buf
+ h
->res_buflen
, body
, bodylen
);
896 h
->res_buflen
+= bodylen
;
899 /* responding 200 OK ! */
901 BuildResp_upnphttp(struct upnphttp
* h
,
902 const char * body
, int bodylen
)
904 BuildResp2_upnphttp(h
, 200, "OK", body
, bodylen
);
908 SendResp_upnphttp(struct upnphttp
* h
)
912 while (h
->res_sent
< h
->res_buflen
)
914 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
915 h
->res_buflen
- h
->res_sent
, 0);
919 continue; /* try again immediatly */
920 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
922 /* try again later */
925 syslog(LOG_ERR
, "send(res_buf): %m");
926 break; /* avoid infinite loop */
930 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
931 h
->res_sent
, h
->res_buflen
);
939 return 1; /* finished */
943 SendRespAndClose_upnphttp(struct upnphttp
* h
)
947 while (h
->res_sent
< h
->res_buflen
)
949 n
= send(h
->socket
, h
->res_buf
+ h
->res_sent
,
950 h
->res_buflen
- h
->res_sent
, 0);
954 continue; /* try again immediatly */
955 if(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
957 /* try again later */
958 h
->state
= ESendingAndClosing
;
961 syslog(LOG_ERR
, "send(res_buf): %m");
962 break; /* avoid infinite loop */
966 syslog(LOG_ERR
, "send(res_buf): %d bytes sent (out of %d)",
967 h
->res_sent
, h
->res_buflen
);
975 CloseSocket_upnphttp(h
);