LCOV - code coverage report
Current view: top level - core/proto - proto_http.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 77 77 100.0 %
Date: 2021-11-24 03:28:48 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * @file proto_http.c
       9             :  * @brief Parse a subset of the HTTP protocol.
      10             :  **/
      11             : 
      12             : #define PROTO_HTTP_PRIVATE
      13             : #include "core/or/or.h"
      14             : #include "lib/buf/buffers.h"
      15             : #include "core/proto/proto_http.h"
      16             : 
      17             : /** Return true if <b>cmd</b> looks like a HTTP (proxy) request. */
      18             : int
      19           7 : peek_buf_has_http_command(const buf_t *buf)
      20             : {
      21          14 :   if (buf_peek_startswith(buf, "CONNECT ") ||
      22          14 :       buf_peek_startswith(buf, "DELETE ") ||
      23          11 :       buf_peek_startswith(buf, "GET ") ||
      24           8 :       buf_peek_startswith(buf, "POST ") ||
      25           4 :       buf_peek_startswith(buf, "PUT " ))
      26           3 :     return 1;
      27             :   return 0;
      28             : }
      29             : 
      30             : /** There is a (possibly incomplete) http statement on <b>buf</b>, of the
      31             :  * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.)
      32             :  * If a) the headers include a Content-Length field and all bytes in
      33             :  * the body are present, or b) there's no Content-Length field and
      34             :  * all headers are present, then:
      35             :  *
      36             :  *  - strdup headers into <b>*headers_out</b>, and NUL-terminate it.
      37             :  *  - memdup body into <b>*body_out</b>, and NUL-terminate it.
      38             :  *  - Then remove them from <b>buf</b>, and return 1.
      39             :  *
      40             :  *  - If headers or body is NULL, discard that part of the buf.
      41             :  *  - If a headers or body doesn't fit in the arg, return -1.
      42             :  *  (We ensure that the headers or body don't exceed max len,
      43             :  *   _even if_ we're planning to discard them.)
      44             :  *  - If force_complete is true, then succeed even if not all of the
      45             :  *    content has arrived.
      46             :  *
      47             :  * Else, change nothing and return 0.
      48             :  */
      49             : int
      50          81 : fetch_from_buf_http(buf_t *buf,
      51             :                     char **headers_out, size_t max_headerlen,
      52             :                     char **body_out, size_t *body_used, size_t max_bodylen,
      53             :                     int force_complete)
      54             : {
      55          81 :   const char *headers;
      56          81 :   size_t headerlen, bodylen, contentlen=0;
      57          81 :   int crlf_offset;
      58          81 :   int r;
      59             : 
      60          81 :   if (buf_datalen(buf) == 0)
      61             :     return 0;
      62             : 
      63          80 :   crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4);
      64          80 :   if (crlf_offset > (int)max_headerlen ||
      65           2 :       (crlf_offset < 0 && buf_datalen(buf) > max_headerlen)) {
      66           2 :     log_debug(LD_HTTP,"headers too long.");
      67           2 :     return -1;
      68          78 :   } else if (crlf_offset < 0) {
      69           1 :     log_debug(LD_HTTP,"headers not all here yet.");
      70           1 :     return 0;
      71             :   }
      72             :   /* Okay, we have a full header.  Make sure it all appears in the first
      73             :    * chunk. */
      74          77 :   headerlen = crlf_offset + 4;
      75          77 :   size_t headers_in_chunk = 0;
      76          77 :   buf_pullup(buf, headerlen, &headers, &headers_in_chunk);
      77             : 
      78          77 :   bodylen = buf_datalen(buf) - headerlen;
      79          77 :   log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen);
      80             : 
      81          77 :   if (max_headerlen <= headerlen) {
      82           1 :     log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.",
      83             :              (int)headerlen, (int)max_headerlen-1);
      84           1 :     return -1;
      85             :   }
      86          76 :   if (max_bodylen <= bodylen) {
      87           3 :     log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.",
      88             :              (int)bodylen, (int)max_bodylen-1);
      89           3 :     return -1;
      90             :   }
      91             : 
      92          73 :   r = buf_http_find_content_length(headers, headerlen, &contentlen);
      93          73 :   if (r == -1) {
      94           1 :     log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe "
      95             :              "someone is trying to crash us.");
      96           1 :     return -1;
      97          72 :   } else if (r == 1) {
      98             :     /* if content-length is malformed, then our body length is 0. fine. */
      99          25 :     log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
     100          25 :     if (bodylen < contentlen) {
     101           1 :       if (!force_complete) {
     102           1 :         log_debug(LD_HTTP,"body not all here yet.");
     103           1 :         return 0; /* not all there yet */
     104             :       }
     105             :     }
     106          24 :     if (bodylen > contentlen) {
     107           1 :       bodylen = contentlen;
     108           1 :       log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
     109             :     }
     110             :   } else {
     111          47 :     tor_assert(r == 0);
     112             :     /* Leave bodylen alone */
     113             :   }
     114             : 
     115             :   /* all happy. copy into the appropriate places, and return 1 */
     116          71 :   if (headers_out) {
     117          71 :     *headers_out = tor_malloc(headerlen+1);
     118          71 :     buf_get_bytes(buf, *headers_out, headerlen);
     119          71 :     (*headers_out)[headerlen] = 0; /* NUL terminate it */
     120             :   }
     121          71 :   if (body_out) {
     122          39 :     tor_assert(body_used);
     123          39 :     *body_used = bodylen;
     124          39 :     *body_out = tor_malloc(bodylen+1);
     125          39 :     buf_get_bytes(buf, *body_out, bodylen);
     126          39 :     (*body_out)[bodylen] = 0; /* NUL terminate it */
     127             :   }
     128             :   return 1;
     129             : }
     130             : 
     131             : /**
     132             :  * Scan the HTTP headers in the <b>headerlen</b>-byte memory range at
     133             :  * <b>headers</b>, looking for a "Content-Length" header.  Try to set
     134             :  * *<b>result_out</b> to the numeric value of that header if possible.
     135             :  * Return -1 if the header was malformed, 0 if it was missing, and 1 if
     136             :  * it was present and well-formed.
     137             :  */
     138             : STATIC int
     139          86 : buf_http_find_content_length(const char *headers, size_t headerlen,
     140             :                              size_t *result_out)
     141             : {
     142          86 :   const char *p, *newline;
     143          86 :   char *len_str, *eos=NULL;
     144          86 :   size_t remaining, result;
     145          86 :   int ok;
     146          86 :   *result_out = 0; /* The caller shouldn't look at this unless the
     147             :                     * return value is 1, but let's prevent confusion */
     148             : 
     149             : #define CONTENT_LENGTH "\r\nContent-Length: "
     150          86 :   p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
     151          86 :   if (p == NULL)
     152             :     return 0;
     153             : 
     154          37 :   tor_assert(p >= headers && p < headers+headerlen);
     155          37 :   remaining = (headers+headerlen)-p;
     156          37 :   p += strlen(CONTENT_LENGTH);
     157          37 :   remaining -= strlen(CONTENT_LENGTH);
     158             : 
     159          37 :   newline = memchr(p, '\n', remaining);
     160          37 :   if (newline == NULL)
     161             :     return -1;
     162             : 
     163          35 :   len_str = tor_memdup_nulterm(p, newline-p);
     164             :   /* We limit the size to INT_MAX because other parts of the buffer.c
     165             :    * code don't like buffers to be any bigger than that. */
     166          35 :   result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos);
     167          35 :   if (eos && !tor_strisspace(eos)) {
     168           3 :     ok = 0;
     169             :   } else {
     170          32 :     *result_out = result;
     171             :   }
     172          35 :   tor_free(len_str);
     173             : 
     174          35 :   return ok ? 1 : -1;
     175             : }

Generated by: LCOV version 1.14