Diff for /embedaddon/lighttpd/src/request.c between versions 1.1.1.2 and 1.1.1.3

version 1.1.1.2, 2014/06/15 20:20:06 version 1.1.1.3, 2016/11/02 10:35:00
Line 1 Line 1
   #include "first.h"
   
 #include "request.h"  #include "request.h"
 #include "keyvalue.h"  #include "keyvalue.h"
 #include "log.h"  #include "log.h"
Line 10 Line 12
 #include <stdio.h>  #include <stdio.h>
 #include <ctype.h>  #include <ctype.h>
   
static int request_check_hostname(server *srv, connection *con, buffer *host) {static int request_check_hostname(buffer *host) {
         enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;          enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
         size_t i;          size_t i;
         int label_len = 0;          int label_len = 0;
        size_t host_len;        size_t host_len, hostport_len;
         char *colon;          char *colon;
         int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */          int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */
         int level = 0;          int level = 0;
   
         UNUSED(srv);  
         UNUSED(con);  
   
         /*          /*
          *       hostport      = host [ ":" port ]           *       hostport      = host [ ":" port ]
          *       host          = hostname | IPv4address | IPv6address           *       host          = hostname | IPv4address | IPv6address
Line 33  static int request_check_hostname(server *srv, connect Line 32  static int request_check_hostname(server *srv, connect
          *       port          = *digit           *       port          = *digit
          */           */
   
         /* no Host: */  
         if (!host || host->used == 0) return 0;  
   
         host_len = host->used - 1;  
   
         /* IPv6 adress */          /* IPv6 adress */
         if (host->ptr[0] == '[') {          if (host->ptr[0] == '[') {
                 char *c = host->ptr + 1;                  char *c = host->ptr + 1;
Line 74  static int request_check_hostname(server *srv, connect Line 68  static int request_check_hostname(server *srv, connect
                 return 0;                  return 0;
         }          }
   
           hostport_len = host_len = buffer_string_length(host);
   
         if (NULL != (colon = memchr(host->ptr, ':', host_len))) {          if (NULL != (colon = memchr(host->ptr, ':', host_len))) {
                 char *c = colon + 1;                  char *c = colon + 1;
   
Line 92  static int request_check_hostname(server *srv, connect Line 88  static int request_check_hostname(server *srv, connect
         /* if the hostname ends in a "." strip it */          /* if the hostname ends in a "." strip it */
         if (host->ptr[host_len-1] == '.') {          if (host->ptr[host_len-1] == '.') {
                 /* shift port info one left */                  /* shift port info one left */
                if (NULL != colon) memmove(colon-1, colon, host->used - host_len);                if (NULL != colon) memmove(colon-1, colon, hostport_len - host_len);
                else host->ptr[host_len-1] = '\0';                buffer_string_set_length(host, --hostport_len);
                host_len -= 1;                if (--host_len == 0) return -1;
                host->used -= 1; 
         }          }
   
         if (host_len == 0) return -1;  
   
         /* scan from the right and skip the \0 */          /* scan from the right and skip the \0 */
         for (i = host_len; i-- > 0; ) {          for (i = host_len; i-- > 0; ) {
Line 208  static int request_check_hostname(server *srv, connect Line 202  static int request_check_hostname(server *srv, connect
         return 0;          return 0;
 }  }
   
   int http_request_host_normalize(buffer *b) {
       /*
        * check for and canonicalize numeric IP address and portnum (optional)
        * (IP address may be followed by ":portnum" (optional))
        * - IPv6: "[...]"
        * - IPv4: "x.x.x.x"
        * - IPv4: 12345678   (32-bit decimal number)
        * - IPv4: 012345678  (32-bit octal number)
        * - IPv4: 0x12345678 (32-bit hex number)
        *
        * allow any chars (except ':' and '\0' and stray '[' or ']')
        *   (other code may check chars more strictly or more pedantically)
        * ':'  delimits (optional) port at end of string
        * "[]" wraps IPv6 address literal
        * '\0' should have been rejected earlier were it present
        *
        * any chars includes, but is not limited to:
        * - allow '-' any where, even at beginning of word
        *     (security caution: might be confused for cmd flag if passed to shell)
        * - allow all-digit TLDs
        *     (might be mistaken for IPv4 addr by inet_aton()
        *      unless non-digits appear in subdomain)
        */
   
       /* Note: not using getaddrinfo() since it does not support "[]" around IPv6
        * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings.
        * Not using inet_pton() (when available) on IPv4 for similar reasons. */
   
       const char * const p = b->ptr;
       const size_t blen = buffer_string_length(b);
       long port = 0;
   
       if (*p != '[') {
           char * const colon = (char *)memchr(p, ':', blen);
           if (colon) {
               if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/
               if (colon[1] != '\0') {
                   char *e;
                   port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/
                   if (0 < port && port <= USHRT_MAX && *e == '\0') {
                       /* valid port */
                   } else {
                       return -1;
                   }
               } /*(else ignore stray colon at string end)*/
               buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/
           }
   
           if (light_isdigit(*p)) {
               /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/
               struct in_addr addr;
             #if defined(HAVE_INET_ATON) /*(Windows does not provide inet_aton())*/
               if (0 != inet_aton(p, &addr))
             #else
               if ((addr.s_addr = inet_addr(p)) != INADDR_NONE)
             #endif
               {
                 #if defined(HAVE_INET_PTON)/*(expect inet_ntop() if inet_pton())*/
                  #ifndef INET_ADDRSTRLEN
                  #define INET_ADDRSTRLEN 16
                  #endif
                   char buf[INET_ADDRSTRLEN];
                   inet_ntop(AF_INET, (const void *)&addr, buf, sizeof(buf));
                   buffer_copy_string(b, buf);
                 #else
                   buffer_copy_string(b, inet_ntoa(addr)); /*(not thread-safe)*/
                 #endif
               }
           }
       } else { /* IPv6 addr */
         #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
   
           struct in6_addr addr;
           char *bracket = b->ptr+blen-1;
           char *percent = strchr(b->ptr+1, '%');
           size_t len;
           int rc;
           char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/
           if (blen <= 2) return -1; /*(invalid "[]")*/
           if (*bracket != ']') {
               bracket = (char *)memchr(b->ptr+1, ']', blen-1);
               if (NULL == bracket || bracket[1] != ':'  || bracket - b->ptr == 1){
                  return -1;
               }
               if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/
                   char *e;
                   port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/
                   if (0 < port && port <= USHRT_MAX && *e == '\0') {
                       /* valid port */
                   } else {
                       return -1;
                   }
               }
           }
   
           *bracket = '\0';/*(terminate IPv6 string)*/
           if (percent) *percent = '\0'; /*(remove %interface from address)*/
           rc = inet_pton(AF_INET6, b->ptr+1, &addr);
           if (percent) *percent = '%'; /*(restore %interface)*/
           *bracket = ']'; /*(restore bracket)*/
           if (1 != rc) return -1;
   
           inet_ntop(AF_INET6,(const void *)&addr, buf, sizeof(buf));
           len = strlen(buf);
           if (percent) {
               if (percent > bracket) return -1;
               if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1;
               memcpy(buf+len, percent, (size_t)(bracket - percent));
               len += (size_t)(bracket - percent);
           }
           buffer_string_set_length(b, 1); /* truncate after '[' */
           buffer_append_string_len(b, buf, len);
           buffer_append_string_len(b, CONST_STR_LEN("]"));
   
         #else
   
           return -1;
   
         #endif
       }
   
       if (port) {
           buffer_append_string_len(b, CONST_STR_LEN(":"));
           buffer_append_int(b, (int)port);
       }
   
       return 0;
   }
   
 #if 0  #if 0
 #define DUMP_HEADER  #define DUMP_HEADER
 #endif  #endif
   
 static int http_request_split_value(array *vals, buffer *b) {  static int http_request_split_value(array *vals, buffer *b) {
        size_t i;        size_t i, len;
         int state = 0;          int state = 0;
   
         const char *current;          const char *current;
Line 226  static int http_request_split_value(array *vals, buffe Line 349  static int http_request_split_value(array *vals, buffe
          * into a array (more or less a explode() incl. striping of whitespaces           * into a array (more or less a explode() incl. striping of whitespaces
          */           */
   
        if (b->used == 0) return 0;        if (buffer_string_is_empty(b)) return 0;
   
         current = b->ptr;          current = b->ptr;
        for (i =  0; i < b->used; ++i, ++current) {        len = buffer_string_length(b);
         for (i =  0; i <= len; ++i, ++current) {
                 data_string *ds;                  data_string *ds;
   
                 switch (state) {                  switch (state) {
Line 297  int http_request_parse(server *srv, connection *con) { Line 421  int http_request_parse(server *srv, connection *con) {
         int line = 0;          int line = 0;
   
         int request_line_stage = 0;          int request_line_stage = 0;
        size_t i, first;        size_t i, first, ilen;
   
         int done = 0;          int done = 0;
           const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT);
   
         /*          /*
          * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"           * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
Line 310  int http_request_parse(server *srv, connection *con) { Line 435  int http_request_parse(server *srv, connection *con) {
         if (con->conf.log_request_header) {          if (con->conf.log_request_header) {
                 log_error_write(srv, __FILE__, __LINE__, "sdsdSb",                  log_error_write(srv, __FILE__, __LINE__, "sdsdSb",
                                 "fd:", con->fd,                                  "fd:", con->fd,
                                "request-len:", con->request.request->used,                                "request-len:", buffer_string_length(con->request.request),
                                 "\n", con->request.request);                                  "\n", con->request.request);
         }          }
   
Line 319  int http_request_parse(server *srv, connection *con) { Line 444  int http_request_parse(server *srv, connection *con) {
             con->request.request->ptr[1] == '\n') {              con->request.request->ptr[1] == '\n') {
                 /* we are in keep-alive and might get \r\n after a previous POST request.*/                  /* we are in keep-alive and might get \r\n after a previous POST request.*/
   
                buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2);                buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, buffer_string_length(con->request.request) - 2);
         } else {          } else {
                 /* fill the local request buffer */                  /* fill the local request buffer */
                buffer_copy_string_buffer(con->parse_request, con->request.request);                buffer_copy_buffer(con->parse_request, con->request.request);
         }          }
   
         keep_alive_set = 0;          keep_alive_set = 0;
Line 334  int http_request_parse(server *srv, connection *con) { Line 459  int http_request_parse(server *srv, connection *con) {
          *           *
          * <method> <uri> <protocol>\r\n           * <method> <uri> <protocol>\r\n
          * */           * */
        for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) {        ilen = buffer_string_length(con->parse_request);
                char *cur = con->parse_request->ptr + i;        for (i = 0, first = 0; i < ilen && line == 0; i++) {
                switch(con->parse_request->ptr[i]) {
                switch(*cur) { 
                 case '\r':                  case '\r':
                         if (con->parse_request->ptr[i+1] == '\n') {                          if (con->parse_request->ptr[i+1] == '\n') {
                                 http_method_t r;                                  http_method_t r;
                                 char *nuri = NULL;                                  char *nuri = NULL;
                                size_t j;                                size_t j, jlen;
   
                                 /* \r\n -> \0\0 */                                  /* \r\n -> \0\0 */
                                 con->parse_request->ptr[i] = '\0';                                  con->parse_request->ptr[i] = '\0';
Line 476  int http_request_parse(server *srv, connection *con) { Line 600  int http_request_parse(server *srv, connection *con) {
                                 }                                  }
   
                                 /* check uri for invalid characters */                                  /* check uri for invalid characters */
                                for (j = 0; j < con->request.uri->used - 1; j++) {                                jlen = buffer_string_length(con->request.uri);
                                        if (!request_uri_is_valid_char(con->request.uri->ptr[j])) {                                if (http_header_strict) {
                                                unsigned char buf[2];                                        for (j = 0; j < jlen && request_uri_is_valid_char(con->request.uri->ptr[j]); j++) ;
                                 } else {
                                         char *z = memchr(con->request.uri->ptr, '\0', jlen);
                                         j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr);
                                 }
                                 if (j < jlen) {
                                                 con->http_status = 400;                                                  con->http_status = 400;
                                                 con->keep_alive = 0;                                                  con->keep_alive = 0;
   
                                                 if (srv->srvconf.log_request_header_on_error) {                                                  if (srv->srvconf.log_request_header_on_error) {
                                                           unsigned char buf[2];
                                                         buf[0] = con->request.uri->ptr[j];                                                          buf[0] = con->request.uri->ptr[j];
                                                         buf[1] = '\0';                                                          buf[1] = '\0';
   
Line 505  int http_request_parse(server *srv, connection *con) { Line 635  int http_request_parse(server *srv, connection *con) {
                                                 }                                                  }
   
                                                 return 0;                                                  return 0;
                                         }  
                                 }                                  }
   
                                buffer_copy_string_buffer(con->request.orig_uri, con->request.uri);                                buffer_copy_buffer(con->request.orig_uri, con->request.uri);
   
                                 con->http_status = 0;                                  con->http_status = 0;
   
Line 551  int http_request_parse(server *srv, connection *con) { Line 680  int http_request_parse(server *srv, connection *con) {
   
         in_folding = 0;          in_folding = 0;
   
        if (con->request.uri->used == 1) {        if (buffer_string_is_empty(con->request.uri)) {
                 con->http_status = 400;                  con->http_status = 400;
                 con->response.keep_alive = 0;                  con->response.keep_alive = 0;
                 con->keep_alive = 0;                  con->keep_alive = 0;
Line 579  int http_request_parse(server *srv, connection *con) { Line 708  int http_request_parse(server *srv, connection *con) {
                 con->request.http_host = ds->value;                  con->request.http_host = ds->value;
         }          }
   
        for (; i < con->parse_request->used && !done; i++) {        for (; i <= ilen && !done; i++) {
                 char *cur = con->parse_request->ptr + i;                  char *cur = con->parse_request->ptr + i;
   
                 if (is_key) {                  if (is_key) {
Line 703  int http_request_parse(server *srv, connection *con) { Line 832  int http_request_parse(server *srv, connection *con) {
                                 }                                  }
                                 break;                                  break;
                         default:                          default:
                                if (*cur < 32 || ((unsigned char)*cur) >= 127) {                                if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') {
                                         con->http_status = 400;                                          con->http_status = 400;
                                         con->keep_alive = 0;                                          con->keep_alive = 0;
                                         con->response.keep_alive = 0;                                          con->response.keep_alive = 0;
Line 825  int http_request_parse(server *srv, connection *con) { Line 954  int http_request_parse(server *srv, connection *con) {
                                                         } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {                                                          } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
                                                                 char *err;                                                                  char *err;
                                                                 unsigned long int r;                                                                  unsigned long int r;
                                                                size_t j;                                                                size_t j, jlen;
   
                                                                 if (con_length_set) {                                                                  if (con_length_set) {
                                                                         con->http_status = 400;                                                                          con->http_status = 400;
Line 842  int http_request_parse(server *srv, connection *con) { Line 971  int http_request_parse(server *srv, connection *con) {
                                                                         return 0;                                                                          return 0;
                                                                 }                                                                  }
   
                                                                if (ds->value->used == 0) SEGFAULT();                                                                jlen = buffer_string_length(ds->value);
                                                                for (j = 0; j < jlen; j++) {
                                                                for (j = 0; j < ds->value->used - 1; j++) { 
                                                                         char c = ds->value->ptr[j];                                                                          char c = ds->value->ptr[j];
                                                                         if (!isdigit((unsigned char)c)) {                                                                          if (!isdigit((unsigned char)c)) {
                                                                                 log_error_write(srv, __FILE__, __LINE__, "sbs",                                                                                  log_error_write(srv, __FILE__, __LINE__, "sbs",
Line 1027  int http_request_parse(server *srv, connection *con) { Line 1155  int http_request_parse(server *srv, connection *con) {
                         case '\t':                          case '\t':
                                 /* strip leading WS */                                  /* strip leading WS */
                                 if (value == cur) value = cur+1;                                  if (value == cur) value = cur+1;
                                /* fallthrough */                                break;
                         default:                          default:
                                if (*cur >= 0 && *cur < 32 && *cur != '\t') {                                if (http_header_strict ? (*cur >= 0 && *cur < 32) : *cur == '\0') {
                                         if (srv->srvconf.log_request_header_on_error) {                                          if (srv->srvconf.log_request_header_on_error) {
                                                 log_error_write(srv, __FILE__, __LINE__, "sds",                                                  log_error_write(srv, __FILE__, __LINE__, "sds",
                                                                 "invalid char in header", (int)*cur, "-> 400");                                                                  "invalid char in header", (int)*cur, "-> 400");
Line 1061  int http_request_parse(server *srv, connection *con) { Line 1189  int http_request_parse(server *srv, connection *con) {
   
                 /* RFC 2616, 14.23 */                  /* RFC 2616, 14.23 */
                 if (con->request.http_host == NULL ||                  if (con->request.http_host == NULL ||
                    buffer_is_empty(con->request.http_host)) {                    buffer_string_is_empty(con->request.http_host)) {
                         con->http_status = 400;                          con->http_status = 400;
                         con->response.keep_alive = 0;                          con->response.keep_alive = 0;
                         con->keep_alive = 0;                          con->keep_alive = 0;
Line 1086  int http_request_parse(server *srv, connection *con) { Line 1214  int http_request_parse(server *srv, connection *con) {
         }          }
   
         /* check hostname field if it is set */          /* check hostname field if it is set */
        if (NULL != con->request.http_host &&        if (!buffer_is_empty(con->request.http_host) &&
            0 != request_check_hostname(srv, con, con->request.http_host)) {            (((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_STRICT) &&
               0 != request_check_hostname(con->request.http_host))
              || ((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) &&
                  0 != http_request_host_normalize(con->request.http_host)))) {
   
                 if (srv->srvconf.log_request_header_on_error) {                  if (srv->srvconf.log_request_header_on_error) {
                         log_error_write(srv, __FILE__, __LINE__, "s",                          log_error_write(srv, __FILE__, __LINE__, "s",
Line 1132  int http_request_parse(server *srv, connection *con) { Line 1263  int http_request_parse(server *srv, connection *con) {
                 }                  }
                 break;                  break;
         default:          default:
                /* the may have a content-length */                /* require Content-Length if request contains request body */
                 if (array_get_element(con->request.headers, "Transfer-Encoding")) {
                         /* presence of Transfer-Encoding in request headers requires "chunked"
                          * be final encoding in HTTP/1.1.  Return 411 Length Required as
                          * lighttpd does not support request input transfer-encodings */
                         con->keep_alive = 0;
                         con->http_status = 411; /* 411 Length Required */
                         return 0;
                 }
                 break;                  break;
         }          }
   
Line 1149  int http_request_parse(server *srv, connection *con) { Line 1288  int http_request_parse(server *srv, connection *con) {
                         return 0;                          return 0;
                 }                  }
   
                 /* divide by 1024 as srvconf.max_request_size is in kBytes */  
                 if (srv->srvconf.max_request_size != 0 &&  
                     (con->request.content_length >> 10) > srv->srvconf.max_request_size) {  
                         /* the request body itself is larger then  
                          * our our max_request_size  
                          */  
   
                         con->http_status = 413;  
                         con->keep_alive = 0;  
   
                         log_error_write(srv, __FILE__, __LINE__, "sos",  
                                         "request-size too long:", (off_t) con->request.content_length, "-> 413");  
                         return 0;  
                 }  
   
   
                 /* we have content */                  /* we have content */
                 if (con->request.content_length != 0) {                  if (con->request.content_length != 0) {
                         return 1;                          return 1;
Line 1177  int http_request_parse(server *srv, connection *con) { Line 1300  int http_request_parse(server *srv, connection *con) {
 int http_request_header_finished(server *srv, connection *con) {  int http_request_header_finished(server *srv, connection *con) {
         UNUSED(srv);          UNUSED(srv);
   
        if (con->request.request->used < 5) return 0;        if (buffer_string_length(con->request.request) < 4) return 0;
   
        if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1;        if (0 == memcmp(con->request.request->ptr + buffer_string_length(con->request.request) - 4, CONST_STR_LEN("\r\n\r\n"))) return 1;
         if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;          if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;
   
         return 0;          return 0;

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


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