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 }