| #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]; | |
| memset(port, 0, 10); | |
| memcpy(port, (char *)start, end-start); | |
| 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; | |
| } |