Diff for /embedaddon/miniupnpd/upnphttp.c between versions 1.1.1.2 and 1.1.1.3

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

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>