1824 lines
45 KiB
C
Executable File
1824 lines
45 KiB
C
Executable File
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <syslog.h>
|
|
#include <arpa/inet.h>
|
|
#include <limits.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
|
|
#include "upnphttp.h"
|
|
#include "upnpsoap.h"
|
|
|
|
static int get_sockfd(void)
|
|
{
|
|
static int sockfd = -1;
|
|
|
|
if (sockfd == -1) {
|
|
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
|
|
perror("user: socket creation failed");
|
|
return(-1);
|
|
}
|
|
}
|
|
return sockfd;
|
|
}
|
|
|
|
IPCon IPCon_New(char * ifname)
|
|
{
|
|
IPCon ipcon=NULL;
|
|
|
|
ipcon = (IPCon)malloc(sizeof(_IPCon));
|
|
if (!ipcon) {
|
|
printf("Error in IPCon_New:Cannot allocate memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
ipcon->ifname = ifname;
|
|
return (ipcon);
|
|
}
|
|
|
|
|
|
IPCon IPCon_Destroy(IPCon this)
|
|
{
|
|
if (!this)
|
|
return (NULL);
|
|
|
|
free(this);
|
|
return (NULL);
|
|
}
|
|
|
|
BOOLEAN IPCon_GetIpAddr(IPCon this, struct in_addr *adr)
|
|
{
|
|
struct ifreq ifr;
|
|
int fd;
|
|
|
|
fd = get_sockfd();
|
|
|
|
if (fd >= 0) {
|
|
strcpy(ifr.ifr_name, this->ifname);
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
|
|
if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
|
|
memcpy(adr,&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,sizeof(struct in_addr));
|
|
close(fd);
|
|
return TRUE;
|
|
} else {
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
char *IPCon_GetIpAddrByStr(IPCon this)
|
|
{
|
|
struct in_addr adr;
|
|
BOOLEAN result=FALSE;
|
|
|
|
result = IPCon_GetIpAddr(this,&adr);
|
|
|
|
if (result == FALSE) {
|
|
return NULL;
|
|
} else {
|
|
return inet_ntoa(adr);
|
|
}
|
|
|
|
}
|
|
|
|
int OpenAndConfHTTPSocket(const char * addr, unsigned short port)
|
|
{
|
|
int s;
|
|
int i = 1;
|
|
struct sockaddr_in listenname;
|
|
|
|
if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
syslog(LOG_ERR, "socket(http): %m");
|
|
return -1;
|
|
}
|
|
|
|
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
|
|
{
|
|
syslog(LOG_WARNING, "setsockopt(http, SO_REUSEADDR): %m");
|
|
}
|
|
|
|
memset(&listenname, 0, sizeof(struct sockaddr_in));
|
|
listenname.sin_family = AF_INET;
|
|
listenname.sin_port = htons(port);
|
|
listenname.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0)
|
|
{
|
|
syslog(LOG_ERR, "bind(http): %m");
|
|
return -1;
|
|
}
|
|
|
|
if(listen(s, 6) < 0)
|
|
{
|
|
syslog(LOG_ERR, "listen(http): %m");
|
|
return -1;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
int ReliableSend(int socket, const char *data, const int len)
|
|
{
|
|
int n;
|
|
unsigned int byte_left = len;
|
|
int bytes_sent = 0;
|
|
|
|
if (socket < 0 || data == NULL || len <= 0)
|
|
return -1;
|
|
|
|
while (byte_left > 0) {
|
|
// write data
|
|
n = send(socket, data + bytes_sent, byte_left,
|
|
MSG_DONTROUTE | MSG_NOSIGNAL );
|
|
if( n == -1 ) {
|
|
syslog(LOG_ERR, "ReliableSend: sending failed!");
|
|
return -1;
|
|
}
|
|
|
|
byte_left = byte_left - n;
|
|
bytes_sent += n;
|
|
}
|
|
|
|
n = bytes_sent;
|
|
return n;
|
|
}
|
|
|
|
struct upnphttp * New_upnphttp(int s)
|
|
{
|
|
struct upnphttp * ret;
|
|
if(s<0)
|
|
return NULL;
|
|
ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
|
|
if(ret == NULL)
|
|
return NULL;
|
|
memset(ret, 0, sizeof(struct upnphttp));
|
|
ret->socket = s;
|
|
return ret;
|
|
}
|
|
|
|
void CloseSocket_upnphttp(struct upnphttp * h)
|
|
{
|
|
close(h->socket);
|
|
h->socket = -1;
|
|
h->state = 100;
|
|
}
|
|
|
|
void Delete_upnphttp(struct upnphttp * h)
|
|
{
|
|
if(h)
|
|
{
|
|
if(h->socket >= 0)
|
|
{
|
|
|
|
close(h->socket);
|
|
h->socket=-1;
|
|
}
|
|
|
|
|
|
if(h->req_buf)
|
|
{
|
|
free(h->req_buf);
|
|
h->req_buf=NULL;
|
|
}
|
|
|
|
|
|
if(h->res_buf)
|
|
{
|
|
|
|
free(h->res_buf);
|
|
h->res_buf=NULL;
|
|
}
|
|
|
|
free(h);
|
|
}
|
|
}
|
|
|
|
/* parse HttpHeaders of the REQUEST */
|
|
static void ParseHttpHeaders(struct upnphttp * h)
|
|
{
|
|
char * line;
|
|
char * colon;
|
|
char * p;
|
|
int n;
|
|
line = h->req_buf;
|
|
/* TODO : check if req_buf, contentoff are ok */
|
|
while(line < (h->req_buf + h->req_contentoff))
|
|
{
|
|
colon = strchr(line, ':');
|
|
if(colon)
|
|
{
|
|
if(strncasecmp(line, "Content-Length", 14)==0)
|
|
{
|
|
p = colon;
|
|
while(*p < '0' || *p > '9')
|
|
p++;
|
|
h->req_contentlen = atoi(p);
|
|
/*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
|
|
printf(" readbufflen=%d contentoff = %d\n",
|
|
h->req_buflen, h->req_contentoff);*/
|
|
}
|
|
else if(strncasecmp(line, "SOAPAction", 10)==0)
|
|
{
|
|
p = colon;
|
|
n = 0;
|
|
while(*p == ':' || *p == ' ' || *p == '\t')
|
|
p++;
|
|
while(p[n]>=' ')
|
|
{
|
|
n++;
|
|
}
|
|
if((p[0] == '"' && p[n-1] == '"')
|
|
|| (p[0] == '\'' && p[n-1] == '\''))
|
|
{
|
|
p++; n -= 2;
|
|
}
|
|
h->req_soapAction = p;
|
|
h->req_soapActionLen = n;
|
|
}
|
|
}
|
|
while(!(line[0] == '\r' && line[1] == '\n'))
|
|
line++;
|
|
line += 2;
|
|
}
|
|
}
|
|
|
|
/* very minimalistic 404 error message */
|
|
static void 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 = ReliableSend(h->socket, error404, sizeof(error404) - 1);
|
|
if (n != (sizeof(error404) - 1))
|
|
{
|
|
syslog(LOG_ERR, "Send404: %d bytes sent (out of %d)",
|
|
n, (sizeof(error404) - 1));
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
|
|
// support HNAP1
|
|
void SendError(struct upnphttp * h, const int code, const char *title, const char *realm, const char *body)
|
|
{
|
|
char *error_title=NULL;
|
|
char *error_realm=NULL;
|
|
char *error_body=NULL;
|
|
int len;
|
|
int n;
|
|
|
|
if (code <=0 || title == NULL) {
|
|
Send404(h);
|
|
return;
|
|
}
|
|
|
|
error_title = (char *)malloc(2048);
|
|
sprintf(error_title, "HTTP/1.1 %d %s\r\n"
|
|
"Connection: close\r\n"
|
|
"Content-type: text/html\r\n", code, title);
|
|
if (realm) {
|
|
error_realm = (char *)malloc(256);
|
|
sprintf(error_realm, "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm);
|
|
strcat(error_title, error_realm);
|
|
}
|
|
|
|
strcat(error_title, "\r\n");
|
|
|
|
if (body) {
|
|
error_body = (char *)malloc(1024);
|
|
sprintf(error_body, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
|
|
"<BODY><H1>%s</H1>%s</BODY></HTML>\r\n", code, title, title, body);
|
|
strcat(error_title, error_body);
|
|
}
|
|
|
|
len = strlen(error_title);
|
|
|
|
n = ReliableSend(h->socket, error_title, len);
|
|
if (n != len)
|
|
{
|
|
syslog(LOG_ERR, "Send%d: %d bytes sent (out of %d)", code,
|
|
n, len);
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
if (error_title)
|
|
{
|
|
free(error_title);
|
|
error_title=NULL;
|
|
}
|
|
|
|
if (error_realm)
|
|
{
|
|
free(error_realm);
|
|
error_realm=NULL;
|
|
}
|
|
|
|
if(error_body)
|
|
{
|
|
free(error_body);
|
|
error_body=NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/* Precondition Failed */
|
|
static void Send412PreconditionFailed(struct upnphttp * h)
|
|
{
|
|
static const char error412[] = "HTTP/1.1 412 Precondition Failed\r\n"
|
|
"Content-Length: 0\r\n"
|
|
"Connection: close\r\n\r\n";
|
|
int n;
|
|
|
|
n = ReliableSend(h->socket, error412, sizeof(error412) - 1);
|
|
if (n != (sizeof(error412) - 1))
|
|
{
|
|
syslog(LOG_ERR, "Send412PreconditionFailed: %d bytes sent (out of %d)",
|
|
n, (sizeof(error412) - 1));
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
|
|
/* Too Many Subscribers */
|
|
static void Send412TooManySubscribers(struct upnphttp * h)
|
|
{
|
|
static const char error412[] = "HTTP/1.1 412 Too Many Subscribers\r\n"
|
|
"Content-Length: 0\r\n"
|
|
"Connection: close\r\n\r\n";
|
|
int n;
|
|
|
|
n = ReliableSend(h->socket, error412, sizeof(error412) - 1);
|
|
if (n != (sizeof(error412) - 1))
|
|
{
|
|
syslog(LOG_ERR, "Send412TooManySubscribers: %d bytes sent (out of %d)",
|
|
n, (sizeof(error412) - 1));
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
|
|
/* very minimalistic 501 error message */
|
|
static void Send501(struct upnphttp * h)
|
|
{
|
|
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>"
|
|
"<BODY><H1>Not Implemented</H1>The HTTP Method "
|
|
"is not implemented by this server.</BODY></HTML>\r\n";
|
|
int n;
|
|
|
|
n = ReliableSend(h->socket, error501, sizeof(error501) - 1);
|
|
if (n != (sizeof(error501) - 1))
|
|
{
|
|
syslog(LOG_ERR, "Send501: %d bytes sent (out of %d)",
|
|
n, (sizeof(error501) - 1));
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
|
|
static const char * findendheaders(const char * s, int len)
|
|
{
|
|
while(len-->0)
|
|
{
|
|
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
|
|
return s;
|
|
s++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Sends the description generated by the parameter */
|
|
static void sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
|
{
|
|
char * desc;
|
|
int len;
|
|
desc = f(&len);
|
|
if(!desc)
|
|
{
|
|
syslog(LOG_ERR, "XML description generation failed");
|
|
return;
|
|
}
|
|
BuildResp_upnphttp(h, desc, len);
|
|
SendResp_upnphttp(h);
|
|
CloseSocket_upnphttp(h);
|
|
free(desc);
|
|
}
|
|
|
|
/* ProcessHTTPPOST_upnphttp()
|
|
* executes the SOAP query if it is possible */
|
|
static void ProcessHTTPPOST_upnphttp(struct upnphttp * h)
|
|
{
|
|
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
|
|
{
|
|
if(h->req_soapAction)
|
|
{
|
|
/* we can process the request */
|
|
syslog(LOG_INFO, "SOAPAction: %.*s",
|
|
h->req_soapActionLen, h->req_soapAction);
|
|
ExecuteSoapAction(h,
|
|
h->req_soapAction,
|
|
h->req_soapActionLen);
|
|
}
|
|
else
|
|
{
|
|
static const char err400str[] =
|
|
"<html><body>Bad request</body></html>";
|
|
syslog(LOG_INFO, "No SOAPAction in HTTP headers");
|
|
BuildResp2_upnphttp(h, 400, "Bad Request",
|
|
err400str, sizeof(err400str) - 1);
|
|
SendResp_upnphttp(h);
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* waiting for remaining data */
|
|
h->state = 1;
|
|
}
|
|
}
|
|
|
|
Upnp_Document CreatePropertySet(void)
|
|
{
|
|
Upnp_Document PropSet=NULL;
|
|
|
|
PropSet = (Upnp_Document) malloc(sizeof(Upnp_Document_CTX));
|
|
if (PropSet == NULL) {
|
|
syslog(LOG_ERR, "CreatePropertySet: out of memory!");
|
|
return NULL;
|
|
}
|
|
memset(PropSet, 0, sizeof(Upnp_Document_CTX));
|
|
LIST_INIT(&PropSet->doc_head);
|
|
return PropSet;
|
|
}
|
|
|
|
int UpnpAddToPropertySet(Upnp_Document PropSet,
|
|
const char *VarName, const char *message)
|
|
{
|
|
struct Upnp_Document_element *tmp=NULL;
|
|
char *name=NULL;
|
|
char *content=NULL;
|
|
|
|
if (PropSet == NULL || VarName == NULL || message == NULL)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
tmp = (struct Upnp_Document_element *) malloc(sizeof(struct Upnp_Document_element));
|
|
if (tmp == NULL) {
|
|
syslog(LOG_ERR, "UpnpAddToPropertySet: out of memory!");
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
memset(tmp, 0, sizeof(struct Upnp_Document_element));
|
|
|
|
name = (char *) malloc(strlen(VarName) + 1);
|
|
if (name == NULL) {
|
|
syslog(LOG_ERR, "UpnpAddToPropertySet: out of memory!");
|
|
free(tmp);
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
memset(name, 0, strlen(VarName) + 1);
|
|
memcpy(name, VarName, strlen(VarName));
|
|
|
|
content = (char *) malloc(strlen(message) + 1);
|
|
if (content == NULL) {
|
|
syslog(LOG_ERR, "UpnpAddToPropertySet: out of memory!");
|
|
free(tmp);
|
|
free(name);
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
memset(content, 0, strlen(message) + 1);
|
|
memcpy(content, message, strlen(message));
|
|
|
|
PropSet->NumOfVarName += 1;
|
|
PropSet->TotalMessageLen = strlen(message) + (strlen(VarName) * 2);
|
|
tmp->VarName = name;
|
|
tmp->message = content;
|
|
LIST_INSERT_HEAD(&PropSet->doc_head, tmp, entries);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
static char *MakeEventBody(Upnp_Document PropSet, unsigned int *total_len)
|
|
{
|
|
unsigned int len=0;
|
|
char *buf=NULL;
|
|
unsigned int buf_len=0;
|
|
struct Upnp_Document_element *e;
|
|
|
|
if (PropSet == NULL || PropSet->NumOfVarName == 0)
|
|
return NULL;
|
|
|
|
buf_len = (strlen("<e:property><></></e:property>") * PropSet->NumOfVarName)
|
|
+ PropSet->TotalMessageLen + 100;
|
|
buf = (char *) malloc(buf_len);
|
|
if (buf == NULL) {
|
|
syslog(LOG_ERR, "MakeEventBody: out of memory!");
|
|
return NULL;
|
|
}
|
|
memset(buf, 0, buf_len);
|
|
|
|
for(e = PropSet->doc_head.lh_first; e != NULL; e = e->entries.le_next)
|
|
{
|
|
if (e->VarName) {
|
|
char *tmpbuf=NULL;
|
|
unsigned int tmpbuf_len=0;
|
|
unsigned int propertyLen=0;
|
|
|
|
tmpbuf_len = strlen("<e:property><></></e:property>")
|
|
+ (strlen(e->VarName) * 2)
|
|
+ strlen(e->message) + 1;
|
|
tmpbuf = (char *) malloc(tmpbuf_len);
|
|
if (tmpbuf == NULL) {
|
|
syslog(LOG_ERR, "MakeEventBody: out of memory!");
|
|
free(buf);
|
|
return NULL;
|
|
}
|
|
memset(tmpbuf, 0, tmpbuf_len);
|
|
propertyLen = sprintf(tmpbuf,
|
|
"<e:property><%s>%s</%s></e:property>",
|
|
e->VarName, e->message, e->VarName);
|
|
len += propertyLen;
|
|
strcat(buf, tmpbuf);
|
|
free(tmpbuf);
|
|
}
|
|
}
|
|
|
|
*total_len = len;
|
|
return buf;
|
|
}
|
|
|
|
static void UpnpSendEvent(char *packet,
|
|
const char *EventBody, const unsigned int bodylength,
|
|
struct upnp_subscription_record *subscribe_list,
|
|
struct upnp_subscription_element *sub)
|
|
{
|
|
unsigned int packetLength;
|
|
struct sockaddr_in dest;
|
|
int n, sockfd=-1;
|
|
struct EvtRespElement *EvtResp=NULL;
|
|
|
|
if (packet == NULL || EventBody == NULL || bodylength == 0 ||
|
|
subscribe_list == NULL || sub == NULL)
|
|
return;
|
|
|
|
packetLength = sprintf(packet,
|
|
"NOTIFY %s HTTP/1.1\r\n"
|
|
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
|
|
"HOST: %s:%d\r\n"
|
|
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
|
"NT: upnp:event\r\n"
|
|
"NTS: upnp:propchange\r\n"
|
|
"SID: %s\r\n"
|
|
"SEQ: %d\r\n"
|
|
"Content-Length: %d\r\n\r\n"
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">"
|
|
"%s"
|
|
"</e:propertyset>",
|
|
sub->callback_url,
|
|
sub->IP,
|
|
sub->port,
|
|
sub->sid,
|
|
(int)sub->seq,
|
|
bodylength+110,
|
|
EventBody);
|
|
|
|
sub->seq++;
|
|
if (sub->seq >= UINT_MAX)
|
|
sub->seq = 1;
|
|
//printf("Packet------------>\n%s\n<-----------------Packet\n\n", packet);
|
|
|
|
memset(&dest,0,sizeof(struct sockaddr_in));
|
|
dest.sin_addr.s_addr = sub->IP_inet_addr;
|
|
dest.sin_port = htons(sub->port);
|
|
dest.sin_family = AF_INET;
|
|
|
|
// create a socket
|
|
if ((sockfd = socket( AF_INET, SOCK_STREAM, 0 )) == -1) {
|
|
syslog(LOG_ERR, "UpnpSendEvent: creating a socket falied!");
|
|
return;
|
|
}
|
|
if (connect( sockfd, ( struct sockaddr * )&dest,
|
|
sizeof( struct sockaddr_in ) ) == -1) {
|
|
syslog(LOG_ERR, "UpnpSendEvent: connecting a socket falied!");
|
|
close(sockfd);
|
|
return;
|
|
}
|
|
|
|
n = ReliableSend(sockfd, packet, packetLength);
|
|
if (n != packetLength)
|
|
{
|
|
syslog(LOG_ERR, "UpnpSendEvent: %d bytes sent (out of %d)",
|
|
n, packetLength);
|
|
close(sockfd);
|
|
return;
|
|
}
|
|
|
|
EvtResp = (struct EvtRespElement *) malloc(sizeof(struct EvtRespElement));
|
|
if (EvtResp == NULL) {
|
|
syslog(LOG_ERR, "UpnpSendEvent: out of memory!");
|
|
close(sockfd);
|
|
return;
|
|
}
|
|
memset(EvtResp, 0, sizeof(struct EvtRespElement));
|
|
EvtResp->socket = sockfd;
|
|
memcpy(EvtResp->sid, sub->sid, strlen(sub->sid));
|
|
EvtResp->TimeOut = 30;
|
|
LIST_INSERT_HEAD(&subscribe_list->EvtResp_head, EvtResp, entries);
|
|
}
|
|
|
|
void UpnpSendEventSingle(Upnp_Document PropSet,
|
|
struct upnp_subscription_record *subscribe_list,
|
|
struct upnp_subscription_element *sub)
|
|
{
|
|
char *packet=NULL;
|
|
unsigned int bodylength=0;
|
|
char *EventBody=NULL;
|
|
|
|
if (PropSet == NULL || sub == NULL || subscribe_list == NULL ||
|
|
subscribe_list->subscription_head.lh_first == NULL)
|
|
return;
|
|
|
|
EventBody = MakeEventBody(PropSet, &bodylength);
|
|
if (EventBody == NULL) {
|
|
syslog(LOG_ERR, "UpnpSendEventSingle: MakeEventBody failed!");
|
|
return;
|
|
}
|
|
|
|
packet = (char *) malloc(bodylength + 483);
|
|
if (packet == NULL) {
|
|
syslog(LOG_ERR, "UpnpSendEventSingle: out of memory!");
|
|
free(EventBody);
|
|
return;
|
|
}
|
|
memset(packet, 0, bodylength + 483);
|
|
|
|
UpnpSendEvent(packet, EventBody, bodylength, subscribe_list, sub);
|
|
free(EventBody);
|
|
free(packet);
|
|
}
|
|
|
|
void ProcessEventingResp(struct EvtRespElement *EvtResp)
|
|
{
|
|
char *buf=NULL;
|
|
int n;
|
|
|
|
if (EvtResp == NULL)
|
|
return;
|
|
|
|
buf = (char *) malloc(512);
|
|
if (buf == NULL) {
|
|
close(EvtResp->socket);
|
|
return;
|
|
}
|
|
memset(buf, 0, 512);
|
|
|
|
if ((n = recv(EvtResp->socket, buf, 512, 0)) == -1) {
|
|
syslog(LOG_ERR, "ProcessEventingResp: Receive failed!");
|
|
}
|
|
else if(n==0)
|
|
{
|
|
syslog(LOG_WARNING, "ProcessEventingResp: connection closed inexpectedly");
|
|
}
|
|
|
|
//printf("------------>\nProcessEventingResp: sid[%s]\n%s\n<--------------\n", EvtResp->sid, buf);
|
|
close(EvtResp->socket);
|
|
free(buf);
|
|
}
|
|
|
|
void UpnpSendEventAll(Upnp_Document PropSet,
|
|
struct upnp_subscription_record *sub_list)
|
|
{
|
|
struct upnp_subscription_element *sub;
|
|
unsigned int bodylength=0;
|
|
char *EventBody=NULL;
|
|
char *packet=NULL;
|
|
|
|
if (PropSet == NULL || sub_list == NULL || sub_list->subscription_head.lh_first == NULL)
|
|
return;
|
|
|
|
EventBody = MakeEventBody(PropSet, &bodylength);
|
|
if (EventBody == NULL) {
|
|
syslog(LOG_ERR, "UpnpSendEventAll: MakeEventBody failed!");
|
|
return;
|
|
}
|
|
|
|
packet = (char *) malloc(bodylength + 483);
|
|
if (packet == NULL) {
|
|
syslog(LOG_ERR, "UpnpSendEventAll: out of memory!");
|
|
free(EventBody);
|
|
return;
|
|
}
|
|
|
|
for (sub = sub_list->subscription_head.lh_first; sub != NULL; sub = sub->entries.le_next)
|
|
{
|
|
memset(packet, 0, bodylength + 483);
|
|
UpnpSendEvent(packet, EventBody, bodylength, sub_list, sub);
|
|
}
|
|
free(EventBody);
|
|
free(packet);
|
|
}
|
|
|
|
void UpnpDocument_free(Upnp_Document PropSet)
|
|
{
|
|
struct Upnp_Document_element *e;
|
|
struct Upnp_Document_element *next;
|
|
|
|
if (PropSet == NULL)
|
|
return;
|
|
|
|
for(e = PropSet->doc_head.lh_first; e != NULL; )
|
|
{
|
|
next = e->entries.le_next;
|
|
if (e->VarName)
|
|
free(e->VarName);
|
|
if (e->message)
|
|
free(e->message);
|
|
LIST_REMOVE(e, entries);
|
|
free(e);
|
|
e = next;
|
|
}
|
|
|
|
free(PropSet);
|
|
}
|
|
|
|
static int BuildSubscribeResponse(struct upnphttp * h, struct process_upnp_subscription *sub)
|
|
{
|
|
char *Subresp=NULL;
|
|
int n, packet_len, ret=UPNP_E_SUCCESS;
|
|
|
|
Subresp = (char *) malloc(300);
|
|
if (Subresp == NULL) {
|
|
syslog(LOG_ERR, "BuildSubscribeResponse: out of memory!");
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
}
|
|
memset(Subresp, 0, 300);
|
|
|
|
packet_len = sprintf(Subresp,
|
|
"HTTP/1.1 200 OK \r\n"
|
|
"Server: " MINIUPNPD_SERVER_STRING "\r\n"
|
|
"SID: %s\r\n"
|
|
"TIMEOUT: Second-%d\r\n"
|
|
"Content-Length: 0\r\n"
|
|
"\r\n",
|
|
sub->sid, (int)h->subscribe_list->max_subscription_time);
|
|
|
|
//printf("BuildSubscribeResponse------->\n%s<------------BuildSubscribeResponse\n", Subresp);
|
|
|
|
n = ReliableSend(h->socket, Subresp, packet_len);
|
|
if (n != packet_len)
|
|
{
|
|
syslog(LOG_ERR, "BuildSubscribeResponse: %d bytes sent (out of %d)",
|
|
n, packet_len);
|
|
}
|
|
|
|
free(Subresp);
|
|
CloseSocket_upnphttp(h);
|
|
return ret;
|
|
}
|
|
|
|
static void BuildUnSubscribeResponse(struct upnphttp * h)
|
|
{
|
|
static const char UnSubresp[] =
|
|
"HTTP/1.1 200 OK \r\n"
|
|
"Content-Length: 0\r\n"
|
|
"\r\n";
|
|
|
|
int n;
|
|
|
|
n = ReliableSend(h->socket, UnSubresp, sizeof(UnSubresp) - 1);
|
|
if (n != (sizeof(UnSubresp) - 1))
|
|
{
|
|
syslog(LOG_ERR, "BuildUnSubscribeResponse: %d bytes sent (out of %d)",
|
|
n, (sizeof(UnSubresp) - 1));
|
|
}
|
|
CloseSocket_upnphttp(h);
|
|
}
|
|
|
|
static struct upnp_subscription_element *UPnPTryToSubscribe
|
|
(struct upnphttp * h, struct process_upnp_subscription *sub)
|
|
{
|
|
int SIDNumber,rnumber;
|
|
char *SID=NULL;
|
|
struct upnp_subscription_element *new_sub=NULL;
|
|
|
|
if (h == NULL || sub == NULL)
|
|
return NULL;
|
|
|
|
if (h->subscribe_list->total_subscription >= h->subscribe_list->max_subscription_num)
|
|
return NULL;
|
|
|
|
new_sub = (struct upnp_subscription_element *) malloc(sizeof(struct upnp_subscription_element));
|
|
if (new_sub == NULL)
|
|
return NULL;
|
|
memset(new_sub, 0, sizeof(struct upnp_subscription_element));
|
|
|
|
//
|
|
// The SID must be globally unique, so lets generate it using
|
|
// a bunch of random hex characters
|
|
//
|
|
SID = (char*)malloc(SID_LEN);
|
|
if (SID == NULL) {
|
|
free(new_sub);
|
|
return NULL;
|
|
}
|
|
memset(SID,0,SID_LEN);
|
|
sprintf(SID,"uuid:");
|
|
for(SIDNumber=5;SIDNumber<=12;++SIDNumber)
|
|
{
|
|
rnumber = rand()%16;
|
|
sprintf(SID+SIDNumber,"%x",rnumber);
|
|
}
|
|
sprintf(SID+SIDNumber,"-");
|
|
for(SIDNumber=14;SIDNumber<=17;++SIDNumber)
|
|
{
|
|
rnumber = rand()%16;
|
|
sprintf(SID+SIDNumber,"%x",rnumber);
|
|
}
|
|
sprintf(SID+SIDNumber,"-");
|
|
for(SIDNumber=19;SIDNumber<=22;++SIDNumber)
|
|
{
|
|
rnumber = rand()%16;
|
|
sprintf(SID+SIDNumber,"%x",rnumber);
|
|
}
|
|
sprintf(SID+SIDNumber,"-");
|
|
for(SIDNumber=24;SIDNumber<=27;++SIDNumber)
|
|
{
|
|
rnumber = rand()%16;
|
|
sprintf(SID+SIDNumber,"%x",rnumber);
|
|
}
|
|
sprintf(SID+SIDNumber,"-");
|
|
for(SIDNumber=29;SIDNumber<=40;++SIDNumber)
|
|
{
|
|
rnumber = rand()%16;
|
|
sprintf(SID+SIDNumber,"%x",rnumber);
|
|
}
|
|
|
|
memcpy(sub->sid, SID, SID_LEN);
|
|
if (BuildSubscribeResponse(h, sub) != UPNP_E_SUCCESS) {
|
|
free(SID);
|
|
free(new_sub);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(new_sub->IP, sub->IP, IP_ADDRLEN);
|
|
new_sub->IP_inet_addr = sub->IP_inet_addr;
|
|
new_sub->port = sub->port;
|
|
memcpy(new_sub->sid, SID, SID_LEN);
|
|
memcpy(new_sub->callback_url, sub->callback_url, URL_MAX_LEN);
|
|
new_sub->TimeOut = sub->TimeOut;
|
|
//WPS2DOTX
|
|
// new_sub->subscription_timeout = h->subscribe_list->subscription_timeout;
|
|
new_sub->subscription_timeout = sub->TimeOut;
|
|
|
|
// printf("\n\nsubscription_timeout=%d\n",new_sub->subscription_timeout);
|
|
|
|
new_sub->eventID = UPNP_EVENT_SUBSCRIPTION_REQUEST;
|
|
LIST_INSERT_HEAD(&h->subscribe_list->subscription_head, new_sub, entries);
|
|
h->subscribe_list->total_subscription++;
|
|
syslog(LOG_INFO, "Subscribe: total_subscription [%d]",
|
|
(int)h->subscribe_list->total_subscription);
|
|
|
|
free(SID);
|
|
return new_sub;
|
|
}
|
|
|
|
static char *GetTokenValue(const unsigned long buf, const unsigned long len, unsigned int *Value_len)
|
|
{
|
|
unsigned long start=0, end=0;
|
|
unsigned long buffer_end;
|
|
char *line;
|
|
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
line = (char *)buf;
|
|
buffer_end = (unsigned long)line + len;
|
|
|
|
while ((unsigned long)line < buffer_end) {
|
|
if ((*line == ' ') || (*line == ':') || (*line == '-'))
|
|
line++;
|
|
else {
|
|
start = (unsigned long)line;
|
|
break;
|
|
}
|
|
}
|
|
if (start == 0) {
|
|
*Value_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
while ((unsigned long)line < buffer_end) {
|
|
if ((*line == ' ') || (*line == '\r') || (*line == '\n')) {
|
|
end = (unsigned long)line;
|
|
break;
|
|
}
|
|
else
|
|
line++;
|
|
}
|
|
if (end == 0) {
|
|
*Value_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
*Value_len = (unsigned int)(end - start);
|
|
return (char *)start;
|
|
}
|
|
|
|
static __inline__ void GetLineLen(const unsigned long buf, unsigned int *line_len)
|
|
{
|
|
unsigned long start=0;
|
|
const unsigned short MaxCharsPerLine = 1000;
|
|
unsigned short NumChars=0;
|
|
char *line;
|
|
|
|
line = (char *)buf;
|
|
start = (unsigned long)line;
|
|
while (*line != '\n') {
|
|
NumChars++;
|
|
if (NumChars >= MaxCharsPerLine) {
|
|
syslog(LOG_WARNING, "Too many characters in a line!");
|
|
*line_len = 0;
|
|
return;
|
|
}
|
|
line++;
|
|
}
|
|
|
|
*line_len = (unsigned long)line + 1 - start;
|
|
return;
|
|
}
|
|
|
|
|
|
// To do : support IPv6
|
|
static __inline__ int GetIPandPortandCallBack(const unsigned long buf, const unsigned int buf_len,
|
|
struct process_upnp_subscription *sub)
|
|
{
|
|
char *line=NULL;
|
|
unsigned long buffer_end=0;
|
|
unsigned char dot_count=0;
|
|
unsigned long start=0;
|
|
unsigned long end=0;
|
|
unsigned char GotIPandPort=0;
|
|
|
|
line = (char *)buf;
|
|
buffer_end = (unsigned long)line + buf_len;
|
|
while ((unsigned long)line < buffer_end) {
|
|
if (*line == ' ') {
|
|
line++;
|
|
continue;
|
|
}
|
|
if (strncasecmp("<http://", line, 8) != 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
else
|
|
break;
|
|
}
|
|
|
|
line += 8;
|
|
start =(unsigned long) line;
|
|
while ((unsigned long)line < buffer_end) {
|
|
if (*line == '.')
|
|
dot_count++;
|
|
if ((*line == ':') || (*line == '/')) {
|
|
if (dot_count != IP_V4_DOT_COUNT)
|
|
return UPNP_E_INVALID_PARAM;
|
|
memcpy(sub->IP, (char *)start, (unsigned long)line-start);
|
|
if ((sub->IP_inet_addr = inet_addr(sub->IP)) == -1)
|
|
return UPNP_E_INVALID_PARAM;
|
|
break;
|
|
}
|
|
line++;
|
|
}
|
|
|
|
if (*line == '/') {
|
|
sub->port = 80;
|
|
GotIPandPort = 1;
|
|
start = (unsigned long)line;
|
|
end = 0;
|
|
goto get_callback;
|
|
}
|
|
|
|
line += 1;
|
|
start = (unsigned long)line;
|
|
while ((unsigned long)line < buffer_end) {
|
|
if (*line == '/') {
|
|
end = (unsigned long)line;
|
|
if (end <= start)
|
|
return UPNP_E_INVALID_PARAM;
|
|
else {
|
|
char port[10];
|
|
char copy_size;/*CVE-2021-35392*/
|
|
memset(port, 0, 10);
|
|
|
|
/*CVE-2021-35392*/
|
|
copy_size=end-start;
|
|
if( copy_size>10 )
|
|
{
|
|
copy_size=10;
|
|
}
|
|
|
|
memcpy(port, (char *)start, copy_size);
|
|
sub->port = atoi(port);
|
|
GotIPandPort = 1;
|
|
start = (unsigned long)line;
|
|
end = 0;
|
|
break;
|
|
}
|
|
}
|
|
line++;
|
|
}
|
|
|
|
get_callback:
|
|
if (!GotIPandPort)
|
|
return UPNP_E_INVALID_PARAM;
|
|
while ((unsigned long)line < buffer_end) {
|
|
if (*line == '>') {
|
|
end = (unsigned long)line;
|
|
if (end <= start)
|
|
return UPNP_E_INVALID_PARAM;
|
|
else {
|
|
memcpy(sub->callback_url, (char *)start, end-start);
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
}
|
|
line++;
|
|
}
|
|
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
|
|
static int ParseSUBSCRIBEPacket(struct upnphttp * h,
|
|
struct process_upnp_subscription *sub)
|
|
{
|
|
char *line=NULL, *value=NULL, *tmp=NULL;
|
|
unsigned int line_len=0, value_len=0, tmp_len=0;
|
|
unsigned long buffer_end=0;
|
|
|
|
if (h->req_buf == NULL || h->req_contentoff == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
line = h->req_buf;
|
|
buffer_end = (unsigned long)h->req_buf + h->req_contentoff;
|
|
while ((unsigned long)line < buffer_end) {
|
|
if (*line == ' ') {
|
|
line++;
|
|
continue;
|
|
}
|
|
GetLineLen((const unsigned long)line, &line_len);
|
|
if (line_len == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
else if (line_len > 2) {
|
|
if (strncasecmp("SUBSCRIBE", line, 9) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+9, line_len-9, &value_len);
|
|
if (value == NULL || value_len == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
if (strncasecmp(h->subscribe_list->event_url, value, value_len) != 0) {
|
|
syslog(LOG_WARNING, "SUBSCRIBE event url mismatched!");
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
}
|
|
else if (strncasecmp("UNSUBSCRIBE", line, 11) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+11, line_len-11, &value_len);
|
|
if (value == NULL || value_len == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
if (strncasecmp(h->subscribe_list->event_url, value, value_len) != 0) {
|
|
syslog(LOG_WARNING, "UNSUBSCRIBE event url mismatched!");
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
}
|
|
else if (strncasecmp("Host", line, 4) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+4, line_len-4, &value_len);
|
|
if (value == NULL || value_len == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
char host_info[30];
|
|
memset(host_info, 0, 30);
|
|
sprintf(host_info, "%s:%d", h->subscribe_list->my_IP, h->subscribe_list->my_port);
|
|
if (strncmp(value, host_info, value_len) != 0) {
|
|
syslog(LOG_WARNING, "Wrong host [%s]", host_info);
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
}
|
|
else if (strncasecmp("Callback", line, 8) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+8, line_len-8, &value_len);
|
|
if (value == NULL || value_len == 0 || (value_len > (URL_MAX_LEN-1)))
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
if (GetIPandPortandCallBack((const unsigned long) value, value_len, sub) != UPNP_E_SUCCESS)
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
else if (strncasecmp("Timeout", line, 7) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+7, line_len-7, &value_len);
|
|
if (value == NULL || value_len == 0 || value_len < 8)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
if (strncasecmp("Second", value, 6) != 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
tmp = value + 6;
|
|
tmp_len = value_len - 6;
|
|
value = GetTokenValue((const unsigned long)tmp, tmp_len+1, &value_len);
|
|
if (value_len == 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
else if (value_len == 8 && (strncasecmp("infinite", line, 8) == 0))
|
|
sub->TimeOut = MAX_SUB_TIMEOUT;
|
|
else {
|
|
if (value_len > 4)
|
|
sub->TimeOut = MAX_SUB_TIMEOUT;
|
|
else {
|
|
char time_out[5];
|
|
memset(time_out, 0, 5);
|
|
memcpy(time_out, value, value_len);
|
|
sub->TimeOut = atoi(time_out);
|
|
}
|
|
}
|
|
}
|
|
else if (strncasecmp("SID", line, 3) == 0) {
|
|
value = GetTokenValue((const unsigned long)line+3, line_len-3, &value_len);
|
|
if (value == NULL || value_len == 0 || value_len < 4)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
if (strncasecmp("uuid", value, 4) != 0)
|
|
return UPNP_E_INVALID_PARAM;
|
|
tmp = value + 4;
|
|
tmp_len = value_len - 4;
|
|
value = GetTokenValue((const unsigned long)tmp, tmp_len+1, &value_len);
|
|
if (value == NULL || value_len == 0 || value_len != 36)
|
|
return UPNP_E_INVALID_PARAM;
|
|
|
|
char sid[SID_LEN];
|
|
memset(sid, 0, SID_LEN);
|
|
memcpy(sub->sid, "uuid:", 5);
|
|
memcpy(sub->sid+5, value, value_len);
|
|
}
|
|
}
|
|
|
|
line += line_len;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
static void UPnPProcessSUBSCRIBE(struct upnphttp * h)
|
|
{
|
|
struct process_upnp_subscription sub;
|
|
struct upnp_subscription_element *new_sub=NULL;
|
|
int ret;
|
|
|
|
memset(&sub, 0, sizeof(struct process_upnp_subscription));
|
|
ret = ParseSUBSCRIBEPacket(h, &sub);
|
|
if (ret != UPNP_E_SUCCESS) {
|
|
Send412PreconditionFailed(h);
|
|
return;
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
printf("IP [%s]\n", sub.IP);
|
|
printf("Host [%d.%d.%d.%d:%d]\n", (sub.IP_inet_addr>>24)&0xFF,
|
|
(sub.IP_inet_addr>>16)&0xFF,
|
|
(sub.IP_inet_addr>>8)&0xFF,
|
|
(sub.IP_inet_addr)&0xFF, sub.port);
|
|
printf("SID [%s]\n", sub.sid);
|
|
printf("CallBack %s\n", sub.callback_url);
|
|
printf("Timeout [%d]\n", (int)sub.TimeOut);
|
|
#endif
|
|
|
|
if (sub.TimeOut == 0)
|
|
sub.TimeOut = MAX_SUB_TIMEOUT;
|
|
|
|
if (sub.sid[0] == 0)
|
|
{ //Subscribe
|
|
if (sub.callback_url[0] == 0) {
|
|
Send412PreconditionFailed(h);
|
|
return;
|
|
}
|
|
|
|
new_sub = UPnPTryToSubscribe(h, &sub);
|
|
if (new_sub == NULL) {
|
|
Send412TooManySubscribers(h);
|
|
return;
|
|
}
|
|
|
|
if (h->subscribe_list->EventCallBack)
|
|
h->subscribe_list->EventCallBack(new_sub);
|
|
}
|
|
else
|
|
{ // Renewal subscription
|
|
struct upnp_subscription_element *e;
|
|
struct upnp_subscription_element *next;
|
|
unsigned char count=0;
|
|
|
|
for(e = h->subscribe_list->subscription_head.lh_first; e != NULL; )
|
|
{
|
|
next = e->entries.le_next;
|
|
if(strcmp(e->sid, sub.sid) == 0)
|
|
{
|
|
count++;
|
|
if (BuildSubscribeResponse(h, &sub) != UPNP_E_SUCCESS) {
|
|
LIST_REMOVE(e, entries);
|
|
h->subscribe_list->total_subscription--;
|
|
free(e);
|
|
syslog(LOG_ERR, "UPnPProcessSUBSCRIBE : renew failed!");
|
|
return;
|
|
}
|
|
else {
|
|
//WPS2DOTX
|
|
//e->subscription_timeout = h->subscribe_list->subscription_timeout;
|
|
e->subscription_timeout = (int)sub.TimeOut;
|
|
|
|
e->eventID = UPNP_EVENT_RENEWAL_COMPLETE;
|
|
}
|
|
syslog(LOG_INFO, "Renewal subscription: total_subscription [%d]",
|
|
(int)h->subscribe_list->total_subscription);
|
|
if (h->subscribe_list->EventCallBack){
|
|
h->subscribe_list->EventCallBack(e);
|
|
if(e->wscdReNewState == 1){
|
|
Send412PreconditionFailed(h);
|
|
e->wscdReNewState = 0;
|
|
}
|
|
}
|
|
}
|
|
e = next;
|
|
}
|
|
if (count == 0) {
|
|
Send412PreconditionFailed(h);
|
|
syslog(LOG_ERR, "UPnPProcessSUBSCRIBE[renew] : Could not find the sid[%s]!", sub.sid);
|
|
if (new_sub == NULL) {
|
|
new_sub = (struct upnp_subscription_element *)malloc(sizeof(struct upnp_subscription_element));
|
|
if (new_sub == NULL)
|
|
return;
|
|
memset(new_sub, 0, sizeof(struct upnp_subscription_element));
|
|
new_sub->eventID = UPNP_EVENT_RENEWAL_COMPLETE;
|
|
memcpy(new_sub->sid, sub.sid, strlen(sub.sid));
|
|
if (h->subscribe_list->EventCallBack)
|
|
h->subscribe_list->EventCallBack(new_sub);
|
|
free(new_sub);
|
|
}
|
|
else
|
|
syslog(LOG_ERR, "UPnPProcessSUBSCRIBE[renew] : Could not allocate buffer for new_sub!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void UPnPProcessUNSUBSCRIBE(struct upnphttp * h)
|
|
{
|
|
struct process_upnp_subscription sub;
|
|
struct upnp_subscription_element *e;
|
|
struct upnp_subscription_element *next;
|
|
int ret;
|
|
unsigned char count=0;
|
|
|
|
memset(&sub, 0, sizeof(struct process_upnp_subscription));
|
|
ret = ParseSUBSCRIBEPacket(h, &sub);
|
|
if (ret != UPNP_E_SUCCESS) {
|
|
Send412PreconditionFailed(h);
|
|
return;
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
printf("IP [%s]\n", sub.IP);
|
|
printf("Host [%d.%d.%d.%d:%d]\n", (sub.IP_inet_addr>>24)&0xFF,
|
|
(sub.IP_inet_addr>>16)&0xFF,
|
|
(sub.IP_inet_addr>>8)&0xFF,
|
|
(sub.IP_inet_addr)&0xFF, sub.port);
|
|
printf("SID [%s]\n", sub.sid);
|
|
printf("CallBack %s\n", sub.callback_url);
|
|
printf("Timeout [%d]\n", (int)sub.TimeOut);
|
|
#endif
|
|
|
|
if (sub.sid[0] == 0) {
|
|
Send412PreconditionFailed(h);
|
|
return;
|
|
}
|
|
|
|
for(e = h->subscribe_list->subscription_head.lh_first; e != NULL; )
|
|
{
|
|
next = e->entries.le_next;
|
|
if(strcmp(e->sid, sub.sid) == 0)
|
|
{
|
|
count++;
|
|
LIST_REMOVE(e, entries);
|
|
BuildUnSubscribeResponse(h);
|
|
h->subscribe_list->total_subscription--;
|
|
syslog(LOG_INFO, "UNSUBSCRIBE: total_subscription [%d]",
|
|
(int)h->subscribe_list->total_subscription);
|
|
|
|
e->eventID = UPNP_EVENT_UNSUBSCRIBE_COMPLETE;
|
|
if (h->subscribe_list->EventCallBack)
|
|
h->subscribe_list->EventCallBack(e);
|
|
free(e);
|
|
}
|
|
e = next;
|
|
}
|
|
|
|
if (count == 0) {
|
|
BuildUnSubscribeResponse(h);
|
|
syslog(LOG_ERR, "UPnPProcessUNSUBSCRIBE : Could not find the sid!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parse and process Http Query
|
|
* called once all the HTTP headers have been received. */
|
|
static void ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
|
{
|
|
char HttpCommand[16];
|
|
char HttpUrl[128];
|
|
char * HttpVer;
|
|
char * p;
|
|
int i;
|
|
p = h->req_buf;
|
|
if(!p)
|
|
return;
|
|
for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
|
|
HttpCommand[i] = *(p++);
|
|
HttpCommand[i] = '\0';
|
|
while(*p==' ')
|
|
p++;
|
|
for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
|
|
HttpUrl[i] = *(p++);
|
|
HttpUrl[i] = '\0';
|
|
while(*p==' ')
|
|
p++;
|
|
HttpVer = h->HttpVer;
|
|
for(i = 0; i<15 && *p != '\r'; i++)
|
|
HttpVer[i] = *(p++);
|
|
HttpVer[i] = '\0';
|
|
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
|
|
HttpCommand, HttpUrl, HttpVer);
|
|
ParseHttpHeaders(h);
|
|
if(strcmp("POST", HttpCommand) == 0)
|
|
{
|
|
h->req_command = EPost;
|
|
ProcessHTTPPOST_upnphttp(h);
|
|
}
|
|
else if(strcmp("GET", HttpCommand) == 0)
|
|
{
|
|
h->req_command = EGet;
|
|
|
|
if (strncasecmp((char *)h->req_buf, "GET /HNAP", 9) == 0) {
|
|
i = 0;
|
|
int len;
|
|
while(h->soapMethods[i].methodName)
|
|
{
|
|
len = strlen(h->soapMethods[i].methodName);
|
|
if(strncmp("GetDeviceSettings", h->soapMethods[i].methodName, len) == 0)
|
|
{
|
|
h->soapMethods[i].methodImpl(h);
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
|
|
Send404(h);
|
|
return;
|
|
}
|
|
i = 0;
|
|
if (h->sendDesc) {
|
|
while(h->sendDesc[i].DescName)
|
|
{
|
|
if(strcmp(h->sendDesc[i].DescName, HttpUrl) == 0)
|
|
{
|
|
sendXMLdesc(h, h->sendDesc[i].sendDescImpl);
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
|
|
Send404(h);
|
|
}
|
|
else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
|
|
{
|
|
//printf("<<--------------------\n%s\n------------------->>\n", h->req_buf);
|
|
UPnPProcessSUBSCRIBE(h);
|
|
}
|
|
else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
|
|
{
|
|
//printf("<<--------------------\n%s\n------------------->>\n", h->req_buf);
|
|
UPnPProcessUNSUBSCRIBE(h);
|
|
}
|
|
else
|
|
{
|
|
syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
|
|
Send501(h);
|
|
}
|
|
}
|
|
|
|
|
|
void Process_upnphttp(struct upnphttp * h)
|
|
{
|
|
char *buf=NULL;
|
|
int n;
|
|
|
|
if(!h)
|
|
return;
|
|
|
|
buf = (char *) malloc(2048);
|
|
if (buf == NULL) {
|
|
syslog(LOG_ERR, "Process_upnphttp: out of memory!");
|
|
return;
|
|
}
|
|
memset(buf, 0, 2048);
|
|
|
|
switch(h->state)
|
|
{
|
|
case 0:
|
|
n = recv(h->socket, buf, 2048, 0);
|
|
if(n<0)
|
|
{
|
|
syslog(LOG_ERR, "recv (state0): %m");
|
|
h->state = 100;
|
|
}
|
|
else if(n==0)
|
|
{
|
|
syslog(LOG_WARNING, "connection closed inexpectedly");
|
|
h->state = 100;
|
|
}
|
|
else
|
|
{
|
|
const char * endheaders;
|
|
/*printf("== PACKET RECEIVED (%d bytes) ==\n", n);
|
|
fwrite(buf, 1, n, stdout); // debug
|
|
printf("== END OF PACKET RECEIVED ==\n");*/
|
|
/* if 1st arg of realloc() is null,
|
|
* realloc behaves the same as malloc() */
|
|
//h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
|
|
h->req_buf = (char *)realloc(h->req_buf, 2048); //Brad modify for HNAP, bug fix
|
|
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" */
|
|
endheaders = findendheaders(h->req_buf, h->req_buflen);
|
|
if(endheaders)
|
|
{
|
|
h->req_contentoff = endheaders - h->req_buf + 4;
|
|
ProcessHttpQuery_upnphttp(h);
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
n = recv(h->socket, buf, 2048, 0);
|
|
if(n<0)
|
|
{
|
|
syslog(LOG_ERR, "recv (state1): %m");
|
|
h->state = 100;
|
|
}
|
|
else if(n==0)
|
|
{
|
|
syslog(LOG_WARNING, "connection closed inexpectedly");
|
|
h->state = 100;
|
|
}
|
|
else
|
|
{
|
|
/*fwrite(buf, 1, n, stdout);*/ /* debug */
|
|
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
|
|
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;
|
|
default:
|
|
syslog(LOG_WARNING, "unexpected state (%d)", h->state);
|
|
}
|
|
|
|
free(buf);
|
|
}
|
|
|
|
static const char httpresphead[] =
|
|
"%s %d %s\r\n"
|
|
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
|
"Connection: close\r\n"
|
|
"Content-Length: %d\r\n"
|
|
"Server: " MINIUPNPD_SERVER_STRING "\r\n"
|
|
"Ext:\r\n"
|
|
"\r\n";
|
|
/*
|
|
"<?xml version=\"1.0\"?>\n"
|
|
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
|
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
"<s:Body>"
|
|
|
|
"</s:Body>"
|
|
"</s:Envelope>";
|
|
*/
|
|
/* with response code and response message
|
|
* also allocate enough memory */
|
|
void
|
|
BuildHeader_upnphttp(struct upnphttp * h, int respcode,
|
|
const char * respmsg,
|
|
int bodylen)
|
|
{
|
|
int templen;
|
|
if(!h->res_buf)
|
|
{
|
|
templen = sizeof(httpresphead) + 64 + bodylen;
|
|
h->res_buf = (char *)malloc(templen);
|
|
memset(h->res_buf, 0, templen);
|
|
h->res_buf_alloclen = templen;
|
|
}
|
|
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
|
|
httpresphead, h->HttpVer,
|
|
respcode, respmsg, bodylen);
|
|
if(h->res_buf_alloclen < (h->res_buflen + bodylen))
|
|
{
|
|
h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
|
|
memset(h->res_buf, 0, (h->res_buflen + bodylen));
|
|
h->res_buf_alloclen = h->res_buflen + bodylen;
|
|
}
|
|
}
|
|
|
|
void
|
|
BuildResp2_upnphttp(struct upnphttp * h, int respcode,
|
|
const char * respmsg,
|
|
const char * body, int bodylen)
|
|
{
|
|
BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
|
|
memcpy(h->res_buf + h->res_buflen, body, bodylen);
|
|
h->res_buflen += bodylen;
|
|
}
|
|
|
|
/* responding 200 OK ! */
|
|
void BuildResp_upnphttp(struct upnphttp * h,
|
|
const char * body, int bodylen)
|
|
{
|
|
BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
|
|
}
|
|
|
|
void SendResp_upnphttp(struct upnphttp * h)
|
|
{
|
|
int n;
|
|
|
|
n = ReliableSend(h->socket, h->res_buf, h->res_buflen);
|
|
if (n != h->res_buflen)
|
|
{
|
|
syslog(LOG_ERR, "SendResp_upnphttp: %d bytes sent (out of %d)",
|
|
n, h->res_buflen);
|
|
}
|
|
}
|
|
|
|
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
|
|
|
|
/* encode 3 8-bit binary bytes as 4 '6-bit' characters */
|
|
static void ILibencodeblock( unsigned char in[3], unsigned char out[4], int len )
|
|
{
|
|
out[0] = cb64[ in[0] >> 2 ];
|
|
out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
|
|
out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
|
|
out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
|
|
}
|
|
|
|
/*! \fn ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output)
|
|
\brief Base64 encode a stream adding padding and line breaks as per spec.
|
|
\par
|
|
\b Note: The encoded stream must be freed
|
|
\param input The stream to encode
|
|
\param inputlen The length of \a input
|
|
\param output The encoded stream
|
|
\returns The length of the encoded stream
|
|
*/
|
|
int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output)
|
|
{
|
|
unsigned char* out=NULL;
|
|
unsigned char* in;
|
|
|
|
*output = (unsigned char*)malloc(((inputlen * 4) / 3) + 5);
|
|
out = *output;
|
|
if (out == NULL)
|
|
return 0;
|
|
in = input;
|
|
|
|
if (input == NULL || inputlen == 0)
|
|
{
|
|
*output = NULL;
|
|
return 0;
|
|
}
|
|
|
|
while ((in+3) <= (input+inputlen))
|
|
{
|
|
ILibencodeblock(in, out, 3);
|
|
in += 3;
|
|
out += 4;
|
|
}
|
|
if ((input+inputlen)-in == 1)
|
|
{
|
|
ILibencodeblock(in, out, 1);
|
|
out += 4;
|
|
}
|
|
else
|
|
if ((input+inputlen)-in == 2)
|
|
{
|
|
ILibencodeblock(in, out, 2);
|
|
out += 4;
|
|
}
|
|
*out = 0;
|
|
|
|
return (int)(out-*output);
|
|
}
|
|
|
|
/* Decode 4 '6-bit' characters into 3 8-bit binary bytes */
|
|
static void ILibdecodeblock( unsigned char in[4], unsigned char out[3] )
|
|
{
|
|
out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
|
|
out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
|
|
out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
|
|
}
|
|
|
|
/*! \fn ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output)
|
|
\brief Decode a base64 encoded stream discarding padding, line breaks and noise
|
|
\par
|
|
\b Note: The decoded stream must be freed
|
|
\param input The stream to decode
|
|
\param inputlen The length of \a input
|
|
\param output The decoded stream
|
|
\returns The length of the decoded stream
|
|
*/
|
|
int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output)
|
|
{
|
|
unsigned char* inptr;
|
|
unsigned char* out=NULL;
|
|
unsigned char v;
|
|
unsigned char in[4];
|
|
int i, len;
|
|
|
|
if (input == NULL || inputlen == 0)
|
|
{
|
|
*output = NULL;
|
|
return 0;
|
|
}
|
|
|
|
*output = (unsigned char*)malloc(((inputlen * 3) / 4) + 4);
|
|
out = *output;
|
|
if (out == NULL)
|
|
return 0;
|
|
inptr = input;
|
|
|
|
while( inptr <= (input+inputlen) )
|
|
{
|
|
for( len = 0, i = 0; i < 4 && inptr <= (input+inputlen); i++ )
|
|
{
|
|
v = 0;
|
|
while( inptr <= (input+inputlen) && v == 0 ) {
|
|
v = (unsigned char) *inptr;
|
|
inptr++;
|
|
v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
|
|
if( v ) {
|
|
v = (unsigned char) ((v == '$') ? 0 : v - 61);
|
|
}
|
|
}
|
|
if( inptr <= (input+inputlen) ) {
|
|
len++;
|
|
if( v ) {
|
|
in[ i ] = (unsigned char) (v - 1);
|
|
}
|
|
}
|
|
else {
|
|
in[i] = 0;
|
|
}
|
|
}
|
|
if( len )
|
|
{
|
|
ILibdecodeblock( in, out );
|
|
out += len-1;
|
|
}
|
|
}
|
|
*out = 0;
|
|
return (int)(out-*output);
|
|
}
|
|
|
|
int OpenAndConfUNIXSocket(const char *file_path)
|
|
{
|
|
int s, len;
|
|
struct sockaddr_un local;
|
|
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
perror("UNIXSocket");
|
|
return -1;
|
|
}
|
|
|
|
local.sun_family = AF_UNIX;
|
|
strcpy(local.sun_path, file_path);
|
|
unlink(local.sun_path);
|
|
//len = strlen(local.sun_path) + sizeof(local.sun_family);
|
|
len = sizeof(struct sockaddr_un);
|
|
if (bind(s, (struct sockaddr *)&local, len) == -1) {
|
|
perror("UNIXSocket bind");
|
|
return -1;
|
|
}
|
|
|
|
if (listen(s, 5) == -1) {
|
|
perror("UNIXSocket listen");
|
|
return -1;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
int CreateUnixSocket(const char *function_name,
|
|
const char *file_path,
|
|
const int time_out)
|
|
{
|
|
struct sockaddr_un remote;
|
|
struct timeval tv;
|
|
int len, s;
|
|
|
|
if (file_path == NULL)
|
|
return -1;
|
|
|
|
if (time_out < 0)
|
|
return -1;
|
|
|
|
// Inter Process Communication
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
tv.tv_sec = time_out;
|
|
tv.tv_usec = 0;
|
|
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)) < 0)
|
|
{
|
|
if (function_name) {
|
|
syslog(LOG_WARNING, "%s : setsockopt(unix_socket, SO_RCVTIMEO): %m", function_name);
|
|
}
|
|
else {
|
|
syslog(LOG_WARNING, "setsockopt(unix_socket, SO_RCVTIMEO): %m");
|
|
}
|
|
}
|
|
|
|
remote.sun_family = AF_UNIX;
|
|
strcpy(remote.sun_path, file_path);
|
|
//len = strlen(remote.sun_path) + sizeof(remote.sun_family);
|
|
len = sizeof(struct sockaddr_un);
|
|
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
|
|
close(s);
|
|
return -1;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int UnixSocketSendAndReceive(const char *function_name,
|
|
const char *file_path,
|
|
const int time_out,
|
|
const char *in, char *out, const int out_len)
|
|
{
|
|
int s=-1, t, in_len, ret=-1;
|
|
|
|
if (file_path == NULL || in == NULL || out == NULL || out_len < 1)
|
|
return -1;
|
|
|
|
s = CreateUnixSocket(function_name, file_path, time_out);
|
|
if (s == -1) {
|
|
if (function_name)
|
|
syslog(LOG_ERR, "%s : CreateUnixSocket failed", function_name);
|
|
goto finish;
|
|
}
|
|
|
|
in_len = strlen(in);
|
|
if (ReliableSend(s, in, in_len) != in_len) {
|
|
if (function_name) {
|
|
syslog(LOG_ERR, "%s : Unix socket send: %m", function_name);
|
|
}
|
|
goto finish;
|
|
}
|
|
if ((t = recv(s, out, out_len, 0)) > 0) {
|
|
out[t] = '\0';
|
|
ret = 0;
|
|
}
|
|
else {
|
|
if (t < 0) {
|
|
if (function_name) {
|
|
syslog(LOG_ERR, "%s : Unix socket recv: %m", function_name);
|
|
}
|
|
}
|
|
else {
|
|
if (function_name) {
|
|
syslog(LOG_WARNING, "%s : Server closed connection: %m", function_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
finish:
|
|
if (s >= 0)
|
|
close(s);
|
|
return ret;
|
|
}
|