Changeset 895

Show
Ignore:
Timestamp:
08/21/07 20:24:56 (1 year ago)
Author:
adefacc
Message:

improved header parsing and added support for HTTP 501 and HTTP 505 error responses, now HTTP/0.9 requests should be properly understood and of course refused (because they are unsecure and deprecated)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cherokee/trunk/ChangeLog

    r894 r895  
     12007-08-21  A.D.F  <adefacc@tin.it> 
     2 
     3        * cherokee/macros.h 
     4          - added LBS macro (HTTP linear blank space); 
     5 
     6        * cherokee/header.h 
     7          - parse_request_first_line() + parse_response_first_line() + 
     8            cherokee_header_parse(): 
     9            - added formal parameter "error_code" (HTTP error code); 
     10 
     11        * cherokee/header.c 
     12          - parse_request_first_line() + parse_response_first_line() + 
     13            cherokee_header_parse(): 
     14            - added formal parameter "error_code" (HTTP error code); 
     15          - parse_request_first_line(): 
     16            - added support for: 
     17              - HTTP 505 version not supported; 
     18              - HTTP 501 (method) not implemented; 
     19            - partially rewritten the HTTP request parser in order 
     20              to be more standard conformant; 
     21          - cherokee_header_has_header(), lowered minimum HTTP request length 
     22            in order to handle properly HTTP/0.9 requests too 
     23            (which are refused with error: HTTP 505 Version Not Implemented); 
     24 
     25        * cherokee/connection.c, 
     26          cherokee/downloader.c 
     27          - added formal parameter (HTTP) error_code. 
     28 
    1292007-08-13  A.D.F  <adefacc@tin.it> 
    230 
  • cherokee/trunk/cherokee/connection.c

    r893 r895  
    13681368{ 
    13691369        ret_t    ret; 
     1370        cherokee_http_t error_code = http_bad_request; 
    13701371        char    *host, *upgrade, *cnt; 
    13711372        cuint_t  host_len, upgrade_len, cnt_len; 
     
    13731374        /* Header parsing 
    13741375         */ 
    1375         ret = cherokee_header_parse (&conn->header, &conn->incoming_header, header_type_request); 
    1376         if (ret < ret_ok) { 
     1376        ret = cherokee_header_parse (&conn->header, &conn->incoming_header, header_type_request, &error_code); 
     1377        if (unlikely (ret < ret_ok)) 
    13771378                goto error; 
    1378         } 
    13791379 
    13801380        /* Maybe read the POST data 
     
    14871487 
    14881488error: 
    1489         conn->error_code = http_bad_request
     1489        conn->error_code = error_code
    14901490        return ret_error; 
    14911491} 
  • cherokee/trunk/cherokee/downloader.c

    r889 r895  
    270270        size_t              readed = 0; 
    271271        cherokee_socket_t  *sock   = &downloader->socket; 
     272        cherokee_http_t     error_code = http_bad_request; 
    272273 
    273274        ret = cherokee_socket_bufread (sock, &downloader->reply_header, DEFAULT_RECV_SIZE, &readed); 
     
    303304                 */ 
    304305                ret = cherokee_header_parse (downloader->header, 
    305                                              &downloader->reply_header, 
    306                                              header_type_response);              
     306                                             &downloader->reply_header, 
     307                                             header_type_response, 
     308                                             &error_code 
     309                );               
    307310                if (unlikely(ret != ret_ok)) return ret_error; 
    308311 
  • cherokee/trunk/cherokee/header.c

    r863 r895  
    194194 
    195195static ret_t 
    196 parse_response_first_line (cherokee_header_t *hdr, cherokee_buffer_t *buf, char **next_pos
     196parse_response_first_line (cherokee_header_t *hdr, cherokee_buffer_t *buf, char **next_pos, cherokee_http_t *error_code
    197197{ 
    198198        char *line  = buf->buf; 
     
    200200        char  tmp[4]; 
    201201        char *end; 
    202  
    203         end = strchr (line, CHR_CR); 
    204         if (end == NULL) { 
     202        size_t  len; 
     203 
     204        /* NOTE: here we deal only with HTTP/1.0 or higher. 
     205         */ 
     206        len = strcspn(line, CRLF); 
     207        end = &line[len]; 
     208 
     209        /* Some security checks 
     210         */ 
     211        if (len < 14 || buf->len < 14) { 
    205212                return ret_error; 
    206213        } 
    207214 
    208         /* Some security checks 
    209          */ 
    210         if (buf->len < 14) { 
    211                 return ret_error; 
    212         } 
    213  
    214215        /* Return next line 
    215216         */ 
    216         *next_pos = end + 2; 
     217        switch (*end) { 
     218                case CHR_CR: 
     219                        if (end[1] != CHR_LF) 
     220                                return ret_error; 
     221                        *next_pos = end + 2; 
     222                        break; 
     223                case CHR_LF: 
     224                        *next_pos = end + 1; 
     225                        break; 
     226                default: 
     227                        return ret_error; 
     228        } 
    217229 
    218230        /* Example: 
    219231         * HTTP/1.0 403 Forbidden 
    220232         */ 
    221         if (unlikely(! cmp_str(begin, "HTTP/"))) { 
     233        if (unlikely(! cmp_str(begin, "HTTP/1."))) { 
     234                /* TODO: improve parser 
     235                 * (if ! "HTTP/" then leave default http_bad_request). 
     236                 */ 
     237                *error_code = http_version_not_supported; 
    222238                return ret_error; 
    223239        } 
    224240 
     241        if (unlikely(! isdigit(begin[7]))) { 
     242                return ret_error; 
     243        } 
     244 
    225245        /* Get the HTTP version 
    226246         */ 
    227247        switch (begin[7]) { 
    228         case '1': 
    229                 hdr->version = http_version_11;  
    230                 break; 
    231248        case '0': 
    232249                hdr->version = http_version_10;  
    233250                break; 
    234         case '9': 
    235                 hdr->version = http_version_09;  
    236                 break; 
     251        case '1': 
    237252        default: 
    238                 return ret_error; 
     253                hdr->version = http_version_11;  
     254                break; 
    239255        } 
    240256 
    241257        /* Read the response code 
     258         * TODO: skip spaces properly (usually there is only one blank). 
    242259         */ 
    243260        memcpy (tmp, begin+9, 3); 
     
    324341 
    325342static ret_t 
    326 parse_request_first_line (cherokee_header_t *hdr, cherokee_buffer_t *buf, char **next_pos
     343parse_request_first_line (cherokee_header_t *hdr, cherokee_buffer_t *buf, char **next_pos, cherokee_http_t *error_code
    327344{ 
    328345        ret_t  ret; 
    329346        char  *line  = buf->buf; 
    330347        char  *begin = buf->buf; 
     348        char  *begin_ver = buf->buf; 
    331349        char  *end; 
    332350        char  *ptr; 
    333351        char  *restore; 
    334352        char  chr_end; 
     353        size_t len = 0; 
     354        size_t len_ver = 0; 
    335355 
    336356        /* Basic security check. The shortest possible request 
    337          * "GET / HTTP/1.0" is 14 characters long.. 
    338          */ 
    339         if (unlikely(buf->len < 14)) { 
     357         * "GET / HTTP/1.0" is 14 characters long but we want 
     358         * to reply to HTTP/0.9 requests too which don't have 
     359         * HTTP version. 
     360         */ 
     361        if (unlikely(buf->len < 6)) { 
    340362                return ret_error; 
    341363        } 
     
    352374                /* Return begin of next line  
    353375                 */ 
     376                len = (size_t) (end - line); 
    354377                *next_pos = end + 1; 
    355378        } else { 
    356379                /* Return begin of next line  
    357380                 */ 
     381                len = (size_t) (end - line); 
    358382                *next_pos = end + 2; 
    359383        } 
     
    362386        restore = end; 
    363387 
     388        /* Check shortest string length "GET /" 
     389         */ 
     390        if (len < 5) 
     391                goto error; 
     392 
    364393        /* Get the method 
    365394         */ 
    366395        ret = parse_method (hdr, line, &begin); 
    367         if (unlikely (ret != ret_ok)) 
     396        if (unlikely (ret != ret_ok)) { 
     397                *error_code = http_not_implemented; 
    368398                goto error; 
    369  
    370         /* Get the protocol version 
    371          */      
    372         switch (end[-1]) { 
    373         case '1': 
    374                 if (unlikely (! cmp_str (end-8, "HTTP/1.1"))) 
    375                         goto error; 
    376                 hdr->version = http_version_11;  
    377                 break; 
    378         case '0': 
    379                 if (unlikely (! cmp_str (end-8, "HTTP/1.0"))) 
    380                         goto error; 
    381                 hdr->version = http_version_10;  
    382                 break; 
    383         case '9': 
    384                 if (unlikely (! cmp_str (end-8, "HTTP/0.9"))) 
    385                         goto error; 
    386                 hdr->version = http_version_09;  
    387                 break; 
    388         default: 
     399        } 
     400 
     401        /* Skip other blank spaces between method and URI. 
     402         */ 
     403        begin += strspn (begin, LBS); 
     404 
     405        /* Length of URI. 
     406         */ 
     407        len = strcspn (begin, LBS); 
     408        if (len == 0) 
    389409                goto error; 
    390         } 
     410 
     411        if (begin[len] == '\0') { 
     412                /* HTTP/0.9 has no HTTP version string and 
     413                 * it is not supported by this server. 
     414                 */ 
     415                *error_code = http_version_not_supported; 
     416                goto error; 
     417        } 
     418 
     419        begin_ver = &begin[len]; 
     420        begin_ver += strspn (begin_ver, LBS); 
     421        len_ver = (size_t) (end - begin_ver); 
     422 
     423        /* Verify "HTTP/x.x" pattern. 
     424         */ 
     425        if (len_ver != 8 || 
     426            !isdigit(begin_ver[5]) || 
     427            begin_ver[6] != '.' || 
     428            !isdigit(begin_ver[7]) || 
     429            !cmp_str (begin_ver, "HTTP/")) { 
     430                goto error; 
     431        } 
     432 
     433        /* Get the protocol version. 
     434         */ 
     435        if (begin_ver[5] != '1' || begin_ver[7] > '1') { 
     436                *error_code = http_version_not_supported; 
     437                goto error; 
     438        } 
     439 
     440        hdr->version = (begin_ver[7] == '1' ? http_version_11 : http_version_10);  
    391441 
    392442        /* Skip the HTTP version string: " HTTP/x.y" 
    393443         */ 
    394         end -= 9
     444        end = &begin[len]
    395445 
    396446        /* Look for the QueryString 
    397447         */ 
    398         hdr->request_args_len = end - begin; 
     448        hdr->request_args_len = len; 
    399449        ptr = strchr (begin, '?'); 
    400450 
    401451        if (ptr) { 
    402452                end = ptr; 
    403                 hdr->query_string_off = ++ptr - buf->buf
    404                 hdr->query_string_len = (unsigned long) strchr(ptr, ' ') - (unsigned long) ptr
     453                hdr->query_string_off = (off_t) (++ptr - buf->buf)
     454                hdr->query_string_len = (cint_t) (&begin[len] - ptr)
    405455        } else { 
    406456                hdr->query_string_len = 0; 
     
    409459        /* Get the request 
    410460         */ 
    411         hdr->request_off = begin - buf->buf
    412         hdr->request_len = end - begin
     461        hdr->request_off = (off_t)  (begin - buf->buf)
     462        hdr->request_len = (cint_t) (end - begin)
    413463 
    414464        /* Check if the request is a full URL 
     
    418468                char   *dir; 
    419469                char   *host = begin + 7; 
    420  
     470                char   end_chr = *end; 
     471 
     472                if (host[0] == '/' || host[0] == '.') 
     473                        goto error; 
     474 
     475                *end = '\0'; 
    421476                dir = strchr (host, '/'); 
     477                *end = end_chr; 
    422478                if (dir == NULL) 
    423479                        goto error; 
     
    474530 
    475531ret_t  
    476 cherokee_header_parse (cherokee_header_t *hdr, cherokee_buffer_t *buffer, cherokee_type_header_t type
     532cherokee_header_parse (cherokee_header_t *hdr, cherokee_buffer_t *buffer, cherokee_type_header_t type, cherokee_http_t *error_code
    477533{ 
    478534        ret_t  ret; 
     
    483539        char  *header_end; 
    484540        char  chr_header_end; 
     541 
     542        /* Set default error code. 
     543         */ 
     544        *error_code = http_bad_request; 
    485545 
    486546        /* Check the buffer content 
     
    529589                 * GET /icons/compressed.png HTTP/1.1CRLF 
    530590                 */ 
    531                 ret = parse_request_first_line (hdr, buffer, &begin); 
     591                ret = parse_request_first_line (hdr, buffer, &begin, error_code); 
    532592                if (unlikely(ret < ret_ok)) { 
    533593                        PRINT_DEBUG ("ERROR: Failed to parse header_type_request:\n===\n%s===\n", buffer->buf); 
     
    538598 
    539599        case header_type_response: 
    540                 ret = parse_response_first_line (hdr, buffer, &begin); 
     600                ret = parse_response_first_line (hdr, buffer, &begin, error_code); 
    541601                if (unlikely(ret < ret_ok)) { 
    542602                        PRINT_DEBUG ("ERROR: Failed to parse header_type_response:\n===\n%s===\n", buffer->buf); 
     
    923983 
    924984        /* Do we have enough information ? 
     985         * len("GET /" LF_LF) = 7                   (HTTP/0.9) 
    925986         * len("GET /" CRLF_CRLF) = 9               (HTTP/0.9) 
    926987         * len("GET / HTTP/1.0" CRLF_CRLF) = 18     (HTTP/1.x) 
    927988         */ 
    928         if (unlikely (buffer->len < 18)) { 
     989        if (unlikely (buffer->len < 7)) { 
    929990                return ret_not_found; 
    930991        } 
  • cherokee/trunk/cherokee/header.h

    r841 r895  
    8282 
    8383ret_t cherokee_header_clean               (cherokee_header_t *hdr); 
    84 ret_t cherokee_header_parse               (cherokee_header_t *hdr, cherokee_buffer_t *buffer, cherokee_type_header_t type); 
     84ret_t cherokee_header_parse               (cherokee_header_t *hdr, cherokee_buffer_t *buffer, cherokee_type_header_t type, cherokee_http_t *error_code); 
    8585ret_t cherokee_header_has_header          (cherokee_header_t *hdr, cherokee_buffer_t *buffer, int tail_len); 
    8686 
  • cherokee/trunk/cherokee/macros.h

    r883 r895  
    140140#define CRLF      "\r\n"        /* EOH (End Of Header Line) */ 
    141141#define LWS       " \t\r\n"     /* HTTP linear white space */ 
     142#define LBS       " \t"         /* HTTP linear blank space */ 
    142143#define CHR_CR    '\r'          /* Carriage return */ 
    143144#define CHR_LF    '\n'          /* Line feed (new line) */