1 : /*
2 : * Copyright (C) 2004, 2005, 2006 James Antill
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, write to the Free Software
16 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 : *
18 : * email: james@and.org
19 : */
20 : /* main HTTP parsing routines, currently mainly implements server portions */
21 :
22 : #define EX_UTILS_NO_USE_INIT 1
23 : #define EX_UTILS_NO_USE_EXIT 1
24 : #define EX_UTILS_NO_USE_LIMIT 1
25 : #define EX_UTILS_NO_USE_BLOCK 1
26 : #define EX_UTILS_NO_USE_GET 1
27 : #define EX_UTILS_NO_USE_PUT 1
28 : #define EX_UTILS_RET_FAIL 1
29 : #include "ex_utils.h"
30 :
31 : #include "mk.h"
32 :
33 : #include "vlg.h"
34 :
35 : #define HTTPD_HAVE_GLOBAL_OPTS 1
36 : #include "httpd.h"
37 : #include "httpd_policy.h"
38 :
39 : #if ! COMPILE_DEBUG
40 : # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
41 : # define HTTP_CONF_SAFE_PRINT_REQ TRUE
42 : #else
43 : # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
44 : # define HTTP_CONF_SAFE_PRINT_REQ FALSE
45 : #endif
46 : #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
47 :
48 : #define HTTPD_CONF_ZIP_LIMIT_MIN 8
49 :
50 : #define CLEN COMPILE_STRLEN
51 :
52 : /* is the cstr a prefix of the vstr */
53 : #define VPREFIX(vstr, p, l, cstr) \
54 : (((l) >= CLEN(cstr)) && \
55 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
56 : /* is the cstr a suffix of the vstr */
57 : #define VSUFFIX(vstr, p, l, cstr) \
58 : (((l) >= CLEN(cstr)) && \
59 : vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
60 :
61 : /* is the cstr a prefix of the vstr, no case */
62 : #define VIPREFIX(vstr, p, l, cstr) \
63 : (((l) >= CLEN(cstr)) && \
64 : vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
65 :
66 : /* for simplicity */
67 : #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
68 : #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
69 :
70 : #define HTTP__HDR_SET(req, h, p, l) do { \
71 : (req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
72 : (req)-> http_hdrs -> hdr_ ## h ->len = (l); \
73 : } while (FALSE)
74 : #define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
75 : (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
76 : (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
77 : } while (FALSE)
78 :
79 : #define HTTP__XTRA_HDR_INIT(x) do { \
80 : req-> x ## _vs1 = NULL; \
81 : req-> x ## _pos = 0; \
82 : req-> x ## _len = 0; \
83 : } while (FALSE)
84 :
85 : #include <syslog.h>
86 :
87 : static Vlg *vlg = NULL;
88 :
89 : void httpd_parse_init(Vlg *passed_vlg)
90 64 : {
91 32 : ASSERT(passed_vlg && !vlg);
92 64 : vlg = passed_vlg;
93 64 : }
94 :
95 : void httpd_parse_exit(void)
96 44 : {
97 22 : ASSERT(vlg);
98 44 : vlg = NULL;
99 44 : }
100 :
101 : /* HTTP crack -- Implied linear whitespace between tokens, note that it
102 : * is *LWS == *([CRLF] 1*(SP | HT)) */
103 : void http_parse_skip_lws(const Vstr_base *s1, size_t *pos, size_t *len)
104 111193 : {
105 111193 : size_t lws__len = 0;
106 :
107 55604 : ASSERT(s1 && pos && len);
108 :
109 : while (TRUE)
110 : {
111 227202 : if (VPREFIX(s1, *pos, *len, HTTP_EOL))
112 : {
113 4112 : *len -= CLEN(HTTP_EOL); *pos += CLEN(HTTP_EOL);
114 : }
115 218978 : else if (lws__len)
116 111193 : break;
117 :
118 111897 : if (!(lws__len = vstr_spn_cstr_chrs_fwd(s1, *pos, *len, HTTP_LWS)))
119 0 : break;
120 111897 : *len -= lws__len;
121 111897 : *pos += lws__len;
122 111897 : }
123 111193 : }
124 :
125 : /* might have been able to do it with string matches, but getting...
126 : * "HTTP/1.1" = OK
127 : * "HTTP/1.10" = OK
128 : * "HTTP/1.10000000000000" = BAD
129 : * ...seemed not as easy. It also seems like you have to accept...
130 : * "HTTP / 01 . 01" as "HTTP/1.1"
131 : */
132 : int http_parse_version(struct Con *con, struct Httpd_req_data *req)
133 39241 : {
134 39241 : Vstr_base *data = con->evnt->io_r;
135 39241 : size_t op_pos = VSTR_SECTS_NUM(req->sects, 3)->pos;
136 39241 : size_t op_len = VSTR_SECTS_NUM(req->sects, 3)->len;
137 39241 : unsigned int major = 0;
138 39241 : unsigned int minor = 0;
139 39241 : size_t num_len = 0;
140 : unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
141 39241 : VSTR_FLAG_PARSE_NUM_OVERFLOW);
142 :
143 39241 : if (!VPREFIX(data, op_pos, op_len, "HTTP"))
144 144 : HTTPD_ERR_MSG_RET(req, 400, "<PROTO>/Version", FALSE);
145 :
146 39097 : op_len -= CLEN("HTTP"); op_pos += CLEN("HTTP");
147 39097 : HTTP_SKIP_LWS(data, op_pos, op_len);
148 :
149 39097 : if (!VPREFIX(data, op_pos, op_len, "/"))
150 144 : HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
151 38953 : op_len -= CLEN("/"); op_pos += CLEN("/");
152 38953 : HTTP_SKIP_LWS(data, op_pos, op_len);
153 :
154 38953 : major = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
155 38953 : op_len -= num_len; op_pos += num_len;
156 38953 : HTTP_SKIP_LWS(data, op_pos, op_len);
157 :
158 38953 : if (!num_len || !VPREFIX(data, op_pos, op_len, "."))
159 432 : HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
160 :
161 38521 : op_len -= CLEN("."); op_pos += CLEN(".");
162 38521 : HTTP_SKIP_LWS(data, op_pos, op_len);
163 :
164 38521 : minor = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
165 38521 : op_len -= num_len; op_pos += num_len;
166 38521 : HTTP_SKIP_LWS(data, op_pos, op_len);
167 :
168 38521 : if (!num_len || op_len)
169 288 : HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
170 :
171 : if (0) { } /* not allowing HTTP/0.9 here */
172 38305 : else if ((major == 1) && (minor > 1))
173 144 : req->ver_1_x = TRUE;
174 54366 : else if ((major == 1) && (minor == 1))
175 32569 : req->ver_1_1 = TRUE;
176 5520 : else if ((major == 1) && (minor == 0))
177 : { /* do nothing */ }
178 : else
179 144 : HTTPD_ERR_MSG_RET(req, 505, "HTTP/Version", FALSE);
180 :
181 38089 : return (TRUE);
182 : }
183 :
184 : void http_parse_clear_hdrs(struct Httpd_req_data *req)
185 99661 : {
186 99661 : Vstr_base *tmp = req->http_hdrs->multi->combiner_store;
187 :
188 49167 : ASSERT(tmp);
189 :
190 99661 : HTTP__HDR_SET(req, ua, 0, 0);
191 99661 : HTTP__HDR_SET(req, referer, 0, 0);
192 :
193 99661 : HTTP__HDR_SET(req, authorization, 0, 0);
194 99661 : HTTP__HDR_SET(req, expect, 0, 0);
195 99661 : HTTP__HDR_SET(req, host, 0, 0);
196 99661 : req->http_host_port = 80;
197 99661 : HTTP__HDR_SET(req, if_modified_since, 0, 0);
198 99661 : HTTP__HDR_SET(req, if_range, 0, 0);
199 99661 : HTTP__HDR_SET(req, if_unmodified_since, 0, 0);
200 99661 : HTTP__HDR_SET(req, range, 0, 0);
201 99661 : HTTP__HDR_SET(req, x_moz, 0, 0);
202 :
203 99661 : vstr_del(tmp, 1, tmp->len);
204 99661 : HTTP__HDR_MULTI_SET(req, accept, 0, 0);
205 99661 : HTTP__HDR_MULTI_SET(req, accept_charset, 0, 0);
206 99661 : HTTP__HDR_MULTI_SET(req, accept_encoding, 0, 0);
207 99661 : HTTP__HDR_MULTI_SET(req, accept_language, 0, 0);
208 99661 : HTTP__HDR_MULTI_SET(req, connection, 0, 0);
209 99661 : HTTP__HDR_MULTI_SET(req, if_match, 0, 0);
210 99661 : HTTP__HDR_MULTI_SET(req, if_none_match, 0, 0);
211 :
212 99661 : if (req->xtra_content)
213 97754 : vstr_del(req->xtra_content, 1, req->xtra_content->len);
214 :
215 99661 : HTTP__XTRA_HDR_INIT(content_type);
216 99661 : HTTP__XTRA_HDR_INIT(content_disposition);
217 99661 : HTTP__XTRA_HDR_INIT(content_language);
218 99661 : HTTP__XTRA_HDR_INIT(content_location);
219 99661 : HTTP__XTRA_HDR_INIT(content_md5);
220 99661 : HTTP__XTRA_HDR_INIT(gzip_content_md5);
221 99661 : HTTP__XTRA_HDR_INIT(bzip2_content_md5);
222 99661 : HTTP__XTRA_HDR_INIT(cache_control);
223 99661 : HTTP__XTRA_HDR_INIT(etag);
224 99661 : HTTP__XTRA_HDR_INIT(expires);
225 99661 : HTTP__XTRA_HDR_INIT(link);
226 99661 : HTTP__XTRA_HDR_INIT(p3p);
227 99661 : HTTP__XTRA_HDR_INIT(ext_vary_a);
228 99661 : HTTP__XTRA_HDR_INIT(ext_vary_ac);
229 99661 : HTTP__XTRA_HDR_INIT(ext_vary_al);
230 99661 : }
231 :
232 : static void http__multi_hdr_fixup(Vstr_sect_node *hdr_ignore,
233 : Vstr_sect_node *hdr, size_t pos, size_t len)
234 8400 : {
235 8400 : if (hdr == hdr_ignore)
236 1200 : return;
237 :
238 7200 : if (hdr->pos <= pos)
239 7200 : return;
240 :
241 0 : hdr->pos += len;
242 : }
243 :
244 : static int http__multi_hdr_cp(Vstr_base *comb,
245 : Vstr_base *data, Vstr_sect_node *hdr)
246 3808 : {
247 3808 : size_t pos = comb->len + 1;
248 :
249 3808 : if (!hdr->len)
250 3264 : return (TRUE);
251 :
252 544 : if (!vstr_add_vstr(comb, comb->len,
253 : data, hdr->pos, hdr->len, VSTR_TYPE_ADD_BUF_PTR))
254 0 : return (FALSE);
255 :
256 544 : hdr->pos = pos;
257 :
258 544 : return (TRUE);
259 : }
260 :
261 : static int http__app_multi_hdr(Vstr_base *data, struct Http_hdrs *hdrs,
262 : Vstr_sect_node *hdr, size_t pos, size_t len)
263 24256 : {
264 24256 : Vstr_base *comb = hdrs->multi->comb;
265 :
266 12128 : ASSERT(comb);
267 :
268 12128 : ASSERT((hdr == hdrs->multi->hdr_accept) ||
269 : (hdr == hdrs->multi->hdr_accept_charset) ||
270 : (hdr == hdrs->multi->hdr_accept_encoding) ||
271 : (hdr == hdrs->multi->hdr_accept_language) ||
272 : (hdr == hdrs->multi->hdr_connection) ||
273 : (hdr == hdrs->multi->hdr_if_match) ||
274 : (hdr == hdrs->multi->hdr_if_none_match));
275 :
276 12128 : ASSERT((comb == data) || (comb == hdrs->multi->combiner_store));
277 :
278 24256 : if ((data == comb) && !hdr->pos)
279 : { /* Do the fast thing... */
280 22912 : hdr->pos = pos;
281 22912 : hdr->len = len;
282 22912 : return (TRUE);
283 : }
284 :
285 1344 : if (data == comb)
286 : { /* OK, so we have a crap request and need to JOIN multiple headers... */
287 544 : comb = hdrs->multi->comb = hdrs->multi->combiner_store;
288 :
289 544 : if (!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept) ||
290 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_charset) ||
291 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_encoding) ||
292 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_language) ||
293 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_connection) ||
294 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_match) ||
295 : !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_none_match) ||
296 : FALSE)
297 0 : return (FALSE);
298 : }
299 :
300 1344 : if (!hdr->pos)
301 : {
302 144 : hdr->pos = comb->len + 1;
303 144 : hdr->len = len;
304 144 : return (vstr_add_vstr(comb, comb->len,
305 : data, pos, len, VSTR_TYPE_ADD_BUF_PTR));
306 : }
307 :
308 : /* reverses the order, but that doesn't matter */
309 1200 : if (!vstr_add_cstr_ptr(comb, hdr->pos - 1, ","))
310 0 : return (FALSE);
311 1200 : if (!vstr_add_vstr(comb, hdr->pos - 1,
312 : data, pos, len, VSTR_TYPE_ADD_BUF_PTR))
313 0 : return (FALSE);
314 1200 : hdr->len += ++len;
315 :
316 : /* now need to "move" any hdrs after this one */
317 1200 : pos = hdr->pos - 1;
318 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept, pos, len);
319 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_charset, pos, len);
320 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_encoding, pos, len);
321 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_language, pos, len);
322 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_connection, pos, len);
323 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_match, pos, len);
324 1200 : http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_none_match, pos, len);
325 :
326 1200 : return (TRUE);
327 : }
328 :
329 : /* viprefix, with local knowledge */
330 : static int http__hdr_eq(struct Con *con, size_t pos, size_t len,
331 : const char *hdr, size_t hdr_len, size_t *hdr_val_pos)
332 641869 : {
333 641869 : Vstr_base *data = con->evnt->io_r;
334 :
335 320972 : ASSERT(CLEN(hdr) == hdr_len);
336 320972 : ASSERT(hdr[hdr_len - 1] == ':');
337 :
338 641869 : if (!con->policy->use_hdrs_non_spc)
339 385816 : --hdr_len;
340 :
341 641869 : if ((len < hdr_len) ||
342 : !vstr_cmp_case_buf_eq(data, pos, hdr_len, hdr, hdr_len))
343 569388 : return (FALSE);
344 72481 : len -= hdr_len; pos += hdr_len;
345 :
346 72481 : if (!con->policy->use_hdrs_non_spc)
347 : {
348 44432 : HTTP_SKIP_LWS(data, pos, len);
349 44432 : if (!len)
350 0 : return (FALSE);
351 :
352 44432 : if (vstr_export_chr(data, pos) != ':')
353 4752 : return (FALSE);
354 39680 : --len; ++pos;
355 : }
356 :
357 67729 : *hdr_val_pos = pos;
358 :
359 67729 : return (TRUE);
360 : }
361 :
362 : /* remove LWS from front and end... what a craptastic std. */
363 : static void http__hdr_fixup(Vstr_base *data, size_t *pos, size_t *len,
364 : size_t hdr_val_pos)
365 67265 : {
366 67265 : size_t tmp = 0;
367 :
368 67265 : *len -= hdr_val_pos - *pos; *pos += hdr_val_pos - *pos;
369 67265 : HTTP_SKIP_LWS(data, *pos, *len);
370 :
371 : /* hand coding for a HTTP_SKIP_LWS() going backwards... */
372 105706 : while ((tmp = vstr_spn_cstr_chrs_rev(data, *pos, *len, HTTP_LWS)))
373 : {
374 4816 : *len -= tmp;
375 :
376 4816 : if (VSUFFIX(data, *pos, *len, HTTP_EOL))
377 744 : *len -= CLEN(HTTP_EOL);
378 : }
379 67265 : }
380 :
381 : /* for single headers, multiple ones aren't allowed ...
382 : * we can do last one wins, or just error (erroring is more secure, see
383 : * HTTP Request smuggling) */
384 : #define HDR__EQ(x) http__hdr_eq(con, pos, len, x ":", CLEN(x ":"), &hdr_val_pos)
385 :
386 : #define HDR__EQ_SET(x, h) \
387 : else if (HDR__EQ(x)) do \
388 : { \
389 : if (req->policy->use_hdrs_no_x2 && http_hdrs-> hdr_ ## h ->pos) \
390 : HTTPD_ERR_MSG_RET(req, 400, "Double header " x, FALSE); \
391 : http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
392 : http_hdrs-> hdr_ ## h ->pos = pos; \
393 : http_hdrs-> hdr_ ## h ->len = len; \
394 : } while (FALSE)
395 : #define HDR__EQ_MULTI_SET(x, h) \
396 : else if (HDR__EQ(x)) do \
397 : { \
398 : http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
399 : if (!http__app_multi_hdr(data, http_hdrs, \
400 : http_hdrs->multi-> hdr_ ## h, pos, len)) \
401 : { \
402 : req->malloc_bad = TRUE; \
403 : HTTPD_ERR_MSG_RET(req, 500, "Memory failure", FALSE); \
404 : } \
405 : } while (FALSE)
406 : #define HDR__EQ_IGNORE(x, h) \
407 : else if (HDR__EQ(x)) do \
408 : { \
409 : if (req->policy->use_hdrs_no_x2 && got_ ## h) \
410 : HTTPD_ERR_MSG_RET(req, 400, "Double header " x, FALSE); \
411 : got_ ## h = TRUE; \
412 : } while (FALSE)
413 :
414 : int http_parse_hdrs(struct Con *con, Httpd_req_data *req)
415 38089 : {
416 38089 : Vstr_base *data = con->evnt->io_r;
417 38089 : struct Http_hdrs *http_hdrs = req->http_hdrs;
418 38089 : unsigned int num = 3; /* skip "method URI version" */
419 38089 : int got_content_length = FALSE;
420 38089 : int got_content_lang = FALSE;
421 38089 : int got_content_type = FALSE;
422 38089 : int got_transfer_encoding = FALSE;
423 :
424 145379 : while (++num <= req->sects->num)
425 : {
426 69873 : size_t pos = VSTR_SECTS_NUM(req->sects, num)->pos;
427 69873 : size_t len = VSTR_SECTS_NUM(req->sects, num)->len;
428 69873 : size_t hdr_val_pos = 0;
429 :
430 : if (0) { /* nothing */ }
431 69873 : HDR__EQ_SET("User-Agent", ua);
432 69713 : HDR__EQ_SET("Referer", referer);
433 69361 : HDR__EQ_SET("Authorization", authorization);
434 68113 : HDR__EQ_SET("Expect", expect);
435 67969 : HDR__EQ_SET("Host", host);
436 36232 : HDR__EQ_SET("If-Modified-Since", if_modified_since);
437 35568 : HDR__EQ_SET("If-Range", if_range);
438 34424 : HDR__EQ_SET("If-Unmodified-Since", if_unmodified_since);
439 33928 : HDR__EQ_SET("Range", range);
440 27728 : HDR__EQ_SET("X-Moz", x_moz);
441 :
442 : /* allow continuations over multiple headers... *sigh* */
443 27696 : HDR__EQ_MULTI_SET("Accept", accept);
444 22704 : HDR__EQ_MULTI_SET("Accept-Charset", accept_charset);
445 22704 : HDR__EQ_MULTI_SET("Accept-Encoding", accept_encoding);
446 16256 : HDR__EQ_MULTI_SET("Accept-Language", accept_language);
447 14336 : HDR__EQ_MULTI_SET("Connection", connection);
448 7240 : HDR__EQ_MULTI_SET("If-Match", if_match);
449 5560 : HDR__EQ_MULTI_SET("If-None-Match", if_none_match);
450 :
451 : /* allow a 0 (zero) length content-length, some clients do send these */
452 3440 : else if (HDR__EQ("Content-Length"))
453 : {
454 : unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
455 288 : VSTR_FLAG_PARSE_NUM_OVERFLOW);
456 288 : unsigned int num_val = 0;
457 288 : size_t num_len = 0;
458 :
459 288 : if (req->policy->use_hdrs_no_x2 && got_content_length)
460 0 : HTTPD_ERR_MSG_RET(req, 400, "Double header Content-Length", FALSE);
461 :
462 288 : got_content_length = TRUE;
463 288 : http__hdr_fixup(data, &pos, &len, hdr_val_pos);
464 :
465 288 : num_val = vstr_parse_uint(data, pos, len, num_flags, &num_len, NULL);
466 :
467 288 : if (num_len != len)
468 0 : HTTPD_ERR_MSG_RET(req, 400, "Content-Length is not a number", FALSE);
469 288 : if (num_val)
470 144 : HTTPD_ERR_MSG_RET(req, 413, "Content-Length is not zero", FALSE);
471 : }
472 3152 : HDR__EQ_IGNORE("Content-Type", content_type);
473 3008 : HDR__EQ_IGNORE("Content-Language", content_lang);
474 :
475 : /* in theory ,,identity;foo=bar;baz="zoom",, is ok ... who cares */
476 2864 : else if (HDR__EQ("Transfer-Encoding"))
477 : {
478 720 : if (req->policy->use_hdrs_no_x2 && got_transfer_encoding)
479 144 : HTTPD_ERR_MSG_RET(req, 400, "Double header Transfer-Encoding", FALSE);
480 576 : got_transfer_encoding = TRUE;
481 576 : http__hdr_fixup(data, &pos, &len, hdr_val_pos);
482 :
483 576 : if (!VEQ(data, pos, len, "identity")) /* 3.6 says 501 */
484 144 : HTTPD_ERR_MSG_RET(req, 501, "Transfer-Encoding is not identity", FALSE);
485 : }
486 : else
487 : { /* we can't x2 check ... as some might be multi headers */
488 2144 : size_t tmp = 0;
489 :
490 : /* all headers _must_ contain a ':' */
491 2144 : tmp = vstr_srch_chr_fwd(data, pos, len, ':');
492 2144 : if (!tmp)
493 208 : HTTPD_ERR_MSG_RET(req, 400, "Header does not end with : marker", FALSE);
494 :
495 : /* make sure unknown header is whitespace "valid" */
496 1936 : tmp = vstr_sc_posdiff(pos, tmp);
497 1936 : if (req->policy->use_hdrs_non_spc &&
498 : (vstr_cspn_cstr_chrs_fwd(data, pos, tmp, HTTP_LWS) != tmp))
499 0 : HTTPD_ERR_MSG_RET(req, 400, "Header has spaces before : marker", FALSE);
500 : }
501 : }
502 :
503 37417 : if (!req->policy->use_hdrs_err_411)
504 0 : return (TRUE);
505 :
506 37417 : if (got_content_type && !got_content_length)
507 144 : HTTPD_ERR_MSG_RET(req, 411, "Type but no Length", FALSE);
508 37273 : if (got_content_lang && !got_content_length)
509 144 : HTTPD_ERR_MSG_RET(req, 411, "Language but no Length", FALSE);
510 37129 : if (got_transfer_encoding && !got_content_length)
511 144 : HTTPD_ERR_MSG_RET(req, 411, "Transfer-Encoding but no Length", FALSE);
512 :
513 36985 : return (TRUE);
514 : }
515 : #undef HDR__EQ
516 : #undef HDR__SET
517 : #undef HDR__MUTLI_SET
518 :
519 : #define HDR__CON_1_0_FIXUP(name, h) \
520 : else if (VIEQ(data, pos, tmp, name)) \
521 : do { \
522 : req -> http_hdrs -> hdr_ ## h ->pos = 0; \
523 : req -> http_hdrs -> hdr_ ## h ->len = 0; \
524 : } while (FALSE)
525 : #define HDR__CON_1_0_MULTI_FIXUP(name, h) \
526 : else if (VIEQ(data, pos, tmp, name)) \
527 : do { \
528 : req -> http_hdrs -> multi -> hdr_ ## h ->pos = 0; \
529 : req -> http_hdrs -> multi -> hdr_ ## h ->len = 0; \
530 : } while (FALSE)
531 : void http_parse_connection(struct Con *con, struct Httpd_req_data *req)
532 36649 : {
533 36649 : Vstr_base *data = req->http_hdrs->multi->comb;
534 36649 : size_t pos = 0;
535 36649 : size_t len = 0;
536 36649 : unsigned int num = 0;
537 :
538 36649 : pos = req->http_hdrs->multi->hdr_connection->pos;
539 36649 : len = req->http_hdrs->multi->hdr_connection->len;
540 :
541 36649 : if (HTTPD_VER_GE_1_1(req))
542 31417 : con->keep_alive = HTTP_1_1_KEEP_ALIVE;
543 :
544 36649 : if (!len)
545 29553 : return;
546 :
547 11140 : while (len)
548 : {
549 : size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
550 7592 : HTTP_EOL HTTP_LWS ",");
551 :
552 7592 : ++num;
553 9716 : if (HTTPD_VER_GE_1_1(req))
554 : { /* this is all we have to do for HTTP/1.1 ... proxies understand it */
555 4248 : if (VIEQ(data, pos, tmp, "close"))
556 3872 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
557 : }
558 3344 : else if (VIEQ(data, pos, tmp, "keep-alive"))
559 : {
560 2624 : if (req->policy->use_keep_alive_1_0)
561 2624 : con->keep_alive = HTTP_1_0_KEEP_ALIVE;
562 : }
563 : /* now fixup connection headers for HTTP/1.0 proxies */
564 720 : HDR__CON_1_0_FIXUP("User-Agent", ua);
565 720 : HDR__CON_1_0_FIXUP("Referer", referer);
566 720 : HDR__CON_1_0_FIXUP("Authorization", authorization);
567 720 : HDR__CON_1_0_FIXUP("Expect", expect);
568 720 : HDR__CON_1_0_FIXUP("Host", host);
569 720 : HDR__CON_1_0_FIXUP("If-Modified-Since", if_modified_since);
570 720 : HDR__CON_1_0_FIXUP("If-Range", if_range);
571 720 : HDR__CON_1_0_FIXUP("If-Unmodified-Since", if_unmodified_since);
572 720 : HDR__CON_1_0_FIXUP("Range", range);
573 576 : HDR__CON_1_0_FIXUP("X-Moz", x_moz);
574 :
575 576 : HDR__CON_1_0_MULTI_FIXUP("Accept", accept);
576 432 : HDR__CON_1_0_MULTI_FIXUP("Accept-Charset", accept_charset);
577 432 : HDR__CON_1_0_MULTI_FIXUP("Accept-Encoding", accept_encoding);
578 400 : HDR__CON_1_0_MULTI_FIXUP("Accept-Language", accept_language);
579 256 : HDR__CON_1_0_MULTI_FIXUP("If-Match", if_match);
580 256 : HDR__CON_1_0_MULTI_FIXUP("If-None-Match", if_none_match);
581 :
582 : /* skip to end, or after next ',' */
583 7592 : tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, ",");
584 7592 : len -= tmp; pos += tmp;
585 7592 : if (!len)
586 7096 : break;
587 :
588 248 : assert(VPREFIX(data, pos, len, ","));
589 496 : len -= 1; pos += 1;
590 496 : HTTP_SKIP_LWS(data, pos, len);
591 :
592 496 : if (req->policy->max_connection_nodes &&
593 : (num >= req->policy->max_connection_nodes))
594 0 : return;
595 : }
596 : }
597 : #undef HDR__CON_1_0_FIXUP
598 : #undef HDR__CON_1_0_MULTI_FIXUP
599 :
600 : /* parse >= 1.0 things like, version and headers */
601 : int http_parse_1_x(struct Con *con, struct Httpd_req_data *req)
602 39241 : {
603 19628 : ASSERT(!req->ver_0_9);
604 :
605 39241 : if (!http_parse_version(con, req))
606 1152 : return (FALSE);
607 :
608 38089 : if (!http_parse_hdrs(con, req))
609 1104 : return (FALSE);
610 :
611 36985 : if (req->policy->max_requests &&
612 : (req->policy->max_requests <= con->evnt->acct.req_got) &&
613 : HTTPD_VER_GE_1_1(req))
614 0 : return (TRUE);
615 :
616 36985 : if (!req->policy->use_keep_alive && HTTPD_VER_GE_1_1(req))
617 336 : return (TRUE);
618 :
619 36649 : http_parse_connection(con, req);
620 :
621 36649 : if (req->policy->max_requests &&
622 : (req->policy->max_requests <= con->evnt->acct.req_got))
623 0 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
624 :
625 36649 : return (TRUE);
626 : }
627 :
628 : /* NOTE: allow "1 . 000" or just allow "1.000" ... LWS is craptastic, why
629 : * not go all the way ? */
630 : #define HTTP__PARSE_CHK_RET_OK() do { \
631 : HTTP_SKIP_LWS(data, pos, len); \
632 : \
633 : if (!len || \
634 : VPREFIX(data, pos, len, ",") || \
635 : (allow_more && VPREFIX(data, pos, len, ";"))) \
636 : { \
637 : *passed_pos = pos; \
638 : *passed_len = len; \
639 : \
640 : ASSERT(*val <= 1000); \
641 : \
642 : return (TRUE); \
643 : } \
644 : } while (FALSE)
645 :
646 : /* What is the quality parameter, value between 0 and 1000 inclusive.
647 : * returns TRUE on success, FALSE on failure. */
648 : static int http_parse_quality(Vstr_base *data,
649 : size_t *passed_pos, size_t *passed_len,
650 : int allow_more, unsigned int *val)
651 39664 : {
652 39664 : size_t pos = *passed_pos;
653 39664 : size_t len = *passed_len;
654 39664 : int lead_zero = FALSE;
655 39664 : size_t num_len = 0;
656 39664 : unsigned int parse_flags = VSTR_FLAG02(PARSE_NUM, NO_BEG_PM, NO_NEGATIVE);
657 :
658 19832 : ASSERT(val);
659 :
660 39664 : *val = 1000;
661 :
662 39664 : HTTP_SKIP_LWS(data, pos, len);
663 :
664 39664 : *passed_pos = pos;
665 39664 : *passed_len = len;
666 :
667 39664 : if (!len || VPREFIX(data, pos, len, ","))
668 23760 : return (TRUE);
669 :
670 20240 : if (VPREFIX(data, pos, len, ";q=0.")) /* opt */
671 : {
672 4336 : len -= strlen(";q=0."); pos += strlen(";q=0.");
673 4336 : lead_zero = TRUE;
674 4336 : *val = 0;
675 : }
676 11568 : else if (VPREFIX(data, pos, len, ";"))
677 : {
678 11280 : len -= 1; pos += 1;
679 11280 : HTTP_SKIP_LWS(data, pos, len);
680 :
681 11280 : if (!VPREFIX(data, pos, len, "q"))
682 704 : return (!!allow_more);
683 :
684 10576 : len -= 1; pos += 1;
685 10576 : HTTP_SKIP_LWS(data, pos, len);
686 :
687 10576 : if (!VPREFIX(data, pos, len, "="))
688 144 : return (!!allow_more);
689 :
690 10432 : len -= 1; pos += 1;
691 10432 : HTTP_SKIP_LWS(data, pos, len);
692 :
693 : /* if it's 0[.00?0?] TRUE, 0[.\d\d?\d?] or 1[.00?0?] is FALSE */
694 10432 : if (!(lead_zero = VPREFIX(data, pos, len, "0")) &&
695 : !VPREFIX(data, pos, len, "1"))
696 432 : return (FALSE);
697 10000 : *val = (!lead_zero) * 1000;
698 :
699 10000 : len -= 1; pos += 1;
700 :
701 10000 : HTTP__PARSE_CHK_RET_OK();
702 :
703 5008 : if (!VPREFIX(data, pos, len, "."))
704 144 : return (FALSE);
705 :
706 4864 : len -= 1; pos += 1;
707 : }
708 :
709 :
710 9488 : HTTP_SKIP_LWS(data, pos, len);
711 :
712 9488 : *val += vstr_parse_uint(data, pos, len, 10 | parse_flags, &num_len, NULL);
713 9488 : if (!num_len || (num_len > 3) || (*val > 1000))
714 864 : return (FALSE);
715 8624 : if (!lead_zero)
716 768 : ASSERT(*val == 1000);
717 : else
718 : {
719 7088 : if (num_len < 3) *val *= 10;
720 7088 : if (num_len < 2) *val *= 10;
721 : }
722 8624 : len -= num_len; pos += num_len;
723 :
724 8624 : HTTP__PARSE_CHK_RET_OK();
725 :
726 0 : return (FALSE);
727 : }
728 : #undef HTTP__PARSE_CHK_RET_OK
729 :
730 : /* return the length of a quoted string (must be >= 2), or 0 on syntax error */
731 : static size_t http__len_quoted_string(const Vstr_base *data,
732 : size_t pos, size_t len)
733 5840 : {
734 5840 : size_t orig_pos = pos;
735 :
736 5840 : if (!VPREFIX(data, pos, len, "\""))
737 192 : return (0);
738 :
739 5648 : len -= 1; pos += 1;
740 :
741 : while (TRUE)
742 : {
743 5856 : size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\");
744 :
745 5856 : len -= tmp; pos += tmp;
746 5856 : if (!len)
747 144 : return (0);
748 :
749 5712 : if (vstr_export_chr(data, pos) == '"')
750 5360 : return (vstr_sc_posdiff(orig_pos, pos));
751 :
752 176 : ASSERT(vstr_export_chr(data, pos) == '\\');
753 352 : if (len < 3) /* must be at least <\X"> */
754 144 : return (0);
755 208 : len -= 2; pos += 2;
756 208 : }
757 :
758 : assert_ret(FALSE, 0);
759 : }
760 :
761 : /* skip a quoted string, or fail on syntax error */
762 : static int http__skip_quoted_string(const Vstr_base *data,
763 : size_t *pos, size_t *len)
764 528 : {
765 528 : size_t qlen = http__len_quoted_string(data, *pos, *len);
766 :
767 264 : assert(VPREFIX(data, *pos, *len, "\""));
768 :
769 528 : if (!qlen)
770 288 : return (FALSE);
771 :
772 240 : *len -= qlen; *pos += qlen;
773 :
774 240 : HTTP_SKIP_LWS(data, *pos, *len);
775 240 : return (TRUE);
776 : }
777 :
778 : /* match non-week entity tags in both strings, return true if any match
779 : * only allow non-weak entity tags if allow_weak = FALSE */
780 : int httpd_match_etags(struct Httpd_req_data *req,
781 : const Vstr_base *hdr, size_t hpos, size_t hlen,
782 : const Vstr_base *vs1, size_t epos, size_t elen,
783 : int allow_weak)
784 2624 : {
785 2624 : int need_comma = FALSE;
786 :
787 1312 : ASSERT(hdr);
788 :
789 2624 : if (!vs1)
790 640 : return (FALSE);
791 :
792 5280 : while (hlen)
793 : {
794 3520 : int weak = FALSE;
795 3520 : size_t htlen = 0;
796 :
797 3520 : if (vstr_export_chr(hdr, hpos) == ',')
798 : {
799 768 : hlen -= 1; hpos += 1;
800 768 : HTTP_SKIP_LWS(hdr, hpos, hlen);
801 768 : need_comma = FALSE;
802 768 : continue;
803 : }
804 2752 : else if (need_comma)
805 0 : return (FALSE);
806 :
807 2752 : if (VPREFIX(hdr, hpos, hlen, "W/"))
808 : {
809 512 : weak = TRUE;
810 512 : hlen -= CLEN("W/"); hpos += CLEN("W/");
811 : }
812 2752 : if (!(htlen = http__len_quoted_string(hdr, hpos, hlen)))
813 192 : return (FALSE);
814 :
815 2560 : if (allow_weak || !weak)
816 : {
817 2560 : size_t orig_epos = epos;
818 2560 : size_t orig_elen = elen;
819 2560 : unsigned int num = 0;
820 :
821 6656 : while (elen)
822 : {
823 2560 : size_t etlen = 0;
824 :
825 2560 : if (vstr_export_chr(vs1, epos) == ',')
826 : {
827 0 : elen -= 1; epos += 1;
828 0 : HTTP_SKIP_LWS(vs1, epos, elen);
829 0 : need_comma = FALSE;
830 0 : continue;
831 : }
832 2560 : else if (need_comma)
833 0 : return (FALSE);
834 :
835 2560 : ++num;
836 :
837 4544 : if (!VPREFIX(vs1, epos, elen, "W/"))
838 1984 : weak = FALSE;
839 : else
840 : {
841 576 : weak = TRUE;
842 576 : elen -= CLEN("W/"); epos += CLEN("W/");
843 : }
844 2560 : if (!(etlen = http__len_quoted_string(vs1, epos, elen)))
845 0 : return (FALSE);
846 :
847 2560 : if ((allow_weak || !weak) &&
848 : vstr_cmp_eq(hdr, hpos, htlen, vs1, epos, etlen))
849 1024 : return (TRUE);
850 :
851 1536 : elen -= etlen; epos += etlen;
852 1536 : HTTP_SKIP_LWS(vs1, epos, elen);
853 1536 : need_comma = TRUE;
854 :
855 1536 : if (req->policy->max_etag_nodes && (num >= req->policy->max_etag_nodes))
856 0 : return (FALSE);
857 : }
858 :
859 1536 : epos = orig_epos;
860 1536 : elen = orig_elen;
861 : }
862 :
863 1536 : hlen -= htlen; hpos += htlen;
864 1536 : HTTP_SKIP_LWS(hdr, hpos, hlen);
865 1536 : need_comma = TRUE;
866 : }
867 :
868 768 : return (FALSE);
869 : }
870 :
871 : /* skip, or fail on syntax error */
872 : static int http__skip_parameters(Vstr_base *data, size_t *pos, size_t *len)
873 7536 : {
874 16272 : while (*len && (vstr_export_chr(data, *pos) != ','))
875 : { /* skip parameters */
876 1920 : size_t tmp = 0;
877 :
878 1920 : if (vstr_export_chr(data, *pos) != ';')
879 0 : return (FALSE); /* syntax error */
880 :
881 1920 : *len -= 1; *pos += 1;
882 1920 : HTTP_SKIP_LWS(data, *pos, *len);
883 1920 : tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,=");
884 1920 : *len -= tmp; *pos += tmp;
885 1920 : if (!*len)
886 288 : break;
887 :
888 1632 : switch (vstr_export_chr(data, *pos))
889 : {
890 144 : case ';': break;
891 0 : case ',': break;
892 :
893 : case '=': /* skip parameter value */
894 1344 : *len -= 1; *pos += 1;
895 1344 : HTTP_SKIP_LWS(data, *pos, *len);
896 1344 : if (!*len)
897 144 : return (FALSE); /* syntax error */
898 1200 : if (vstr_export_chr(data, *pos) == '"')
899 : {
900 528 : if (!http__skip_quoted_string(data, pos, len))
901 288 : return (FALSE); /* syntax error */
902 : }
903 : else
904 : {
905 672 : tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,");
906 672 : *len -= tmp; *pos += tmp;
907 : }
908 : break;
909 : }
910 : }
911 :
912 7104 : return (TRUE);
913 : }
914 :
915 : /* returns quality of the passed content-type in the "Accept:" header,
916 : * if it isn't there or we get a syntax error we return 1001 for not avilable */
917 : unsigned int http_parse_accept(Httpd_req_data *req,
918 : const Vstr_base *ct_vs1,
919 : size_t ct_pos, size_t ct_len)
920 29376 : {
921 29376 : Vstr_base *data = req->http_hdrs->multi->comb;
922 29376 : size_t pos = 0;
923 29376 : size_t len = 0;
924 29376 : unsigned int num = 0;
925 29376 : unsigned int quality = 1001;
926 29376 : unsigned int dummy = 1001;
927 29376 : int done_sub_type = FALSE;
928 29376 : size_t ct_sub_len = 0;
929 :
930 29376 : pos = req->http_hdrs->multi->hdr_accept->pos;
931 29376 : len = req->http_hdrs->multi->hdr_accept->len;
932 :
933 29376 : if (!len) /* no accept == accept all */
934 21344 : return (1000);
935 :
936 4016 : ASSERT(ct_vs1);
937 :
938 8032 : if (!(ct_sub_len = vstr_srch_chr_fwd(ct_vs1, ct_pos, ct_len, '/')))
939 : { /* it's too weird, blank it */
940 0 : if (ct_vs1 == req->content_type_vs1)
941 0 : req->content_type_vs1 = NULL;
942 0 : return (1);
943 : }
944 8032 : ct_sub_len = vstr_sc_posdiff(ct_pos, ct_sub_len);
945 :
946 8032 : while (len)
947 : {
948 : size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
949 10336 : HTTP_EOL HTTP_LWS ";,");
950 :
951 10336 : ++num;
952 :
953 : if (0) { }
954 10336 : else if (vstr_cmp_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
955 : { /* full match */
956 2368 : len -= tmp; pos += tmp;
957 2368 : if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
958 0 : return (1);
959 2368 : return (quality);
960 : }
961 9776 : else if ((tmp == (ct_sub_len + 1)) &&
962 : vstr_cmp_eq(data, pos, ct_sub_len, ct_vs1, ct_pos, ct_sub_len) &&
963 : (vstr_export_chr(data, vstr_sc_poslast(pos, tmp)) == '*'))
964 : { /* sub match */
965 1952 : len -= tmp; pos += tmp;
966 1952 : if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
967 144 : return (1);
968 1808 : done_sub_type = TRUE;
969 : }
970 6616 : else if (!done_sub_type && VEQ(data, pos, tmp, "*/*"))
971 : {
972 1344 : len -= tmp; pos += tmp;
973 1344 : if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
974 144 : return (1);
975 : }
976 : else
977 : {
978 4672 : len -= tmp; pos += tmp;
979 4672 : if (!http_parse_quality(data, &pos, &len, TRUE, &dummy))
980 144 : return (1);
981 : }
982 :
983 7536 : if (!http__skip_parameters(data, &pos, &len))
984 432 : return (1);
985 7104 : if (!len)
986 4768 : break;
987 :
988 1168 : assert(VPREFIX(data, pos, len, ","));
989 2336 : len -= 1; pos += 1;
990 2336 : HTTP_SKIP_LWS(data, pos, len);
991 :
992 2336 : if (req->policy->max_A_nodes && (num >= req->policy->max_A_nodes))
993 0 : return (1);
994 : }
995 :
996 2400 : ASSERT(quality <= 1001);
997 4800 : if (quality == 1001)
998 2704 : return (0);
999 :
1000 2096 : return (quality);
1001 : }
1002 :
1003 : static int http__cmp_lang_eq(const Vstr_base *s1, size_t p1, size_t l1,
1004 : const Vstr_base *s2, size_t p2, size_t l2)
1005 15520 : {
1006 15520 : if (l1 == l2)
1007 10848 : return (FALSE);
1008 :
1009 4672 : if (l1 > l2)
1010 1504 : return ((vstr_export_chr(s1, p1 + l2) == '-') &&
1011 : vstr_cmp_case_eq(s1, p1, l2, s2, p2, l2));
1012 :
1013 3168 : return ((vstr_export_chr(s2, p2 + l1) == '-') &&
1014 : vstr_cmp_case_eq(s1, p1, l1, s2, p2, l1));
1015 : }
1016 :
1017 : unsigned int http_parse_accept_language(Httpd_req_data *req,
1018 : const Vstr_base *ct_vs1,
1019 : size_t ct_pos, size_t ct_len)
1020 21760 : {
1021 21760 : Vstr_base *data = req->http_hdrs->multi->comb;
1022 21760 : size_t pos = 0;
1023 21760 : size_t len = 0;
1024 21760 : unsigned int num = 0;
1025 21760 : unsigned int quality = 1001;
1026 21760 : unsigned int dummy = 1001;
1027 21760 : size_t done_sub_type = 0;
1028 :
1029 21760 : pos = req->http_hdrs->multi->hdr_accept_language->pos;
1030 21760 : len = req->http_hdrs->multi->hdr_accept_language->len;
1031 :
1032 21760 : if (!len) /* no accept-language == accept all */
1033 7936 : return (1000);
1034 :
1035 6912 : ASSERT(ct_vs1);
1036 26080 : while (len)
1037 : {
1038 : size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
1039 19104 : HTTP_EOL HTTP_LWS ";,");
1040 :
1041 19104 : ++num;
1042 :
1043 : if (0) { }
1044 19104 : else if (vstr_cmp_case_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
1045 : { /* full match */
1046 2976 : len -= tmp; pos += tmp;
1047 2976 : if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
1048 0 : return (1);
1049 2976 : return (quality);
1050 : }
1051 16928 : else if ((tmp >= done_sub_type) &&
1052 : http__cmp_lang_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
1053 : { /* sub match - can be x-y-A;q=0, x-y-B, x-y
1054 : rfc2616#14.4
1055 : The language quality factor assigned to a language-tag by the
1056 : Accept-Language field is the quality value of the longest
1057 : language-range in the field that matches the language-tag.
1058 : */
1059 800 : unsigned int sub_type_qual = 0;
1060 :
1061 800 : len -= tmp; pos += tmp;
1062 800 : if (!http_parse_quality(data, &pos, &len, FALSE, &sub_type_qual))
1063 0 : return (1);
1064 :
1065 400 : ASSERT(sub_type_qual <= 1000);
1066 800 : if ((tmp > done_sub_type) || (sub_type_qual > quality))
1067 800 : quality = sub_type_qual;
1068 :
1069 800 : done_sub_type = tmp;
1070 : }
1071 16096 : else if (!done_sub_type && VEQ(data, pos, tmp, "*"))
1072 : {
1073 1536 : len -= tmp; pos += tmp;
1074 1536 : if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
1075 0 : return (1);
1076 : }
1077 : else
1078 : {
1079 13792 : len -= tmp; pos += tmp;
1080 13792 : if (!http_parse_quality(data, &pos, &len, FALSE, &dummy))
1081 0 : return (1);
1082 : }
1083 :
1084 16128 : if (!len)
1085 10784 : break;
1086 :
1087 2672 : assert(VPREFIX(data, pos, len, ","));
1088 5344 : len -= 1; pos += 1;
1089 5344 : HTTP_SKIP_LWS(data, pos, len);
1090 :
1091 5344 : if (req->policy->max_AL_nodes && (num >= req->policy->max_AL_nodes))
1092 0 : return (1);
1093 : }
1094 :
1095 5424 : ASSERT(quality <= 1001);
1096 10848 : if (quality == 1001)
1097 9152 : return (0);
1098 :
1099 1696 : return (quality);
1100 : }
1101 :
1102 : int http_parse_accept_encoding(struct Httpd_req_data *req, int force)
1103 27513 : {
1104 27513 : Vstr_base *data = req->http_hdrs->multi->comb;
1105 27513 : size_t pos = 0;
1106 27513 : size_t len = 0;
1107 27513 : unsigned int num = 0;
1108 27513 : unsigned int star_val = 1001;
1109 27513 : unsigned int dummy_val = 1001;
1110 :
1111 27513 : pos = req->http_hdrs->multi->hdr_accept_encoding->pos;
1112 27513 : len = req->http_hdrs->multi->hdr_accept_encoding->len;
1113 :
1114 27513 : if (!force && !req->policy->use_err_406 && !req->allow_accept_encoding)
1115 336 : return (FALSE);
1116 :
1117 27177 : if (!force)
1118 24201 : req->vary_ae = TRUE;
1119 :
1120 27177 : if (req->parsed_content_encoding)
1121 : {
1122 2048 : if (!req->allow_accept_encoding)
1123 0 : return (FALSE);
1124 :
1125 2048 : return (!!req->content_enc_gzip || !!req->content_enc_bzip2);
1126 : }
1127 :
1128 25129 : req->parsed_content_encoding = TRUE;
1129 :
1130 25129 : if (!len)
1131 19081 : goto parse_err;
1132 :
1133 6048 : req->content_encoding_xgzip = FALSE;
1134 6048 : req->content_enc_identity = 1001;
1135 6048 : req->content_enc_gzip = 1001;
1136 6048 : req->content_enc_bzip2 = 1001;
1137 :
1138 16560 : while (len)
1139 : {
1140 : size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
1141 10224 : HTTP_EOL HTTP_LWS ";,");
1142 :
1143 10224 : ++num;
1144 :
1145 : if (0) { }
1146 10224 : else if (VEQ(data, pos, tmp, "identity"))
1147 : {
1148 576 : len -= tmp; pos += tmp;
1149 576 : if (!http_parse_quality(data, &pos, &len, FALSE,
1150 : &req->content_enc_identity))
1151 144 : goto parse_err;
1152 : }
1153 9648 : else if (VEQ(data, pos, tmp, "gzip"))
1154 : {
1155 3680 : len -= tmp; pos += tmp;
1156 3680 : req->content_encoding_xgzip = FALSE;
1157 3680 : if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_gzip))
1158 1008 : goto parse_err;
1159 : }
1160 5968 : else if (VEQ(data, pos, tmp, "bzip2"))
1161 : {
1162 848 : len -= tmp; pos += tmp;
1163 848 : if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_bzip2))
1164 0 : goto parse_err;
1165 : }
1166 5120 : else if (VEQ(data, pos, tmp, "x-gzip"))
1167 : {
1168 144 : len -= tmp; pos += tmp;
1169 144 : req->content_encoding_xgzip = TRUE;
1170 144 : if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_gzip))
1171 0 : goto parse_err; /* ignore quality on x-gzip - just parse for errors */
1172 144 : req->content_enc_gzip = 1000;
1173 : }
1174 4976 : else if (VEQ(data, pos, tmp, "*"))
1175 : { /* "*;q=0,gzip" means TRUE ... and "*;q=1.0,gzip;q=0" means FALSE */
1176 576 : len -= tmp; pos += tmp;
1177 576 : if (!http_parse_quality(data, &pos, &len, FALSE, &star_val))
1178 144 : goto parse_err;
1179 : }
1180 : else
1181 : {
1182 4400 : len -= tmp; pos += tmp;
1183 4400 : if (!http_parse_quality(data, &pos, &len, FALSE, &dummy_val))
1184 0 : goto parse_err;
1185 : }
1186 :
1187 8928 : if (!len)
1188 4464 : break;
1189 2232 : assert(VPREFIX(data, pos, len, ","));
1190 4464 : len -= 1; pos += 1;
1191 4464 : HTTP_SKIP_LWS(data, pos, len);
1192 :
1193 4464 : if (req->policy->max_AE_nodes && (num >= req->policy->max_AE_nodes))
1194 0 : goto parse_err;
1195 : }
1196 :
1197 4752 : if (req->content_enc_gzip == 1001) req->content_enc_gzip = star_val;
1198 4752 : if (req->content_enc_bzip2 == 1001) req->content_enc_bzip2 = star_val;
1199 4752 : if (req->content_enc_identity == 1001) req->content_enc_identity = star_val;
1200 :
1201 4752 : if (req->content_enc_gzip == 1001) req->content_enc_gzip = 0;
1202 4752 : if (req->content_enc_bzip2 == 1001) req->content_enc_bzip2 = 0;
1203 4752 : if (req->content_enc_identity == 1001) req->content_enc_identity = 1;
1204 :
1205 4752 : if (!req->allow_accept_encoding)
1206 32 : return (FALSE);
1207 :
1208 4720 : if ((req->content_enc_identity > req->content_enc_gzip) &&
1209 : (req->content_enc_identity > req->content_enc_bzip2))
1210 1952 : goto parse_err; /* this is insane, but legal */
1211 :
1212 2768 : req->content_encoding_identity = !!req->content_enc_identity;
1213 2768 : req->content_encoding_gzip = !!req->content_enc_gzip;
1214 2768 : req->content_encoding_bzip2 = !!req->content_enc_bzip2;
1215 :
1216 2768 : return (!!req->content_enc_gzip || !!req->content_enc_bzip2);
1217 :
1218 22329 : parse_err:
1219 22329 : req->content_enc_identity = 1;
1220 22329 : req->content_enc_gzip = 0;
1221 22329 : req->content_enc_bzip2 = 0;
1222 :
1223 22329 : req->content_encoding_identity = !!req->content_enc_identity;
1224 22329 : req->content_encoding_gzip = !!req->content_enc_gzip;
1225 22329 : req->content_encoding_bzip2 = !!req->content_enc_bzip2;
1226 22329 : req->content_encoding_xgzip = FALSE;
1227 :
1228 22329 : return (FALSE);
1229 : }
1230 :
1231 : /* try to use gzip content-encoding on entity */
1232 : static int http__try_encoded_content(struct Con *con, Httpd_req_data *req,
1233 : const struct stat64 *req_f_stat,
1234 : off64_t *req_f_stat_st_size,
1235 : Vstr_base *fname,
1236 : const char *zip_ext, size_t zip_len)
1237 3200 : {
1238 3200 : const char *fname_cstr = NULL;
1239 3200 : int fd = -1;
1240 3200 : int ret = FALSE;
1241 3200 : int open_flags = O_NONBLOCK;
1242 :
1243 1600 : ASSERT(con->fs && !con->fs_num && !con->fs_off);
1244 1600 : ASSERT(con->fs->len == (uintmax_t)req_f_stat->st_size);
1245 :
1246 3200 : if (req_f_stat->st_size < HTTPD_CONF_ZIP_LIMIT_MIN) /* minor opt. */
1247 128 : return (ret);
1248 :
1249 3072 : if (req->policy->use_noatime)
1250 0 : open_flags |= O_NOATIME;
1251 :
1252 3072 : vstr_add_cstr_ptr(fname, fname->len, zip_ext);
1253 3072 : fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
1254 :
1255 3072 : if (fname->conf->malloc_bad)
1256 0 : vlg_warn(vlg, "Failed to export cstr for '%s'\n", zip_ext);
1257 3072 : else if ((fd = io__open(fname_cstr, open_flags)) == -1)
1258 : { /* no encoded version */ }
1259 : else
1260 : {
1261 : struct stat64 f_stat[1];
1262 :
1263 2480 : if (fstat64(fd, f_stat) == -1)
1264 0 : vlg_warn(vlg, "fstat: %m\n");
1265 2480 : else if ((req->policy->use_public_only && !(f_stat->st_mode & S_IROTH)) ||
1266 : (S_ISDIR(f_stat->st_mode)) || (!S_ISREG(f_stat->st_mode)) ||
1267 : (req_f_stat->st_mtime > f_stat->st_mtime) ||
1268 : !f_stat->st_size || /* zero sized compressed files aren't valid */
1269 : (*req_f_stat_st_size <= f_stat->st_size))
1270 : { /* ignore the encoded version */ }
1271 : else
1272 : {
1273 : /* swap, close the old fd (later) and use the new */
1274 2336 : SWAP_TYPE(con->fs->fd, fd, int);
1275 :
1276 : /* _only_ copy the new size over, mtime etc. is from the original file */
1277 2336 : con->fs->len = *req_f_stat_st_size = f_stat->st_size;
1278 2336 : req->encoded_mtime = f_stat->st_mtime;
1279 2336 : ret = TRUE;
1280 : }
1281 2480 : close(fd);
1282 : }
1283 :
1284 3072 : if (!ret)
1285 736 : vstr_sc_reduce(fname, 1, fname->len, zip_len);
1286 :
1287 3072 : return (ret);
1288 : }
1289 :
1290 : void httpd_parse_sc_try_fd_encoding(struct Con *con, Httpd_req_data *req,
1291 : const struct stat64 *fs,
1292 : off64_t *req_f_stat_st_size,
1293 : Vstr_base *fname)
1294 24537 : { /* Might normally add "!req->head_op && ..." but
1295 : * http://www.w3.org/TR/chips/#gl6 says that's bad */
1296 24537 : if (http_parse_accept_encoding(req, FALSE))
1297 : {
1298 2768 : if (req->content_enc_bzip2 >= req->content_enc_gzip)
1299 : { /* try bzip2, then gzip */
1300 1136 : if (req->content_encoding_bzip2 &&
1301 : !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
1302 : fname, ".bz2", CLEN(".bz2")))
1303 640 : req->content_encoding_bzip2 = FALSE;
1304 :
1305 1136 : if (req->content_encoding_bzip2) req->content_encoding_gzip = FALSE;
1306 :
1307 1136 : if (req->content_encoding_gzip &&
1308 : !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
1309 : fname, ".gz", CLEN(".gz")))
1310 0 : req->content_encoding_gzip = FALSE;
1311 : }
1312 : else
1313 : { /* try gzip, then bzip2 */
1314 1632 : if (req->content_encoding_gzip &&
1315 : !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
1316 : fname, ".gz", CLEN(".gz")))
1317 224 : req->content_encoding_gzip = FALSE;
1318 :
1319 1632 : if (req->content_encoding_gzip) req->content_encoding_bzip2 = FALSE;
1320 :
1321 1632 : if (req->content_encoding_bzip2 &&
1322 : !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
1323 : fname, ".bz2", CLEN(".bz2")))
1324 0 : req->content_encoding_bzip2 = FALSE;
1325 : }
1326 :
1327 : /* both can't be on ... one, the other or neither */
1328 1384 : ASSERT(!req->content_encoding_bzip2 || !req->content_encoding_gzip);
1329 : }
1330 24537 : }
1331 :
1332 : static int httpd__file_sect_add(struct Con *con, Httpd_req_data *req,
1333 : uintmax_t range_beg, uintmax_t range_end)
1334 4408 : {
1335 4408 : struct File_sect *fs = NULL;
1336 :
1337 2204 : ASSERT(con->fs && (con->fs_sz >= 1));
1338 :
1339 4408 : if (req->policy->max_range_nodes <= con->fs_num)
1340 128 : return (FALSE); /* done at start so we don't allocate anything needlessly */
1341 :
1342 4280 : if (!con->fs_num)
1343 : {
1344 1852 : ASSERT((con->fs == con->fs_store) || (con->fs_sz > 1));
1345 1852 : ASSERT(!con->use_mpbr);
1346 :
1347 1852 : goto file_sect_add;
1348 : }
1349 :
1350 576 : con->use_mpbr = TRUE;
1351 :
1352 576 : if (con->fs == con->fs_store)
1353 : {
1354 32 : ASSERT(con->fs_num == 1);
1355 :
1356 64 : if (!(con->mpbr_ct = vstr_make_base(con->evnt->io_w->conf)))
1357 0 : return (FALSE);
1358 :
1359 64 : if (!(fs = MK(sizeof(struct File_sect) * 16)))
1360 0 : return (FALSE);
1361 64 : con->fs = fs;
1362 64 : con->fs_sz = 16;
1363 :
1364 64 : con->fs->fd = con->fs_store->fd;
1365 64 : con->fs->off = con->fs_store->off;
1366 64 : con->fs->len = con->fs_store->len;
1367 64 : ++fs;
1368 : }
1369 512 : else if (con->fs_num >= con->fs_sz)
1370 : {
1371 0 : unsigned int num = (con->fs_sz << 1) + 1;
1372 :
1373 0 : ASSERT(con->fs_num == con->fs_sz);
1374 :
1375 0 : if (!MV(con->fs, fs, sizeof(struct File_sect) * num))
1376 0 : return (FALSE);
1377 0 : con->fs_sz = num;
1378 : }
1379 :
1380 4280 : file_sect_add:
1381 4280 : fs = con->fs + con->fs_num++;
1382 :
1383 4280 : fs->fd = con->fs->fd; /* copy to each one */
1384 4280 : fs->off = range_beg;
1385 4280 : fs->len = (range_end - range_beg) + 1;
1386 :
1387 4280 : if ((req->fs_len + fs->len) < req->fs_len)
1388 0 : return (FALSE);
1389 :
1390 4280 : req->fs_len += fs->len;
1391 :
1392 4280 : return (TRUE);
1393 : }
1394 :
1395 : /* Allow...
1396 : bytes=NUM-NUM
1397 : bytes=-NUM
1398 : bytes=NUM-
1399 : ...and due to LWS, http crapola parsing, even...
1400 : bytes = , , NUM - NUM , ,
1401 : ...allowing ability to disable multiple ranges at once, due to
1402 : multipart/byteranges being too much crack, I think this is stds. compliant.
1403 : */
1404 : int http_parse_range(struct Con *con, Httpd_req_data *req)
1405 5288 : {
1406 5288 : Vstr_base *data = con->evnt->io_r;
1407 5288 : Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
1408 5288 : size_t pos = h_r->pos;
1409 5288 : size_t len = h_r->len;
1410 5288 : uintmax_t fsize = req->f_stat_st_size;
1411 : unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
1412 5288 : VSTR_FLAG_PARSE_NUM_OVERFLOW);
1413 5288 : size_t num_len = 0;
1414 :
1415 5288 : if (!VPREFIX(data, pos, len, "bytes"))
1416 288 : return (0);
1417 5000 : len -= CLEN("bytes"); pos += CLEN("bytes");
1418 :
1419 5000 : HTTP_SKIP_LWS(data, pos, len);
1420 :
1421 5000 : if (!VPREFIX(data, pos, len, "="))
1422 144 : return (0);
1423 4856 : len -= CLEN("="); pos += CLEN("=");
1424 :
1425 4856 : http_parse_skip_blanks(data, &pos, &len);
1426 :
1427 4856 : while (len)
1428 : {
1429 5704 : uintmax_t range_beg = 0;
1430 5704 : uintmax_t range_end = 0;
1431 :
1432 6744 : if (VPREFIX(data, pos, len, "-"))
1433 : { /* num bytes at end */
1434 1472 : uintmax_t tmp = 0;
1435 :
1436 1472 : len -= CLEN("-"); pos += CLEN("-");
1437 1472 : HTTP_SKIP_LWS(data, pos, len);
1438 :
1439 1472 : tmp = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
1440 1472 : len -= num_len; pos += num_len;
1441 1472 : if (!num_len)
1442 144 : return (0);
1443 :
1444 1328 : if (!tmp)
1445 144 : return (416);
1446 :
1447 1184 : if (tmp >= fsize)
1448 144 : return (0);
1449 :
1450 1040 : range_beg = fsize - tmp;
1451 1040 : range_end = fsize - 1;
1452 : }
1453 : else
1454 : { /* offset - [end] */
1455 4232 : range_beg = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
1456 4232 : len -= num_len; pos += num_len;
1457 4232 : HTTP_SKIP_LWS(data, pos, len);
1458 :
1459 4232 : if (!VPREFIX(data, pos, len, "-"))
1460 288 : return (0);
1461 3944 : len -= CLEN("-"); pos += CLEN("-");
1462 3944 : HTTP_SKIP_LWS(data, pos, len);
1463 :
1464 4968 : if (!len || VPREFIX(data, pos, len, ","))
1465 1024 : range_end = fsize - 1;
1466 : else
1467 : {
1468 2920 : range_end = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, 0);
1469 2920 : len -= num_len; pos += num_len;
1470 2920 : if (!num_len)
1471 144 : return (0);
1472 :
1473 2776 : if (range_end >= fsize)
1474 352 : range_end = fsize - 1;
1475 : }
1476 :
1477 3800 : if ((range_beg >= fsize) || (range_beg > range_end))
1478 288 : return (416);
1479 :
1480 3512 : if ((range_beg == 0) &&
1481 : (range_end == (fsize - 1)))
1482 144 : return (0);
1483 : }
1484 :
1485 4408 : http_parse_skip_blanks(data, &pos, &len);
1486 :
1487 4408 : if (!httpd__file_sect_add(con, req, range_beg, range_end))
1488 128 : return (0); /* after all that, ignore if there is more than one range */
1489 : }
1490 :
1491 3432 : return (200);
1492 : }
1493 :
1494 : /* because we only parse for a combined CRLF, and some proxies/clients parse for
1495 : * either ... make sure we don't have embedded singles which could cause
1496 : * response splitting */
1497 : static int http__chk_single_crlf(Vstr_base *data, size_t pos, size_t len)
1498 61842 : {
1499 61842 : if (vstr_srch_chr_fwd(data, pos, len, '\r'))
1500 0 : return ('\r');
1501 :
1502 61842 : if (vstr_srch_chr_fwd(data, pos, len, '\n'))
1503 0 : return ('\n');
1504 :
1505 61842 : return (0);
1506 : }
1507 :
1508 : unsigned short httpd_parse_host_port(Vstr_base *s1, size_t pos, size_t len)
1509 3456 : {
1510 : unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
1511 3456 : VSTR_FLAG_PARSE_NUM_OVERFLOW);
1512 3456 : size_t num_len = 0;
1513 3456 : unsigned short port = 0;
1514 :
1515 1728 : ASSERT(vstr_export_chr(s1, pos) == ':');
1516 :
1517 3456 : len -= 1; pos += 1; /* skip the ':' */
1518 3456 : port = vstr_parse_ushort(s1, pos, len, num_flags, &num_len, NULL);
1519 :
1520 3456 : if (!port || (num_len != len))
1521 0 : return (0);
1522 :
1523 3456 : return (port);
1524 : }
1525 :
1526 : /* convert a http://abcd/foo into /foo with host=abcd ...
1527 : * also do sanity checking on the URI and host for valid characters */
1528 : int http_parse_host(struct Con *con, struct Httpd_req_data *req)
1529 37657 : {
1530 37657 : Vstr_base *data = con->evnt->io_r;
1531 37657 : size_t op_pos = req->path_pos;
1532 37657 : size_t op_len = req->path_len;
1533 :
1534 : /* HTTP/1.1 requires a host -- allow blank hostnames */
1535 37657 : if (req->ver_1_1 && !req->http_hdrs->hdr_host->pos)
1536 1056 : HTTPD_ERR_MSG_RET(req, 400, "Hostname is required in 1.1", FALSE);
1537 :
1538 : /* check for absolute URIs */
1539 36601 : if (VIPREFIX(data, op_pos, op_len, "http://"))
1540 : { /* ok, be forward compatible */
1541 11329 : size_t tmp = CLEN("http://");
1542 :
1543 11329 : op_len -= tmp; op_pos += tmp;
1544 11329 : tmp = vstr_srch_chr_fwd(data, op_pos, op_len, '/');
1545 11329 : if (!tmp)
1546 : {
1547 1416 : HTTP__HDR_SET(req, host, op_pos, op_len);
1548 1416 : op_len = 1;
1549 1416 : --op_pos;
1550 : }
1551 : else
1552 : { /* found end of host ... */
1553 9913 : size_t host_len = tmp - op_pos;
1554 :
1555 9913 : HTTP__HDR_SET(req, host, op_pos, host_len);
1556 9913 : op_len -= host_len; op_pos += host_len;
1557 : }
1558 5672 : assert(VPREFIX(data, op_pos, op_len, "/"));
1559 : }
1560 :
1561 36601 : if (req->ver_1_x && !req->http_hdrs->hdr_host->pos)
1562 0 : HTTPD_ERR_MSG_RET(req, 400,
1563 : "Hostname or Absolute URL is required in 1.x", FALSE);
1564 :
1565 36601 : if (req->http_hdrs->hdr_host->len)
1566 : { /* check host looks valid ... header must exist, but can be empty */
1567 25529 : size_t pos = req->http_hdrs->hdr_host->pos;
1568 25529 : size_t len = req->http_hdrs->hdr_host->len;
1569 25529 : size_t tmp = 0;
1570 :
1571 : /* leaving out most checks for ".." or invalid chars in hostnames etc.
1572 : as the default filename checks should catch them.
1573 : Note: "Host: ." is also caught by default due to the check for /./
1574 : Note: There are also optional checks in http__valid_hostname()
1575 : */
1576 :
1577 : /* Check for Host header with extra / ...
1578 : * Ie. only allow a single directory name.
1579 : * We could just leave this (it's not a security check, /../ is checked
1580 : * for at filepath time), but I feel like being anal and this way there
1581 : * aren't multiple urls to a single path. */
1582 25529 : if (vstr_srch_chr_fwd(data, pos, len, '/'))
1583 144 : HTTPD_ERR_MSG_RET(req, 400, "Hostname contains /", FALSE);
1584 :
1585 25385 : switch (http__chk_single_crlf(data, pos, len))
1586 : {
1587 : case '\r':
1588 0 : HTTPD_ERR_MSG_RET(req, 400, "Hostname contains \\r", FALSE);
1589 : case '\n':
1590 0 : HTTPD_ERR_MSG_RET(req, 400, "Hostname contains \\n", FALSE);
1591 : case 0:
1592 0 : ASSERT_NO_SWITCH_DEF();
1593 : }
1594 :
1595 25385 : if ((tmp = vstr_srch_chr_fwd(data, pos, len, ':')))
1596 : { /* NOTE: not sure if we have to 400 if the port doesn't match
1597 : * or if it's an "invalid" port number (Ie. == 0 || > 65535) */
1598 2016 : len -= tmp - pos; pos = tmp;
1599 :
1600 2016 : if (!VEQ(data, pos, len, ":"))
1601 1872 : if (!(req->http_host_port = httpd_parse_host_port(data, pos, len)))
1602 0 : HTTPD_ERR_MSG_RET(req, 400, "Port is not a valid number", FALSE);
1603 :
1604 2016 : req->http_hdrs->hdr_host->len -= len;
1605 : }
1606 : }
1607 :
1608 36457 : switch (http__chk_single_crlf(data, op_pos, op_len))
1609 : {
1610 : case '\r':
1611 0 : HTTPD_ERR_MSG_RET(req, 400, "Path contains \\r", FALSE);
1612 : case '\n':
1613 0 : HTTPD_ERR_MSG_RET(req, 400, "Path contains \\n", FALSE);
1614 : case 0:
1615 0 : ASSERT_NO_SWITCH_DEF();
1616 : }
1617 :
1618 : /* uri#fragment ... craptastic clients pass this and assume it is ignored */
1619 36457 : if (req->policy->remove_url_frag)
1620 34953 : op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "#");
1621 : /* uri?foo ... This is "ok" to pass, however if you move dynamic
1622 : * resources to static ones you need to do this */
1623 36457 : if (req->policy->remove_url_query)
1624 1504 : op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "?");
1625 :
1626 36457 : req->path_pos = op_pos;
1627 36457 : req->path_len = op_len;
1628 :
1629 36457 : return (TRUE);
1630 : }
1631 :
1632 : void http_parse_skip_blanks(Vstr_base *data,
1633 : size_t *passed_pos, size_t *passed_len)
1634 9264 : {
1635 9264 : size_t pos = *passed_pos;
1636 9264 : size_t len = *passed_len;
1637 :
1638 9264 : HTTP_SKIP_LWS(data, pos, len);
1639 18960 : while (VPREFIX(data, pos, len, ",")) /* http crack */
1640 : {
1641 5064 : len -= CLEN(","); pos += CLEN(",");
1642 5064 : HTTP_SKIP_LWS(data, pos, len);
1643 : }
1644 :
1645 9264 : *passed_pos = pos;
1646 9264 : *passed_len = len;
1647 9264 : }
1648 :
1649 : static int http_parse_wait_io_r(struct Con *con)
1650 25385 : {
1651 25385 : if (con->evnt->io_r_shutdown)
1652 159 : return (!!con->evnt->io_w->len);
1653 :
1654 : /* so we aren't acting under the req policy anymore */
1655 25226 : httpd_policy_change_con(con, con->policy);
1656 :
1657 25226 : evnt_wait_cntl_add(con->evnt, POLLIN);
1658 25226 : evnt_fd_set_cork(con->evnt, FALSE);
1659 :
1660 25226 : return (TRUE);
1661 : }
1662 :
1663 : static int httpd_serv__parse_no_req(struct Con *con, struct Httpd_req_data *req)
1664 19153 : {
1665 19153 : if (req->policy->max_header_sz &&
1666 : (con->evnt->io_r->len > req->policy->max_header_sz))
1667 : {
1668 62 : evnt_got_pkt(con->evnt);
1669 62 : HTTPD_ERR_MSG_RET(req, 400, "Too big", http_fin_err_req(con, req));
1670 : }
1671 :
1672 19091 : http_req_free(req);
1673 :
1674 19091 : return (http_parse_wait_io_r(con));
1675 : }
1676 :
1677 : /* http spec says ignore leading LWS ... *sigh* */
1678 : static int http__parse_req_all(struct Con *con, struct Httpd_req_data *req,
1679 : const char *eol, int *ern)
1680 70386 : {
1681 70386 : Vstr_base *data = con->evnt->io_r;
1682 70386 : size_t pos = 0;
1683 :
1684 34522 : ASSERT(eol && ern);
1685 :
1686 70386 : *ern = FALSE;
1687 :
1688 : /* should use vstr_del(data, 1, vstr_spn_cstr_buf_fwd(..., HTTP_EOL)); */
1689 : /* NOTE: eol might be HTTP_END_OF_REQUEST, so need to do this first *sigh* */
1690 4943540 : while (VPREFIX(data, 1, data->len, HTTP_EOL))
1691 4802768 : vstr_del(data, 1, CLEN(HTTP_EOL));
1692 :
1693 34522 : ASSERT(!req->len);
1694 70386 : if (!(pos = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
1695 19153 : goto no_req;
1696 :
1697 51233 : req->len = pos + CLEN(eol) - 1; /* add rest of EOL */
1698 :
1699 51233 : return (TRUE);
1700 :
1701 19153 : no_req:
1702 19153 : *ern = httpd_serv__parse_no_req(con, req);
1703 19153 : return (FALSE);
1704 : }
1705 :
1706 : int http_parse_req(struct Con *con)
1707 65648 : {
1708 65648 : Vstr_base *data = con->evnt->io_r;
1709 65648 : struct Httpd_req_data *req = NULL;
1710 65648 : int ern_req_all = FALSE;
1711 :
1712 31911 : ASSERT(con->fs && !con->fs_num);
1713 :
1714 65648 : if (!data->len)
1715 6294 : return (http_parse_wait_io_r(con));
1716 :
1717 59354 : if (!(req = http_req_make(con)))
1718 0 : return (FALSE);
1719 :
1720 59354 : if (!req->policy->allow_http_0_9)
1721 0 : con->parsed_method_ver_1_0 = TRUE;
1722 :
1723 59354 : if (con->parsed_method_ver_1_0)
1724 : { /* wait for all the headers */
1725 39947 : if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
1726 10178 : return (ern_req_all);
1727 : }
1728 : else
1729 : {
1730 19407 : if (!http__parse_req_all(con, req, HTTP_EOL, &ern_req_all))
1731 7415 : return (ern_req_all);
1732 : }
1733 :
1734 41761 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
1735 41761 : http_req_split_method(con, req);
1736 41761 : if (req->sects->malloc_bad)
1737 : {
1738 0 : evnt_got_pkt(con->evnt);
1739 0 : VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "split: %m\n"));
1740 : }
1741 41761 : else if ((req->sects->num < 2) ||
1742 : (!req->policy->allow_http_0_9 && (req->sects->num < 3)))
1743 : {
1744 288 : evnt_got_pkt(con->evnt);
1745 288 : HTTPD_ERR_MSG_RET(req, 400, "Bad request line", http_fin_err_req(con, req));
1746 : }
1747 : else
1748 : {
1749 41473 : size_t op_pos = 0;
1750 41473 : size_t op_len = 0;
1751 :
1752 41473 : if (req->ver_0_9)
1753 672 : vlg_dbg1(vlg, "Method(0.9):"
1754 : " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>\n",
1755 : con->evnt->io_r, req->sects, 1U,
1756 : con->evnt->io_r, req->sects, 2U);
1757 : else
1758 : { /* need to get all headers */
1759 40801 : if (!con->parsed_method_ver_1_0)
1760 : { /* req line isn't 0.9 ... so must continue to be */
1761 11032 : con->parsed_method_ver_1_0 = TRUE;
1762 11032 : req->len = 0;
1763 11032 : if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
1764 1560 : return (ern_req_all);
1765 : }
1766 :
1767 39241 : vlg_dbg1(vlg, "Method(1.x):"
1768 : " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>"
1769 : " $<http-esc.vstr.sect:%p%p%u>\n", data, req->sects, 1U,
1770 : data, req->sects, 2U, data, req->sects, 3U);
1771 :
1772 : /* put this up here so errors don't get DATA */
1773 39241 : op_pos = VSTR_SECTS_NUM(req->sects, 1)->pos;
1774 39241 : op_len = VSTR_SECTS_NUM(req->sects, 1)->len;
1775 39241 : if (VEQ(data, op_pos, op_len, "HEAD"))
1776 16216 : req->head_op = TRUE;
1777 :
1778 39241 : http_req_split_hdrs(con, req);
1779 : }
1780 39913 : evnt_got_pkt(con->evnt);
1781 :
1782 : if (HTTP_CONF_SAFE_PRINT_REQ)
1783 19949 : vlg_dbg3(vlg, "REQ:\n$<vstr.hexdump:%p%zu%zu>",
1784 : data, (size_t)1, req->len);
1785 : else
1786 19964 : vlg_dbg3(vlg, "REQ:\n$<vstr:%p%zu%zu>", data, (size_t)1, req->len);
1787 :
1788 19964 : assert(((req->sects->num >= 3) && !req->ver_0_9) || (req->sects->num == 2));
1789 :
1790 39913 : op_pos = VSTR_SECTS_NUM(req->sects, 1)->pos;
1791 39913 : op_len = VSTR_SECTS_NUM(req->sects, 1)->len;
1792 39913 : req->path_pos = VSTR_SECTS_NUM(req->sects, 2)->pos;
1793 39913 : req->path_len = VSTR_SECTS_NUM(req->sects, 2)->len;
1794 :
1795 39913 : if (!req->ver_0_9 && !http_parse_1_x(con, req))
1796 : {
1797 2256 : if (req->error_code == 500)
1798 0 : return (http_fin_errmem_req(con, req));
1799 2256 : return (http_fin_err_req(con, req));
1800 : }
1801 :
1802 37657 : if (!http_parse_host(con, req))
1803 1200 : return (http_fin_err_req(con, req));
1804 :
1805 : if (0) { }
1806 36457 : else if (VEQ(data, op_pos, op_len, "GET"))
1807 : {
1808 18489 : if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
1809 144 : HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
1810 :
1811 18345 : if (!http_req_make_path(con, req))
1812 1200 : return (http_fin_err_req(con, req));
1813 :
1814 17145 : return (http_req_op_get(con, req));
1815 : }
1816 17968 : else if (req->ver_0_9) /* 400 or 501? - apache does 400 */
1817 336 : HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
1818 17632 : else if (VEQ(data, op_pos, op_len, "HEAD"))
1819 : {
1820 8036 : ASSERT(req->head_op == TRUE); /* above, so errors are chopped */
1821 16072 : req->head_op = TRUE;
1822 :
1823 16072 : if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
1824 192 : HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
1825 :
1826 15880 : if (!http_req_make_path(con, req))
1827 992 : return (http_fin_err_req(con, req));
1828 :
1829 14888 : return (http_req_op_get(con, req));
1830 : }
1831 1560 : else if (VEQ(data, op_pos, op_len, "OPTIONS"))
1832 : {
1833 816 : if (!VPREFIX(data, req->path_pos, req->path_len, "/") &&
1834 : !VEQ(data, req->path_pos, req->path_len, "*"))
1835 144 : HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
1836 :
1837 : /* Speed hack: Don't even call make_path if it's "OPTIONS * ..."
1838 : * and we don't need to check the Host header */
1839 672 : if (req->policy->use_vhosts_name &&
1840 : req->policy->use_host_chk && req->policy->use_host_err_400 &&
1841 : !VEQ(data, req->path_pos, req->path_len, "*") &&
1842 : !http_req_make_path(con, req))
1843 144 : return (http_fin_err_req(con, req));
1844 :
1845 528 : return (http_req_op_opts(con, req));
1846 : }
1847 744 : else if (req->policy->allow_trace_op && VEQ(data, op_pos, op_len, "TRACE"))
1848 312 : return (http_req_op_trace(con, req));
1849 432 : else if (VEQ(data, op_pos, op_len, "TRACE") ||
1850 : VEQ(data, op_pos, op_len, "POST") ||
1851 : VEQ(data, op_pos, op_len, "PUT") ||
1852 : VEQ(data, op_pos, op_len, "DELETE") ||
1853 : VEQ(data, op_pos, op_len, "CONNECT") ||
1854 : FALSE) /* we know about these ... but don't allow them */
1855 144 : HTTPD_ERR_RET(req, 405, http_fin_err_req(con, req));
1856 : else
1857 288 : HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
1858 : }
1859 : ASSERT_NOT_REACHED();
1860 : }
1861 :
|