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 : }
|