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

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

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


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