version 1.1, 2012/02/21 23:16:02
|
version 1.1.1.3, 2013/07/22 00:32:35
|
Line 2
|
Line 2
|
/* Project : miniupnp |
/* Project : miniupnp |
* Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* Author : Thomas Bernard |
* Author : Thomas Bernard |
* Copyright (c) 2005-2008 Thomas Bernard | * Copyright (c) 2005-2012 Thomas Bernard |
* This software is subject to the conditions detailed in the |
* This software is subject to the conditions detailed in the |
* LICENCE file included in this distribution. |
* LICENCE file included in this distribution. |
* */ |
* */ |
Line 13
|
Line 13
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/param.h> |
#include <sys/param.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
#include <syslog.h> |
#include <syslog.h> |
#include <ctype.h> |
#include <ctype.h> |
|
#include <errno.h> |
#include "config.h" |
#include "config.h" |
|
#ifdef ENABLE_HTTP_DATE |
|
#include <time.h> |
|
#endif |
#include "upnphttp.h" |
#include "upnphttp.h" |
#include "upnpdescgen.h" |
#include "upnpdescgen.h" |
#include "miniupnpdpath.h" |
#include "miniupnpdpath.h" |
#include "upnpsoap.h" |
#include "upnpsoap.h" |
#include "upnpevents.h" |
#include "upnpevents.h" |
|
#include "upnputils.h" |
|
|
struct upnphttp * | struct upnphttp * |
New_upnphttp(int s) |
New_upnphttp(int s) |
{ |
{ |
struct upnphttp * ret; |
struct upnphttp * ret; |
Line 33 New_upnphttp(int s)
|
Line 40 New_upnphttp(int s)
|
return NULL; |
return NULL; |
memset(ret, 0, sizeof(struct upnphttp)); |
memset(ret, 0, sizeof(struct upnphttp)); |
ret->socket = s; |
ret->socket = s; |
|
if(!set_non_blocking(s)) |
|
syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m"); |
return ret; |
return ret; |
} |
} |
|
|
Line 44 CloseSocket_upnphttp(struct upnphttp * h)
|
Line 53 CloseSocket_upnphttp(struct upnphttp * h)
|
syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket); |
syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket); |
} |
} |
h->socket = -1; |
h->socket = -1; |
h->state = 100; | h->state = EToDelete; |
} |
} |
|
|
void |
void |
Line 62 Delete_upnphttp(struct upnphttp * h)
|
Line 71 Delete_upnphttp(struct upnphttp * h)
|
} |
} |
} |
} |
|
|
/* parse HttpHeaders of the REQUEST */ | /* parse HttpHeaders of the REQUEST |
| * This function is called after the \r\n\r\n character |
| * sequence has been found in h->req_buf */ |
static void |
static void |
ParseHttpHeaders(struct upnphttp * h) |
ParseHttpHeaders(struct upnphttp * h) |
{ |
{ |
Line 70 ParseHttpHeaders(struct upnphttp * h)
|
Line 81 ParseHttpHeaders(struct upnphttp * h)
|
char * colon; |
char * colon; |
char * p; |
char * p; |
int n; |
int n; |
|
if((h->req_buf == NULL) || (h->req_contentoff <= 0)) |
|
return; |
line = h->req_buf; |
line = h->req_buf; |
/* TODO : check if req_buf, contentoff are ok */ |
|
while(line < (h->req_buf + h->req_contentoff)) |
while(line < (h->req_buf + h->req_contentoff)) |
{ |
{ |
colon = strchr(line, ':'); | colon = line; |
| while(*colon != ':') |
| { |
| if(*colon == '\r' || *colon == '\n') |
| { |
| colon = NULL; /* no ':' character found on the line */ |
| break; |
| } |
| colon++; |
| } |
if(colon) |
if(colon) |
{ |
{ |
if(strncasecmp(line, "Content-Length", 14)==0) |
if(strncasecmp(line, "Content-Length", 14)==0) |
Line 83 ParseHttpHeaders(struct upnphttp * h)
|
Line 104 ParseHttpHeaders(struct upnphttp * h)
|
while(*p < '0' || *p > '9') |
while(*p < '0' || *p > '9') |
p++; |
p++; |
h->req_contentlen = atoi(p); |
h->req_contentlen = atoi(p); |
|
if(h->req_contentlen < 0) { |
|
syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen); |
|
h->req_contentlen = 0; |
|
} |
/*printf("*** Content-Lenght = %d ***\n", h->req_contentlen); |
/*printf("*** Content-Lenght = %d ***\n", h->req_contentlen); |
printf(" readbufflen=%d contentoff = %d\n", |
printf(" readbufflen=%d contentoff = %d\n", |
h->req_buflen, h->req_contentoff);*/ |
h->req_buflen, h->req_contentoff);*/ |
Line 94 ParseHttpHeaders(struct upnphttp * h)
|
Line 119 ParseHttpHeaders(struct upnphttp * h)
|
while(*p == ':' || *p == ' ' || *p == '\t') |
while(*p == ':' || *p == ' ' || *p == '\t') |
p++; |
p++; |
while(p[n]>=' ') |
while(p[n]>=' ') |
{ |
|
n++; |
n++; |
} |
|
if((p[0] == '"' && p[n-1] == '"') |
if((p[0] == '"' && p[n-1] == '"') |
|| (p[0] == '\'' && p[n-1] == '\'')) |
|| (p[0] == '\'' && p[n-1] == '\'')) |
{ |
{ |
p++; n -= 2; |
p++; n -= 2; |
} |
} |
h->req_soapAction = p; | h->req_soapActionOff = p - h->req_buf; |
h->req_soapActionLen = n; |
h->req_soapActionLen = n; |
} |
} |
|
else if(strncasecmp(line, "accept-language", 15) == 0) |
|
{ |
|
p = colon; |
|
n = 0; |
|
while(*p == ':' || *p == ' ' || *p == '\t') |
|
p++; |
|
while(p[n]>=' ') |
|
n++; |
|
syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p); |
|
/* keep only the 1st accepted language */ |
|
n = 0; |
|
while(p[n]>' ' && p[n] != ',') |
|
n++; |
|
if(n >= (int)sizeof(h->accept_language)) |
|
n = (int)sizeof(h->accept_language) - 1; |
|
memcpy(h->accept_language, p, n); |
|
h->accept_language[n] = '\0'; |
|
} |
|
else if(strncasecmp(line, "expect", 6) == 0) |
|
{ |
|
p = colon; |
|
n = 0; |
|
while(*p == ':' || *p == ' ' || *p == '\t') |
|
p++; |
|
while(p[n]>=' ') |
|
n++; |
|
if(strncasecmp(p, "100-continue", 12) == 0) { |
|
h->respflags |= FLAG_CONTINUE; |
|
syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected"); |
|
} |
|
} |
#ifdef ENABLE_EVENTS |
#ifdef ENABLE_EVENTS |
else if(strncasecmp(line, "Callback", 8)==0) |
else if(strncasecmp(line, "Callback", 8)==0) |
{ |
{ |
Line 114 ParseHttpHeaders(struct upnphttp * h)
|
Line 168 ParseHttpHeaders(struct upnphttp * h)
|
n = 0; |
n = 0; |
while(p[n] != '>' && p[n] != '\r' ) |
while(p[n] != '>' && p[n] != '\r' ) |
n++; |
n++; |
h->req_Callback = p + 1; | h->req_CallbackOff = p + 1 - h->req_buf; |
h->req_CallbackLen = MAX(0, n - 1); |
h->req_CallbackLen = MAX(0, n - 1); |
} |
} |
else if(strncasecmp(line, "SID", 3)==0) |
else if(strncasecmp(line, "SID", 3)==0) |
Line 125 ParseHttpHeaders(struct upnphttp * h)
|
Line 179 ParseHttpHeaders(struct upnphttp * h)
|
n = 0; |
n = 0; |
while(!isspace(p[n])) |
while(!isspace(p[n])) |
n++; |
n++; |
h->req_SID = p; | h->req_SIDOff = p - h->req_buf; |
h->req_SIDLen = n; |
h->req_SIDLen = n; |
} |
} |
/* Timeout: Seconds-nnnn */ |
/* Timeout: Seconds-nnnn */ |
Line 144 intervening space) by either an integer or the keyword
|
Line 198 intervening space) by either an integer or the keyword
|
h->req_Timeout = atoi(p+7); |
h->req_Timeout = atoi(p+7); |
} |
} |
} |
} |
|
#ifdef UPNP_STRICT |
|
else if(strncasecmp(line, "nt", 2)==0) |
|
{ |
|
p = colon + 1; |
|
while(isspace(*p)) |
|
p++; |
|
n = 0; |
|
while(!isspace(p[n])) |
|
n++; |
|
h->req_NTOff = p - h->req_buf; |
|
h->req_NTLen = n; |
|
} |
#endif |
#endif |
|
#endif |
} |
} |
|
/* the loop below won't run off the end of the buffer |
|
* because the buffer is guaranteed to contain the \r\n\r\n |
|
* character sequence */ |
while(!(line[0] == '\r' && line[1] == '\n')) |
while(!(line[0] == '\r' && line[1] == '\n')) |
line++; |
line++; |
line += 2; |
line += 2; |
Line 156 intervening space) by either an integer or the keyword
|
Line 226 intervening space) by either an integer or the keyword
|
static void |
static void |
Send404(struct upnphttp * h) |
Send404(struct upnphttp * h) |
{ |
{ |
/* |
|
static const char error404[] = "HTTP/1.1 404 Not found\r\n" |
|
"Connection: close\r\n" |
|
"Content-type: text/html\r\n" |
|
"\r\n" |
|
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" |
|
"<BODY><H1>Not Found</H1>The requested URL was not found" |
|
" on this server.</BODY></HTML>\r\n"; |
|
int n; |
|
n = send(h->socket, error404, sizeof(error404) - 1, 0); |
|
if(n < 0) |
|
{ |
|
syslog(LOG_ERR, "Send404: send(http): %m"); |
|
}*/ |
|
static const char body404[] = |
static const char body404[] = |
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" |
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" |
"<BODY><H1>Not Found</H1>The requested URL was not found" |
"<BODY><H1>Not Found</H1>The requested URL was not found" |
" on this server.</BODY></HTML>\r\n"; |
" on this server.</BODY></HTML>\r\n"; |
|
|
h->respflags = FLAG_HTML; |
h->respflags = FLAG_HTML; |
BuildResp2_upnphttp(h, 404, "Not Found", |
BuildResp2_upnphttp(h, 404, "Not Found", |
body404, sizeof(body404) - 1); |
body404, sizeof(body404) - 1); |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
|
|
|
static void |
|
Send405(struct upnphttp * h) |
|
{ |
|
static const char body405[] = |
|
"<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>" |
|
"<BODY><H1>Method Not Allowed</H1>The HTTP Method " |
|
"is not allowed on this resource.</BODY></HTML>\r\n"; |
|
|
|
h->respflags |= FLAG_HTML; |
|
BuildResp2_upnphttp(h, 405, "Method Not Allowed", |
|
body405, sizeof(body405) - 1); |
|
SendRespAndClose_upnphttp(h); |
|
} |
|
|
/* very minimalistic 501 error message */ |
/* very minimalistic 501 error message */ |
static void |
static void |
Send501(struct upnphttp * h) |
Send501(struct upnphttp * h) |
{ |
{ |
/* | static const char body501[] = |
static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n" | |
"Connection: close\r\n" | |
"Content-type: text/html\r\n" | |
"\r\n" | |
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>" |
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>" |
"<BODY><H1>Not Implemented</H1>The HTTP Method " |
"<BODY><H1>Not Implemented</H1>The HTTP Method " |
"is not implemented by this server.</BODY></HTML>\r\n"; |
"is not implemented by this server.</BODY></HTML>\r\n"; |
int n; | |
n = send(h->socket, error501, sizeof(error501) - 1, 0); | |
if(n < 0) | |
{ | |
syslog(LOG_ERR, "Send501: send(http): %m"); | |
} | |
*/ | |
static const char body501[] = | |
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>" | |
"<BODY><H1>Not Implemented</H1>The HTTP Method " | |
"is not implemented by this server.</BODY></HTML>\r\n"; | |
h->respflags = FLAG_HTML; |
h->respflags = FLAG_HTML; |
BuildResp2_upnphttp(h, 501, "Not Implemented", |
BuildResp2_upnphttp(h, 501, "Not Implemented", |
body501, sizeof(body501) - 1); |
body501, sizeof(body501) - 1); |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
|
|
|
/* findendheaders() find the \r\n\r\n character sequence and |
|
* return a pointer to it. |
|
* It returns NULL if not found */ |
static const char * |
static const char * |
findendheaders(const char * s, int len) |
findendheaders(const char * s, int len) |
{ |
{ |
while(len-->0) | while(len-->3) |
{ |
{ |
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n') |
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n') |
return s; |
return s; |
Line 237 sendDummyDesc(struct upnphttp * h)
|
Line 295 sendDummyDesc(struct upnphttp * h)
|
" <serviceStateTable />" |
" <serviceStateTable />" |
"</scpd>\r\n"; |
"</scpd>\r\n"; |
BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1); |
BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1); |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
#endif |
#endif |
|
|
Line 262 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
Line 319 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
{ |
{ |
BuildResp_upnphttp(h, desc, len); |
BuildResp_upnphttp(h, desc, len); |
} |
} |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
free(desc); |
free(desc); |
} |
} |
|
|
Line 274 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
|
Line 330 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
|
{ |
{ |
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) |
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) |
{ |
{ |
if(h->req_soapAction) | /* the request body is received */ |
| if(h->req_soapActionOff > 0) |
{ |
{ |
/* we can process the request */ |
/* we can process the request */ |
syslog(LOG_INFO, "SOAPAction: %.*s", |
syslog(LOG_INFO, "SOAPAction: %.*s", |
h->req_soapActionLen, h->req_soapAction); | h->req_soapActionLen, h->req_buf + h->req_soapActionOff); |
ExecuteSoapAction(h, | ExecuteSoapAction(h, |
h->req_soapAction, | h->req_buf + h->req_soapActionOff, |
h->req_soapActionLen); |
h->req_soapActionLen); |
} |
} |
else |
else |
Line 291 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
|
Line 348 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
|
h->respflags = FLAG_HTML; |
h->respflags = FLAG_HTML; |
BuildResp2_upnphttp(h, 400, "Bad Request", |
BuildResp2_upnphttp(h, 400, "Bad Request", |
err400str, sizeof(err400str) - 1); |
err400str, sizeof(err400str) - 1); |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
} |
} |
|
else if(h->respflags & FLAG_CONTINUE) |
|
{ |
|
/* Sending the 100 Continue response */ |
|
if(!h->res_buf) { |
|
h->res_buf = malloc(256); |
|
h->res_buf_alloclen = 256; |
|
} |
|
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, |
|
"%s 100 Continue\r\n\r\n", h->HttpVer); |
|
h->res_sent = 0; |
|
h->state = ESendingContinue; |
|
if(SendResp_upnphttp(h)) |
|
h->state = EWaitingForHttpContent; |
|
} |
else |
else |
{ |
{ |
/* waiting for remaining data */ |
/* waiting for remaining data */ |
h->state = 1; | h->state = EWaitingForHttpContent; |
} |
} |
} |
} |
|
|
#ifdef ENABLE_EVENTS |
#ifdef ENABLE_EVENTS |
|
/** |
|
* returns 0 if the callback header value is not valid |
|
* 1 if it is valid. |
|
*/ |
|
static int |
|
checkCallbackURL(struct upnphttp * h) |
|
{ |
|
char addrstr[48]; |
|
int ipv6; |
|
const char * p; |
|
unsigned int i; |
|
|
|
if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8) |
|
return 0; |
|
if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0) |
|
return 0; |
|
ipv6 = 0; |
|
i = 0; |
|
p = h->req_buf + h->req_CallbackOff + 7; |
|
if(*p == '[') { |
|
p++; |
|
ipv6 = 1; |
|
while(*p != ']' && i < (sizeof(addrstr)-1) |
|
&& p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) |
|
addrstr[i++] = *(p++); |
|
} else { |
|
while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1) |
|
&& p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) |
|
addrstr[i++] = *(p++); |
|
} |
|
addrstr[i] = '\0'; |
|
if(ipv6) { |
|
struct in6_addr addr; |
|
if(inet_pton(AF_INET6, addrstr, &addr) <= 0) |
|
return 0; |
|
#ifdef ENABLE_IPV6 |
|
if(!h->ipv6 |
|
|| (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr)))) |
|
return 0; |
|
#else |
|
return 0; |
|
#endif |
|
} else { |
|
struct in_addr addr; |
|
if(inet_pton(AF_INET, addrstr, &addr) <= 0) |
|
return 0; |
|
#ifdef ENABLE_IPV6 |
|
if(h->ipv6) { |
|
if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6))) |
|
return 0; |
|
if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4)) |
|
return 0; |
|
} else { |
|
if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) |
|
return 0; |
|
} |
|
#else |
|
if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) |
|
return 0; |
|
#endif |
|
} |
|
return 1; |
|
} |
|
|
static void |
static void |
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path) |
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path) |
{ |
{ |
const char * sid; |
const char * sid; |
syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path); |
syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path); |
syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d", |
syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d", |
h->req_CallbackLen, h->req_Callback, h->req_Timeout); | h->req_CallbackLen, h->req_buf + h->req_CallbackOff, |
syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID); | h->req_Timeout); |
if(!h->req_Callback && !h->req_SID) { | syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff); |
| if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) { |
/* Missing or invalid CALLBACK : 412 Precondition Failed. |
/* Missing or invalid CALLBACK : 412 Precondition Failed. |
* If CALLBACK header is missing or does not contain a valid HTTP URL, |
* If CALLBACK header is missing or does not contain a valid HTTP URL, |
* the publisher must respond with HTTP error 412 Precondition Failed*/ |
* the publisher must respond with HTTP error 412 Precondition Failed*/ |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} else { |
} else { |
/* - add to the subscriber list |
/* - add to the subscriber list |
* - respond HTTP/x.x 200 OK | * - respond HTTP/x.x 200 OK |
* - Send the initial event message */ |
* - Send the initial event message */ |
/* Server:, SID:; Timeout: Second-(xx|infinite) */ |
/* Server:, SID:; Timeout: Second-(xx|infinite) */ |
if(h->req_Callback) { | /* Check that the callback URL is on the same IP as |
sid = upnpevents_addSubscriber(path, h->req_Callback, | * the request, and not on the internet, nor on ourself (DOS attack ?) */ |
h->req_CallbackLen, h->req_Timeout); | if(h->req_CallbackOff > 0) { |
h->respflags = FLAG_TIMEOUT; | #ifdef UPNP_STRICT |
if(sid) { | /* SID: and Callback: are incompatible */ |
syslog(LOG_DEBUG, "generated sid=%s", sid); | if(h->req_SIDOff > 0) { |
h->respflags |= FLAG_SID; | syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE"); |
h->req_SID = sid; | BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); |
h->req_SIDLen = strlen(sid); | /* "NT: upnp:event" header is mandatory */ |
| } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 || |
| 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) { |
| syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s", |
| h->req_NTLen, h->req_buf + h->req_NTOff); |
| BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
| } else |
| #endif |
| if(checkCallbackURL(h)) { |
| sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff, |
| h->req_CallbackLen, h->req_Timeout); |
| h->respflags = FLAG_TIMEOUT; |
| if(sid) { |
| syslog(LOG_DEBUG, "generated sid=%s", sid); |
| h->respflags |= FLAG_SID; |
| h->res_SID = sid; |
| } |
| BuildResp_upnphttp(h, 0, 0); |
| } else { |
| syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s", |
| h->req_CallbackLen, h->req_buf + h->req_CallbackOff); |
| BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
} |
} |
BuildResp_upnphttp(h, 0, 0); |
|
} else { |
} else { |
/* subscription renew */ |
/* subscription renew */ |
/* Invalid SID |
/* Invalid SID |
412 Precondition Failed. If a SID does not correspond to a known, |
412 Precondition Failed. If a SID does not correspond to a known, |
un-expired subscription, the publisher must respond |
un-expired subscription, the publisher must respond |
with HTTP error 412 Precondition Failed. */ |
with HTTP error 412 Precondition Failed. */ |
if(renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0) { | #ifdef UPNP_STRICT |
| /* SID: and NT: headers are incompatibles */ |
| if(h->req_NTOff > 0) { |
| syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE"); |
| BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); |
| } else |
| #endif |
| if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen, |
| h->req_Timeout) < 0) { |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
} else { |
} else { |
|
h->respflags = FLAG_TIMEOUT; |
BuildResp_upnphttp(h, 0, 0); |
BuildResp_upnphttp(h, 0, 0); |
} |
} |
} |
} |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
} |
} |
|
|
Line 355 static void
|
Line 517 static void
|
ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path) |
ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path) |
{ |
{ |
syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path); |
syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path); |
syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID); | syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff); |
/* Remove from the list */ |
/* Remove from the list */ |
if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0) { | #ifdef UPNP_STRICT |
| if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) { |
| /* SID: header missing or empty */ |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
|
} else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) { |
|
/* no NT: or Callback: header must be present */ |
|
BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); |
|
} else |
|
#endif |
|
if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) { |
|
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); |
} else { |
} else { |
BuildResp_upnphttp(h, 0, 0); |
BuildResp_upnphttp(h, 0, 0); |
} |
} |
SendResp_upnphttp(h); | SendRespAndClose_upnphttp(h); |
CloseSocket_upnphttp(h); | |
} |
} |
#endif |
#endif |
|
|
/* Parse and process Http Query | /* Parse and process Http Query |
* called once all the HTTP headers have been received. */ | * called once all the HTTP headers have been received, |
| * so it is guaranteed that h->req_buf contains the \r\n\r\n |
| * character sequence */ |
static void |
static void |
ProcessHttpQuery_upnphttp(struct upnphttp * h) |
ProcessHttpQuery_upnphttp(struct upnphttp * h) |
{ |
{ |
|
static const struct { |
|
const char * path; |
|
char * (* f)(int *); |
|
} path_desc[] = { |
|
{ ROOTDESC_PATH, genRootDesc}, |
|
{ WANIPC_PATH, genWANIPCn}, |
|
{ WANCFG_PATH, genWANCfg}, |
|
#ifdef HAS_DUMMY_SERVICE |
|
{ DUMMY_PATH, NULL}, |
|
#endif |
|
#ifdef ENABLE_L3F_SERVICE |
|
{ L3F_PATH, genL3F}, |
|
#endif |
|
#ifdef ENABLE_6FC_SERVICE |
|
{ WANIP6FC_PATH, gen6FC}, |
|
#endif |
|
#ifdef ENABLE_DP_SERVICE |
|
{ DP_PATH, genDP}, |
|
#endif |
|
{ NULL, NULL} |
|
}; |
char HttpCommand[16]; |
char HttpCommand[16]; |
char HttpUrl[128]; |
char HttpUrl[128]; |
char * HttpVer; |
char * HttpVer; |
Line 380 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
Line 573 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
p = h->req_buf; |
p = h->req_buf; |
if(!p) |
if(!p) |
return; |
return; |
|
/* note : checking (*p != '\r') is enough to avoid runing off the |
|
* end of the buffer, because h->req_buf is guaranteed to contain |
|
* the \r\n\r\n character sequence */ |
for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++) |
for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++) |
HttpCommand[i] = *(p++); |
HttpCommand[i] = *(p++); |
HttpCommand[i] = '\0'; |
HttpCommand[i] = '\0'; |
Line 405 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
Line 601 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
else if(strcmp("GET", HttpCommand) == 0) |
else if(strcmp("GET", HttpCommand) == 0) |
{ |
{ |
h->req_command = EGet; |
h->req_command = EGet; |
if(strcasecmp(ROOTDESC_PATH, HttpUrl) == 0) | for(i=0; path_desc[i].path; i++) { |
{ | if(strcasecmp(path_desc[i].path, HttpUrl) == 0) { |
sendXMLdesc(h, genRootDesc); | if(path_desc[i].f) |
} | sendXMLdesc(h, path_desc[i].f); |
else if(strcasecmp(WANIPC_PATH, HttpUrl) == 0) | else |
{ | |
sendXMLdesc(h, genWANIPCn); | |
} | |
else if(strcasecmp(WANCFG_PATH, HttpUrl) == 0) | |
{ | |
sendXMLdesc(h, genWANCfg); | |
} | |
#ifdef HAS_DUMMY_SERVICE |
#ifdef HAS_DUMMY_SERVICE |
else if(strcasecmp(DUMMY_PATH, HttpUrl) == 0) | sendDummyDesc(h); |
{ | #else |
sendDummyDesc(h); | continue; |
} | |
#endif |
#endif |
#ifdef ENABLE_L3F_SERVICE | return; |
else if(strcasecmp(L3F_PATH, HttpUrl) == 0) | } |
{ | |
sendXMLdesc(h, genL3F); | |
} |
} |
#endif | if(0 == memcmp(HttpUrl, "/ctl/", 5)) { |
else | /* 405 Method Not Allowed |
{ | * Allow: POST */ |
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl); | h->respflags = FLAG_ALLOW_POST; |
Send404(h); | Send405(h); |
| return; |
} |
} |
|
#ifdef ENABLE_EVENTS |
|
if(0 == memcmp(HttpUrl, "/evt/", 5)) { |
|
/* 405 Method Not Allowed |
|
* Allow: SUBSCRIBE, UNSUBSCRIBE */ |
|
h->respflags = FLAG_ALLOW_SUB_UNSUB; |
|
Send405(h); |
|
return; |
|
} |
|
#endif |
|
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl); |
|
Send404(h); |
} |
} |
#ifdef ENABLE_EVENTS |
#ifdef ENABLE_EVENTS |
else if(strcmp("SUBSCRIBE", HttpCommand) == 0) |
else if(strcmp("SUBSCRIBE", HttpCommand) == 0) |
Line 464 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
Line 662 ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
void |
void |
Process_upnphttp(struct upnphttp * h) |
Process_upnphttp(struct upnphttp * h) |
{ |
{ |
|
char * h_tmp; |
char buf[2048]; |
char buf[2048]; |
int n; |
int n; |
|
|
if(!h) |
if(!h) |
return; |
return; |
switch(h->state) |
switch(h->state) |
{ |
{ |
case 0: | case EWaitingForHttpRequest: |
n = recv(h->socket, buf, 2048, 0); | n = recv(h->socket, buf, sizeof(buf), 0); |
if(n<0) |
if(n<0) |
{ |
{ |
syslog(LOG_ERR, "recv (state0): %m"); | if(errno != EAGAIN && |
h->state = 100; | errno != EWOULDBLOCK && |
| errno != EINTR) |
| { |
| syslog(LOG_ERR, "recv (state0): %m"); |
| h->state = EToDelete; |
| } |
| /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ |
} |
} |
else if(n==0) |
else if(n==0) |
{ |
{ |
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly"); | syslog(LOG_WARNING, "HTTP Connection closed unexpectedly"); |
h->state = 100; | h->state = EToDelete; |
} |
} |
else |
else |
{ |
{ |
const char * endheaders; |
const char * endheaders; |
/* if 1st arg of realloc() is null, |
/* if 1st arg of realloc() is null, |
* realloc behaves the same as malloc() */ |
* realloc behaves the same as malloc() */ |
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1); | h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1); |
memcpy(h->req_buf + h->req_buflen, buf, n); | if (h_tmp == NULL) |
h->req_buflen += n; | { |
h->req_buf[h->req_buflen] = '\0'; | syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)"); |
| h->state = EToDelete; |
| } |
| else |
| { |
| h->req_buf = h_tmp; |
| memcpy(h->req_buf + h->req_buflen, buf, n); |
| h->req_buflen += n; |
| h->req_buf[h->req_buflen] = '\0'; |
| } |
/* search for the string "\r\n\r\n" */ |
/* search for the string "\r\n\r\n" */ |
endheaders = findendheaders(h->req_buf, h->req_buflen); |
endheaders = findendheaders(h->req_buf, h->req_buflen); |
if(endheaders) |
if(endheaders) |
{ |
{ |
|
/* at this point, the request buffer (h->req_buf) |
|
* is guaranteed to contain the \r\n\r\n character sequence */ |
h->req_contentoff = endheaders - h->req_buf + 4; |
h->req_contentoff = endheaders - h->req_buf + 4; |
ProcessHttpQuery_upnphttp(h); |
ProcessHttpQuery_upnphttp(h); |
} |
} |
} |
} |
break; |
break; |
case 1: | case EWaitingForHttpContent: |
n = recv(h->socket, buf, 2048, 0); | n = recv(h->socket, buf, sizeof(buf), 0); |
if(n<0) |
if(n<0) |
{ |
{ |
syslog(LOG_ERR, "recv (state1): %m"); | if(errno != EAGAIN && |
h->state = 100; | errno != EWOULDBLOCK && |
| errno != EINTR) |
| { |
| syslog(LOG_ERR, "recv (state1): %m"); |
| h->state = EToDelete; |
| } |
| /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ |
} |
} |
else if(n==0) |
else if(n==0) |
{ |
{ |
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly"); |
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly"); |
h->state = 100; | h->state = EToDelete; |
} |
} |
else |
else |
{ |
{ |
/*fwrite(buf, 1, n, stdout);*/ /* debug */ | void * tmp = realloc(h->req_buf, n + h->req_buflen); |
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen); | if(!tmp) |
memcpy(h->req_buf + h->req_buflen, buf, n); | |
h->req_buflen += n; | |
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) | |
{ |
{ |
ProcessHTTPPOST_upnphttp(h); | syslog(LOG_ERR, "memory allocation error %m"); |
| h->state = EToDelete; |
} |
} |
|
else |
|
{ |
|
h->req_buf = tmp; |
|
memcpy(h->req_buf + h->req_buflen, buf, n); |
|
h->req_buflen += n; |
|
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) |
|
{ |
|
ProcessHTTPPOST_upnphttp(h); |
|
} |
|
} |
} |
} |
break; |
break; |
|
case ESendingContinue: |
|
if(SendResp_upnphttp(h)) |
|
h->state = EWaitingForHttpContent; |
|
break; |
|
case ESendingAndClosing: |
|
SendRespAndClose_upnphttp(h); |
|
break; |
default: |
default: |
syslog(LOG_WARNING, "Unexpected state: %d", h->state); |
syslog(LOG_WARNING, "Unexpected state: %d", h->state); |
} |
} |
Line 531 Process_upnphttp(struct upnphttp * h)
|
Line 769 Process_upnphttp(struct upnphttp * h)
|
|
|
static const char httpresphead[] = |
static const char httpresphead[] = |
"%s %d %s\r\n" |
"%s %d %s\r\n" |
/*"Content-Type: text/xml; charset=\"utf-8\"\r\n"*/ |
|
"Content-Type: %s\r\n" |
"Content-Type: %s\r\n" |
"Connection: close\r\n" |
"Connection: close\r\n" |
"Content-Length: %d\r\n" |
"Content-Length: %d\r\n" |
/*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/ |
|
"Server: " MINIUPNPD_SERVER_STRING "\r\n" |
"Server: " MINIUPNPD_SERVER_STRING "\r\n" |
; /*"\r\n";*/ |
; /*"\r\n";*/ |
/* |
/* |
Line 556 BuildHeader_upnphttp(struct upnphttp * h, int respcode
|
Line 792 BuildHeader_upnphttp(struct upnphttp * h, int respcode
|
int bodylen) |
int bodylen) |
{ |
{ |
int templen; |
int templen; |
if(!h->res_buf) | if(!h->res_buf || |
{ | h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) { |
templen = sizeof(httpresphead) + 128 + bodylen; | if(h->res_buf) |
| free(h->res_buf); |
| templen = sizeof(httpresphead) + 256 + bodylen; |
h->res_buf = (char *)malloc(templen); |
h->res_buf = (char *)malloc(templen); |
|
if(!h->res_buf) { |
|
syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()"); |
|
return; |
|
} |
h->res_buf_alloclen = templen; |
h->res_buf_alloclen = templen; |
} |
} |
|
h->res_sent = 0; |
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, |
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, |
httpresphead, h->HttpVer, |
httpresphead, h->HttpVer, |
respcode, respmsg, |
respcode, respmsg, |
(h->respflags&FLAG_HTML)?"text/html":"text/xml", | (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"", |
bodylen); |
bodylen); |
|
/* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */ |
|
/* Content-Type MUST be 'text/xml' according to UDA v1.0 */ |
/* Additional headers */ |
/* Additional headers */ |
|
#ifdef ENABLE_HTTP_DATE |
|
{ |
|
char http_date[64]; |
|
time_t t; |
|
struct tm tm; |
|
time(&t); |
|
gmtime_r(&t, &tm); |
|
/* %a and %b depend on locale */ |
|
strftime(http_date, sizeof(http_date), |
|
"%a, %d %b %Y %H:%M:%S GMT", &tm); |
|
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
|
h->res_buf_alloclen - h->res_buflen, |
|
"Date: %s\r\n", http_date); |
|
} |
|
#endif |
#ifdef ENABLE_EVENTS |
#ifdef ENABLE_EVENTS |
if(h->respflags & FLAG_TIMEOUT) { |
if(h->respflags & FLAG_TIMEOUT) { |
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
Line 586 BuildHeader_upnphttp(struct upnphttp * h, int respcode
|
Line 846 BuildHeader_upnphttp(struct upnphttp * h, int respcode
|
if(h->respflags & FLAG_SID) { |
if(h->respflags & FLAG_SID) { |
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
h->res_buf_alloclen - h->res_buflen, |
h->res_buf_alloclen - h->res_buflen, |
"SID: %s\r\n", h->req_SID); | "SID: %s\r\n", h->res_SID); |
} |
} |
#endif |
#endif |
|
if(h->respflags & FLAG_ALLOW_POST) { |
|
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
|
h->res_buf_alloclen - h->res_buflen, |
|
"Allow: %s\r\n", "POST"); |
|
} else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) { |
|
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
|
h->res_buf_alloclen - h->res_buflen, |
|
"Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE"); |
|
} |
|
if(h->accept_language[0] != '\0') { |
|
/* defaulting to "en" */ |
|
h->res_buflen += snprintf(h->res_buf + h->res_buflen, |
|
h->res_buf_alloclen - h->res_buflen, |
|
"Content-Language: %s\r\n", |
|
h->accept_language[0] == '*' ? "en" : h->accept_language); |
|
} |
h->res_buf[h->res_buflen++] = '\r'; |
h->res_buf[h->res_buflen++] = '\r'; |
h->res_buf[h->res_buflen++] = '\n'; |
h->res_buf[h->res_buflen++] = '\n'; |
if(h->res_buf_alloclen < (h->res_buflen + bodylen)) |
if(h->res_buf_alloclen < (h->res_buflen + bodylen)) |
{ |
{ |
h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen)); | char * tmp; |
h->res_buf_alloclen = h->res_buflen + bodylen; | tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen)); |
| if(tmp) |
| { |
| h->res_buf = tmp; |
| h->res_buf_alloclen = h->res_buflen + bodylen; |
| } |
| else |
| { |
| syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()"); |
| } |
} |
} |
} |
} |
|
|
Line 617 BuildResp_upnphttp(struct upnphttp * h,
|
Line 902 BuildResp_upnphttp(struct upnphttp * h,
|
BuildResp2_upnphttp(h, 200, "OK", body, bodylen); |
BuildResp2_upnphttp(h, 200, "OK", body, bodylen); |
} |
} |
|
|
void | int |
SendResp_upnphttp(struct upnphttp * h) |
SendResp_upnphttp(struct upnphttp * h) |
{ |
{ |
int n; | ssize_t n; |
n = send(h->socket, h->res_buf, h->res_buflen, 0); | |
if(n<0) | while (h->res_sent < h->res_buflen) |
{ |
{ |
syslog(LOG_ERR, "send(res_buf): %m"); | n = send(h->socket, h->res_buf + h->res_sent, |
| h->res_buflen - h->res_sent, 0); |
| if(n<0) |
| { |
| if(errno == EINTR) |
| continue; /* try again immediatly */ |
| if(errno == EAGAIN || errno == EWOULDBLOCK) |
| { |
| /* try again later */ |
| return 0; |
| } |
| syslog(LOG_ERR, "send(res_buf): %m"); |
| break; /* avoid infinite loop */ |
| } |
| else if(n == 0) |
| { |
| syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)", |
| h->res_sent, h->res_buflen); |
| break; |
| } |
| else |
| { |
| h->res_sent += n; |
| } |
} |
} |
else if(n < h->res_buflen) | return 1; /* finished */ |
| } |
| |
| void |
| SendRespAndClose_upnphttp(struct upnphttp * h) |
| { |
| ssize_t n; |
| |
| while (h->res_sent < h->res_buflen) |
{ |
{ |
/* TODO : handle correctly this case */ | n = send(h->socket, h->res_buf + h->res_sent, |
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)", | h->res_buflen - h->res_sent, 0); |
n, h->res_buflen); | if(n<0) |
| { |
| if(errno == EINTR) |
| continue; /* try again immediatly */ |
| if(errno == EAGAIN || errno == EWOULDBLOCK) |
| { |
| /* try again later */ |
| h->state = ESendingAndClosing; |
| return; |
| } |
| syslog(LOG_ERR, "send(res_buf): %m"); |
| break; /* avoid infinite loop */ |
| } |
| else if(n == 0) |
| { |
| syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)", |
| h->res_sent, h->res_buflen); |
| break; |
| } |
| else |
| { |
| h->res_sent += n; |
| } |
} |
} |
|
CloseSocket_upnphttp(h); |
} |
} |
|
|