tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

proto_http.c (5694B)


      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 peek_buf_has_http_command(const buf_t *buf)
     20 {
     21  if (buf_peek_startswith(buf, "CONNECT ") ||
     22      buf_peek_startswith(buf, "DELETE ") ||
     23      buf_peek_startswith(buf, "GET ") ||
     24      buf_peek_startswith(buf, "POST ") ||
     25      buf_peek_startswith(buf, "PUT " ))
     26    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 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  const char *headers;
     56  size_t headerlen, bodylen, contentlen=0;
     57  int crlf_offset;
     58  int r;
     59 
     60  if (buf_datalen(buf) == 0)
     61    return 0;
     62 
     63  crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4);
     64  if (crlf_offset > (int)max_headerlen ||
     65      (crlf_offset < 0 && buf_datalen(buf) > max_headerlen)) {
     66    log_debug(LD_HTTP,"headers too long.");
     67    return -1;
     68  } else if (crlf_offset < 0) {
     69    log_debug(LD_HTTP,"headers not all here yet.");
     70    return 0;
     71  }
     72  /* Okay, we have a full header.  Make sure it all appears in the first
     73   * chunk. */
     74  headerlen = crlf_offset + 4;
     75  size_t headers_in_chunk = 0;
     76  buf_pullup(buf, headerlen, &headers, &headers_in_chunk);
     77 
     78  bodylen = buf_datalen(buf) - headerlen;
     79  log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen);
     80 
     81  if (max_headerlen <= headerlen) {
     82    log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.",
     83             (int)headerlen, (int)max_headerlen-1);
     84    return -1;
     85  }
     86  if (max_bodylen <= bodylen) {
     87    log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.",
     88             (int)bodylen, (int)max_bodylen-1);
     89    return -1;
     90  }
     91 
     92  r = buf_http_find_content_length(headers, headerlen, &contentlen);
     93  if (r == -1) {
     94    log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe "
     95             "someone is trying to crash us.");
     96    return -1;
     97  } else if (r == 1) {
     98    /* if content-length is malformed, then our body length is 0. fine. */
     99    log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
    100    if (bodylen < contentlen) {
    101      if (!force_complete) {
    102        log_debug(LD_HTTP,"body not all here yet.");
    103        return 0; /* not all there yet */
    104      }
    105    }
    106    if (bodylen > contentlen) {
    107      bodylen = contentlen;
    108      log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
    109    }
    110  } else {
    111    tor_assert(r == 0);
    112    /* Leave bodylen alone */
    113  }
    114 
    115  /* all happy. copy into the appropriate places, and return 1 */
    116  if (headers_out) {
    117    *headers_out = tor_malloc(headerlen+1);
    118    buf_get_bytes(buf, *headers_out, headerlen);
    119    (*headers_out)[headerlen] = 0; /* NUL terminate it */
    120  }
    121  if (body_out) {
    122    tor_assert(body_used);
    123    *body_used = bodylen;
    124    *body_out = tor_malloc(bodylen+1);
    125    buf_get_bytes(buf, *body_out, bodylen);
    126    (*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 buf_http_find_content_length(const char *headers, size_t headerlen,
    140                             size_t *result_out)
    141 {
    142  const char *p, *newline;
    143  char *len_str, *eos=NULL;
    144  size_t remaining, result;
    145  int ok;
    146  *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  p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
    151  if (p == NULL)
    152    return 0;
    153 
    154  tor_assert(p >= headers && p < headers+headerlen);
    155  remaining = (headers+headerlen)-p;
    156  p += strlen(CONTENT_LENGTH);
    157  remaining -= strlen(CONTENT_LENGTH);
    158 
    159  newline = memchr(p, '\n', remaining);
    160  if (newline == NULL)
    161    return -1;
    162 
    163  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  result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos);
    167  if (eos && !tor_strisspace(eos)) {
    168    ok = 0;
    169  } else {
    170    *result_out = result;
    171  }
    172  tor_free(len_str);
    173 
    174  return ok ? 1 : -1;
    175 }