Tor  0.4.7.0-alpha-dev
proto_http.c
Go to the documentation of this file.
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
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 }
int buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
Definition: buffers.c:815
size_t buf_datalen(const buf_t *buf)
Definition: buffers.c:394
int buf_peek_startswith(const buf_t *buf, const char *cmd)
Definition: buffers.c:834
int buf_get_bytes(buf_t *buf, char *string, size_t string_len)
Definition: buffers.c:637
void buf_pullup(buf_t *buf, size_t bytes, const char **head_out, size_t *len_out)
Definition: buffers.c:211
Header file for buffers.c.
#define LD_HTTP
Definition: log.h:76
#define LD_PROTOCOL
Definition: log.h:72
#define tor_free(p)
Definition: malloc.h:52
Master header file for Tor-specific functionality.
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next)
Definition: parse_int.c:110
int fetch_from_buf_http(buf_t *buf, char **headers_out, size_t max_headerlen, char **body_out, size_t *body_used, size_t max_bodylen, int force_complete)
Definition: proto_http.c:50
int peek_buf_has_http_command(const buf_t *buf)
Definition: proto_http.c:19
STATIC int buf_http_find_content_length(const char *headers, size_t headerlen, size_t *result_out)
Definition: proto_http.c:139
Header for proto_http.c.
#define STATIC
Definition: testsupport.h:32
#define tor_assert(expr)
Definition: util_bug.h:102
int tor_strisspace(const char *s)
Definition: util_string.c:184