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 server request processing */
21 :
22 : #define EX_UTILS_NO_FUNCS 1
23 : #include "ex_utils.h"
24 :
25 : #include "mk.h"
26 :
27 : #include "vlg.h"
28 :
29 : #define HTTPD_HAVE_GLOBAL_OPTS 1
30 : #include "httpd.h"
31 : #include "httpd_policy.h"
32 :
33 : #include <syslog.h>
34 :
35 : #if ! HTTPD_CONF_USE_MIME_XATTR
36 : # define getxattr(w, x, y, z) (errno = ENOSYS, -1)
37 : #else
38 : # include <sys/xattr.h>
39 : #endif
40 :
41 : #if ! COMPILE_DEBUG
42 : # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
43 : # define HTTP_CONF_SAFE_PRINT_REQ TRUE
44 : #else
45 : # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
46 : # define HTTP_CONF_SAFE_PRINT_REQ FALSE
47 : #endif
48 : #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
49 :
50 : #define HTTPD_CONF_ZIP_LIMIT_MIN 8
51 :
52 : #define CLEN COMPILE_STRLEN
53 :
54 : /* is the cstr a prefix of the vstr */
55 : #define VPREFIX(vstr, p, l, cstr) \
56 : (((l) >= CLEN(cstr)) && \
57 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
58 : /* is the cstr a suffix of the vstr */
59 : #define VSUFFIX(vstr, p, l, cstr) \
60 : (((l) >= CLEN(cstr)) && \
61 : vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
62 :
63 : /* is the cstr a prefix of the vstr, no case */
64 : #define VIPREFIX(vstr, p, l, cstr) \
65 : (((l) >= CLEN(cstr)) && \
66 : vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
67 :
68 : /* for simplicity */
69 : #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
70 : #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
71 :
72 : #define HTTP__HDR_SET(req, h, p, l) do { \
73 : (req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
74 : (req)-> http_hdrs -> hdr_ ## h ->len = (l); \
75 : } while (FALSE)
76 : #define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
77 : (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
78 : (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
79 : } while (FALSE)
80 :
81 : #define HTTP__XTRA_HDR_INIT(x) do { \
82 : req-> x ## _vs1 = NULL; \
83 : req-> x ## _pos = 0; \
84 : req-> x ## _len = 0; \
85 : } while (FALSE)
86 :
87 : static Vlg *vlg = NULL;
88 :
89 : void httpd_req_init(Vlg *passed_vlg)
90 64 : {
91 32 : ASSERT(passed_vlg && !vlg);
92 64 : vlg = passed_vlg;
93 64 : }
94 :
95 : void httpd_req_exit(void)
96 44 : {
97 22 : ASSERT(vlg);
98 44 : vlg = NULL;
99 44 : }
100 :
101 : Httpd_req_data *http_req_make(struct Con *con)
102 59398 : {
103 : static Httpd_req_data real_req[1];
104 59398 : Httpd_req_data *req = real_req;
105 59398 : const Httpd_policy_opts *policy = NULL;
106 :
107 29028 : ASSERT(!req->using_req);
108 :
109 59398 : if (!req->done_once)
110 : {
111 44 : Vstr_conf *conf = NULL;
112 :
113 44 : if (con)
114 44 : conf = con->evnt->io_w->conf;
115 :
116 44 : if (!(req->fname = vstr_make_base(conf)) ||
117 : !(req->http_hdrs->multi->combiner_store = vstr_make_base(conf)) ||
118 : !(req->sects = vstr_sects_make(8)) ||
119 : !(req->tag = vstr_make_base(conf)))
120 0 : return (NULL);
121 :
122 44 : req->f_mmap = NULL;
123 44 : req->xtra_content = NULL;
124 : }
125 :
126 59398 : http_parse_clear_hdrs(req);
127 :
128 59398 : req->http_hdrs->multi->comb = con ? con->evnt->io_r : NULL;
129 :
130 59398 : vstr_del(req->fname, 1, req->fname->len);
131 :
132 59398 : req->now = evnt_sc_time();
133 :
134 59398 : req->len = 0;
135 :
136 59398 : req->path_pos = 0;
137 59398 : req->path_len = 0;
138 :
139 59398 : req->error_code = 0;
140 59398 : req->error_line = "";
141 59398 : req->error_msg = "";
142 59398 : req->error_xmsg = "";
143 59398 : req->error_len = 0;
144 :
145 59398 : req->fs_len = 0;
146 :
147 59398 : req->f_stat_st_size = 0;
148 :
149 59398 : req->sects->num = 0;
150 : /* f_stat */
151 59398 : if (con)
152 59354 : req->orig_io_w_len = con->evnt->io_w->len;
153 :
154 : /* NOTE: These should probably be allocated at init time, depending on the
155 : * option flags given */
156 29028 : ASSERT(!req->f_mmap || !req->f_mmap->len);
157 59398 : if (req->f_mmap)
158 4291 : vstr_del(req->f_mmap, 1, req->f_mmap->len);
159 29028 : ASSERT(!req->xtra_content || !req->xtra_content->len);
160 59398 : if (req->xtra_content)
161 57548 : vstr_del(req->xtra_content, 1, req->xtra_content->len);
162 :
163 59398 : req->vhost_prefix_len = 0;
164 :
165 59398 : req->sects->malloc_bad = FALSE;
166 :
167 59398 : req->parsed_content_encoding = FALSE;
168 59398 : req->content_encoding_identity = TRUE;
169 59398 : req->content_encoding_gzip = FALSE;
170 59398 : req->content_encoding_bzip2 = FALSE;
171 59398 : req->content_encoding_xgzip = FALSE;
172 :
173 59398 : req->user_return_error_code = FALSE;
174 :
175 59398 : req->vary_star = con ? con->vary_star : FALSE;
176 59398 : req->vary_a = FALSE;
177 59398 : req->vary_ac = FALSE;
178 59398 : req->vary_ae = FALSE;
179 59398 : req->vary_al = FALSE;
180 59398 : req->vary_rf = FALSE;
181 59398 : req->vary_ua = FALSE;
182 59398 : req->vary_ims = FALSE;
183 59398 : req->vary_ius = FALSE;
184 59398 : req->vary_ir = FALSE;
185 59398 : req->vary_im = FALSE;
186 59398 : req->vary_inm = FALSE;
187 59398 : req->vary_xm = FALSE;
188 :
189 59398 : req->direct_uri = FALSE;
190 59398 : req->direct_filename = FALSE;
191 59398 : req->skip_document_root = FALSE;
192 :
193 59398 : req->ver_0_9 = FALSE;
194 59398 : req->ver_1_1 = FALSE;
195 59398 : req->ver_1_x = FALSE;
196 59398 : req->head_op = FALSE;
197 :
198 59398 : req->chked_encoded_path = FALSE;
199 :
200 59398 : req->neg_content_type_done = FALSE;
201 59398 : req->neg_content_lang_done = FALSE;
202 :
203 59398 : req->conf_secure_dirs = FALSE;
204 59398 : req->conf_friendly_file = FALSE;
205 59398 : req->conf_friendly_dirs = FALSE;
206 :
207 59398 : req->done_once = TRUE;
208 59398 : req->using_req = TRUE;
209 :
210 59398 : req->malloc_bad = FALSE;
211 :
212 59398 : if (con && !vstr_sub_vstr(req->tag, 1, req->tag->len,
213 : con->tag, 1, con->tag->len, VSTR_TYPE_SUB_BUF_REF))
214 0 : return (NULL);
215 :
216 59398 : if (!con)
217 44 : req->policy = NULL;
218 : else
219 : {
220 59354 : policy = con->policy;
221 59354 : httpd_policy_change_req(con, req, policy);
222 : }
223 :
224 59398 : return (req);
225 : }
226 :
227 : void http_req_free(Httpd_req_data *req)
228 59354 : {
229 59354 : if (!req) /* for if/when move to malloc/free */
230 0 : return;
231 :
232 29006 : ASSERT(req->done_once && req->using_req);
233 :
234 : /* we do vstr deletes here to return the nodes back to the pool */
235 59354 : vstr_del(req->fname, 1, req->fname->len);
236 29006 : ASSERT(!req->http_hdrs->multi->combiner_store->len);
237 59354 : if (req->f_mmap)
238 4291 : vstr_del(req->f_mmap, 1, req->f_mmap->len);
239 :
240 59354 : req->http_hdrs->multi->comb = NULL;
241 :
242 59354 : req->using_req = FALSE;
243 : }
244 :
245 : void http_req_exit(void)
246 44 : {
247 44 : Httpd_req_data *req = http_req_make(NULL);
248 44 : struct Http_hdrs__multi *tmp = NULL;
249 :
250 44 : if (!req)
251 0 : return;
252 :
253 44 : tmp = req->http_hdrs->multi;
254 :
255 44 : vstr_free_base(req->fname); req->fname = NULL;
256 44 : vstr_free_base(tmp->combiner_store); tmp->combiner_store = NULL;
257 44 : vstr_free_base(req->f_mmap); req->f_mmap = NULL;
258 44 : vstr_free_base(req->xtra_content); req->xtra_content = NULL;
259 44 : vstr_sects_free(req->sects); req->sects = NULL;
260 44 : vstr_free_base(req->tag); req->tag = NULL;
261 :
262 44 : req->done_once = FALSE;
263 44 : req->using_req = FALSE;
264 : }
265 :
266 :
267 : void httpd_req_absolute_uri(struct Con *con, Httpd_req_data *req,
268 : Vstr_base *lfn, size_t pos, size_t len)
269 4672 : {
270 4672 : Vstr_base *data = con->evnt->io_r;
271 4672 : size_t apos = pos - 1;
272 4672 : size_t alen = lfn->len;
273 4672 : int has_schema = TRUE;
274 4672 : int has_abs_path = TRUE;
275 4672 : int has_data = TRUE;
276 4672 : unsigned int prev_num = 0;
277 :
278 4672 : if (!VPREFIX(lfn, pos, len, "http://"))
279 : {
280 3424 : has_schema = FALSE;
281 3424 : if (!VPREFIX(lfn, pos, len, "/")) /* relative pathname */
282 : {
283 1280 : if (VPREFIX(lfn, pos, len, "./"))
284 : {
285 64 : has_data = TRUE;
286 64 : vstr_del(lfn, pos, CLEN("./")); len -= CLEN("./");
287 64 : alen = lfn->len;
288 : }
289 : else
290 : {
291 1728 : while (VPREFIX(lfn, pos, len, "../"))
292 : {
293 0 : ++prev_num;
294 0 : vstr_del(lfn, pos, CLEN("../")); len -= CLEN("../");
295 : }
296 1152 : if (prev_num)
297 0 : alen = lfn->len;
298 : else
299 1152 : has_data = !!lfn->len;
300 : }
301 :
302 1216 : has_abs_path = FALSE;
303 : }
304 : }
305 :
306 4672 : if (!has_schema)
307 : {
308 3424 : vstr_add_cstr_buf(lfn, apos, "http://");
309 3424 : apos += lfn->len - alen;
310 3424 : alen = lfn->len;
311 3424 : httpd_sc_add_hostname(con, req, lfn, apos);
312 3424 : apos += lfn->len - alen;
313 : }
314 :
315 4672 : if (!has_abs_path)
316 : {
317 1216 : size_t path_len = req->path_len;
318 :
319 1216 : if (has_data || prev_num)
320 : {
321 608 : path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
322 :
323 1216 : while (path_len && prev_num--)
324 : {
325 0 : path_len -= vstr_spn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
326 0 : path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
327 : }
328 608 : if (!path_len) path_len = 1; /* make sure there is a / at the end */
329 : }
330 :
331 1216 : vstr_add_vstr(lfn, apos, data, req->path_pos, path_len, VSTR_TYPE_ADD_DEF);
332 : }
333 4672 : }
334 :
335 : /* doing http://www.example.com/foo/bar where bar is a dir is bad
336 : because all relative links will be relative to foo, not bar.
337 : Also note that location must be "http://www.example.com/foo/bar/" or maybe
338 : "http:/foo/bar/" (but we don't use the later anymore)
339 : */
340 : int http_req_chk_dir(struct Con *con, Httpd_req_data *req, const char *xmsg)
341 608 : {
342 608 : Vstr_base *fname = req->fname;
343 :
344 : /* fname == what was just passed to open() */
345 304 : ASSERT(fname->len);
346 :
347 608 : if (req->policy->use_secure_dirs && !req->conf_secure_dirs)
348 : { /* check if file exists before redirecting without leaking info. */
349 0 : const char *fname_cstr = NULL;
350 : struct stat64 d_stat[1];
351 :
352 0 : vstr_add_cstr_buf(fname, fname->len, "/");
353 0 : HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
354 :
355 0 : fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
356 0 : if (fname->conf->malloc_bad)
357 0 : return (http_fin_errmem_req(con, req));
358 :
359 0 : if ((stat64(fname_cstr, d_stat) == -1) || !S_ISREG(d_stat->st_mode))
360 0 : HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
361 : }
362 :
363 608 : vstr_del(fname, 1, fname->len);
364 608 : httpd_req_absolute_uri(con, req, fname, 1, fname->len);
365 :
366 : /* we got: http://foo/bar/
367 : * and so tried: http://foo/bar/index.html
368 : *
369 : * but foo/bar/index.html is a directory (fun), so redirect to:
370 : * http://foo/bar/index.html/
371 : */
372 608 : if (fname->len && (vstr_export_chr(fname, fname->len) == '/'))
373 144 : HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
374 :
375 608 : vstr_add_cstr_buf(fname, fname->len, "/");
376 :
377 608 : HTTPD_REDIR_MSG(req, 301, "dir -> filename");
378 :
379 608 : if (fname->conf->malloc_bad)
380 0 : return (http_fin_errmem_req(con, req));
381 :
382 608 : return (http_fin_err_req(con, req));
383 : }
384 :
385 : /* doing http://www.example.com/foo/bar/ when url is really
386 : http://www.example.com/foo/bar is a very simple mistake, so we almost
387 : certainly don't want a 404 */
388 : int http_req_chk_file(struct Con *con, Httpd_req_data *req, const char *xmsg)
389 64 : {
390 64 : Vstr_base *fname = req->fname;
391 64 : size_t len = 0;
392 :
393 : /* fname == what was just passed to open() */
394 32 : ASSERT(fname->len);
395 :
396 64 : if (!req->policy->use_friendly_dirs)
397 0 : HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
398 64 : else if (!req->conf_friendly_dirs)
399 : { /* check if file exists before redirecting without leaking info. */
400 64 : const char *fname_cstr = NULL;
401 : struct stat64 d_stat[1];
402 64 : len = vstr_cspn_cstr_chrs_rev(fname, 1, fname->len, "/") + 1;
403 :
404 : /* must be a filename, can't be toplevel */
405 64 : if ((len <= 1) || (len >= (fname->len - req->vhost_prefix_len)))
406 0 : HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
407 :
408 64 : vstr_sc_reduce(fname, 1, fname->len, len);
409 :
410 64 : fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
411 64 : if (fname->conf->malloc_bad)
412 0 : return (http_fin_errmem_req(con, req));
413 64 : if ((stat64(fname_cstr, d_stat) == -1) && !S_ISREG(d_stat->st_mode))
414 0 : HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
415 : }
416 :
417 64 : vstr_sub_cstr_ptr(fname, 1, fname->len, "./");
418 64 : httpd_req_absolute_uri(con, req, fname, 1, fname->len);
419 32 : assert(VSUFFIX(fname, 1, fname->len, "/"));
420 64 : vstr_sc_reduce(fname, 1, fname->len, strlen("/"));
421 :
422 64 : HTTPD_REDIR_MSG(req, 301, "filename -> dir");
423 :
424 64 : if (fname->conf->malloc_bad)
425 0 : return (http_fin_errmem_req(con, req));
426 :
427 64 : return (http_fin_err_req(con, req));
428 : }
429 :
430 : /* FIXME: maybe should be in the parse section...? */
431 : void http_req_split_method(struct Con *con, struct Httpd_req_data *req)
432 41761 : {
433 41761 : Vstr_base *s1 = con->evnt->io_r;
434 41761 : size_t pos = 1;
435 41761 : size_t len = req->len;
436 41761 : size_t el = 0;
437 41761 : size_t skip_len = 0;
438 41761 : unsigned int orig_num = req->sects->num;
439 :
440 41761 : el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL);
441 20825 : ASSERT(el >= pos);
442 41761 : len = el - pos; /* only parse the first line */
443 :
444 : /* split request */
445 41761 : if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
446 144 : return;
447 41617 : vstr_sects_add(req->sects, pos, el - pos);
448 41617 : len -= (el - pos); pos += (el - pos);
449 :
450 : /* just skip whitespace on method call... */
451 41617 : if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
452 41617 : { len -= skip_len; pos += skip_len; }
453 :
454 41617 : if (!len)
455 144 : goto req_line_parse_err;
456 :
457 41473 : if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
458 : {
459 672 : vstr_sects_add(req->sects, pos, len);
460 672 : len = 0;
461 : }
462 : else
463 : {
464 40801 : vstr_sects_add(req->sects, pos, el - pos);
465 40801 : len -= (el - pos); pos += (el - pos);
466 :
467 : /* just skip whitespace on method call... */
468 40801 : if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
469 40801 : { len -= skip_len; pos += skip_len; }
470 : }
471 :
472 41473 : if (len)
473 40801 : vstr_sects_add(req->sects, pos, len);
474 672 : else if (!req->policy->allow_http_0_9)
475 0 : return; /* we keep it, for logging */
476 : else
477 672 : req->ver_0_9 = TRUE;
478 :
479 41473 : if (req->sects->malloc_bad)
480 0 : goto req_line_parse_err;
481 :
482 20792 : return;
483 :
484 144 : req_line_parse_err:
485 144 : req->sects->num = orig_num;
486 : }
487 :
488 : void http_req_split_hdrs(struct Con *con, struct Httpd_req_data *req)
489 39241 : {
490 39241 : Vstr_base *s1 = con->evnt->io_r;
491 39241 : size_t pos = 1;
492 39241 : size_t len = req->len;
493 39241 : size_t el = 0;
494 39241 : size_t hpos = 0;
495 :
496 19628 : ASSERT(req->sects->num >= 3);
497 :
498 : /* skip first line */
499 39241 : el = (VSTR_SECTS_NUM(req->sects, req->sects->num)->pos +
500 : VSTR_SECTS_NUM(req->sects, req->sects->num)->len);
501 :
502 19628 : assert(VEQ(s1, el, CLEN(HTTP_EOL), HTTP_EOL));
503 39241 : len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
504 :
505 39241 : if (VPREFIX(s1, pos, len, HTTP_EOL))
506 3648 : return; /* end of headers */
507 :
508 17804 : ASSERT(vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_END_OF_REQUEST));
509 : /* split headers */
510 35593 : hpos = pos;
511 146475 : while ((el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL)) != pos)
512 : {
513 75289 : char chr = 0;
514 :
515 75289 : len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
516 :
517 75289 : chr = vstr_export_chr(s1, pos);
518 75289 : if (chr == ' ' || chr == '\t') /* header continues to next line */
519 2620 : continue;
520 :
521 70049 : vstr_sects_add(req->sects, hpos, el - hpos);
522 :
523 70049 : hpos = pos;
524 : }
525 : }
526 :
527 : /* try to set the content-type,
528 : * . first if it's manually set leave it,
529 : * . next try looking it up in the xattr for the file.
530 : * . next do a real "lookup" based on the filename
531 : * NOTE: If this lookup "fails" it still returns
532 : * the default content-type. So we just have to determine if we want to use
533 : * it or not. Can also return "content-types" like /404/ which returns a 404
534 : * error for the request */
535 : int http_req_content_type(Httpd_req_data *req)
536 27649 : {
537 27649 : const Vstr_base *vs1 = NULL;
538 27649 : size_t pos = 0;
539 27649 : size_t len = 0;
540 :
541 27649 : if (req->content_type_vs1) /* manually set */
542 1344 : return (TRUE);
543 :
544 26305 : if (req->policy->use_mime_xattr)
545 : { /* lookup mime/type in the xattr of the filename -- this is racey
546 : * but we want to parse the accept line before, open(), so we can't use
547 : * fgetxattr() anyway. */
548 : static const char key[] = "user.mime_type";
549 : char buf[1024]; /* guess */
550 26305 : ssize_t ret = -1;
551 26305 : const char *fname_cstr = NULL;
552 :
553 26305 : if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL)))
554 0 : return (FALSE);
555 :
556 26305 : fname_cstr = vstr_export_cstr_ptr(req->fname, 1, req->fname->len);
557 13160 : ASSERT(fname_cstr); /* must have been done before call */
558 :
559 : /* don't use lgetxattr() as it does nothing on Linux */
560 26305 : if ((ret = getxattr(fname_cstr, key, buf, sizeof(buf))) != -1)
561 : {
562 0 : pos = req->xtra_content->len + 1;
563 0 : len = ret;
564 0 : if (!vstr_add_buf(req->xtra_content, req->xtra_content->len, buf, len))
565 0 : return (FALSE);
566 :
567 0 : req->content_type_vs1 = req->xtra_content;
568 0 : req->content_type_pos = pos;
569 0 : req->content_type_len = len;
570 :
571 0 : HTTP_REQ__X_CONTENT_HDR_CHK(content_type);
572 :
573 0 : return (TRUE);
574 : }
575 26305 : else if (errno == ENOSYS)
576 0 : httpd_disable_getxattr();
577 26305 : else if ((errno == EOPNOTSUPP) || (errno == EPERM))
578 : { /* time limit the warn messages... */
579 : static time_t last = -1;
580 144 : time_t now = evnt_sc_time();
581 :
582 144 : if ((last == -1) || (difftime(last, now) > (10 * 60)))
583 : {
584 12 : vlg_warn(vlg, "getxattr($<vstr.all:%p>, %s): %m\n", req->fname, key);
585 12 : last = now;
586 : }
587 : }
588 : }
589 :
590 26305 : mime_types_match(req->policy->mime_types,
591 : req->fname, 1, req->fname->len, &vs1, &pos, &len);
592 26305 : if (!len)
593 : {
594 3665 : req->parse_accept = FALSE;
595 3665 : return (TRUE);
596 : }
597 :
598 22640 : if ((vstr_export_chr(vs1, pos) == '/') && (len > 2) &&
599 : (vstr_export_chr(vs1, vstr_sc_poslast(pos, len)) == '/'))
600 : {
601 432 : size_t num_len = 1;
602 : static const char xmsg[] = "Mime/Type";
603 :
604 432 : len -= 2;
605 432 : ++pos;
606 432 : req->user_return_error_code = TRUE;
607 432 : req->direct_filename = FALSE;
608 432 : switch (vstr_parse_uint(vs1, pos, len, 0, &num_len, NULL))
609 : {
610 48 : case 400: if (num_len == len) HTTPD_ERR_MSG_RET(req, 400, xmsg, FALSE);
611 144 : case 403: if (num_len == len) HTTPD_ERR_MSG_RET(req, 403, xmsg, FALSE);
612 48 : case 404: if (num_len == len) HTTPD_ERR_MSG_RET(req, 404, xmsg, FALSE);
613 48 : case 410: if (num_len == len) HTTPD_ERR_MSG_RET(req, 410, xmsg, FALSE);
614 48 : case 500: if (num_len == len) HTTPD_ERR_MSG_RET(req, 500, xmsg, FALSE);
615 48 : case 503: if (num_len == len) HTTPD_ERR_MSG_RET(req, 503, xmsg, FALSE);
616 :
617 : default: /* just ignore any other content */
618 48 : req->user_return_error_code = FALSE;
619 48 : return (TRUE);
620 : }
621 : }
622 :
623 22208 : req->content_type_vs1 = vs1;
624 22208 : req->content_type_pos = pos;
625 22208 : req->content_type_len = len;
626 :
627 22208 : return (TRUE);
628 : }
629 :
630 : static void httpd__req_etag_hex_num(Vstr_base *vs1, uintmax_t val, int more)
631 2688 : {
632 : char buf[(sizeof(uintmax_t) * CHAR_BIT) + 1];
633 2688 : size_t len = 0;
634 :
635 2688 : len = vstr_sc_conv_num_uintmax(buf, sizeof(buf), val, "0123456789abcdef", 16);
636 2688 : vstr_add_buf(vs1, vs1->len, buf, len);
637 2688 : if (more)
638 1344 : vstr_add_cstr_buf(vs1, vs1->len, "-");
639 2688 : }
640 :
641 : static int httpd__req_etag_auto(struct Httpd_req_data *req)
642 1344 : {
643 1344 : size_t xpos = 0;
644 1344 : Vstr_base *vs1 = NULL;
645 :
646 672 : ASSERT(!req->etag_vs1 && req->policy->etag_auto_type);
647 :
648 1344 : if (!(xpos = http_req_xtra_content(req, NULL, 0, &req->etag_len)))
649 0 : return (FALSE);
650 1344 : vs1 = req->xtra_content;
651 :
652 : /* If it's too soon, make it weak */
653 1344 : if (difftime(req->now, req->f_stat->st_mtime) <= 1)
654 0 : vstr_add_cstr_buf(vs1, vs1->len, "W/");
655 :
656 1344 : vstr_add_cstr_buf(vs1, vs1->len, "\"");
657 1344 : switch (req->policy->etag_auto_type)
658 : {
659 : case HTTPD_ETAG_TYPE_AUTO_DISM:
660 0 : httpd__req_etag_hex_num(vs1, req->f_stat->st_dev, TRUE);
661 : /* FALL THROUGH */
662 : case HTTPD_ETAG_TYPE_AUTO_ISM:
663 0 : httpd__req_etag_hex_num(vs1, req->f_stat->st_ino, TRUE);
664 : /* FALL THROUGH */
665 : case HTTPD_ETAG_TYPE_AUTO_SM:
666 1344 : httpd__req_etag_hex_num(vs1, req->f_stat->st_size, TRUE);
667 1344 : httpd__req_etag_hex_num(vs1, req->f_stat->st_mtime, FALSE);
668 : /* Use st_mtime.tv_nsec ? */
669 :
670 0 : ASSERT_NO_SWITCH_DEF();
671 : }
672 1344 : vstr_add_cstr_buf(vs1, vs1->len, "\"");
673 :
674 1344 : req->etag_vs1 = vs1;
675 1344 : req->etag_pos = xpos;
676 1344 : req->etag_len = (req->xtra_content->len - xpos) + 1;
677 :
678 1344 : return (!vs1->conf->malloc_bad);
679 : }
680 :
681 : #define HTTPD__HD_EQ(x) \
682 : VEQ(hdrs, h_ ## x ->pos, h_ ## x ->len, date)
683 :
684 : /* gets here if the GET/HEAD response is ok, we test for caching etc. using the
685 : * if-* headers */
686 : /* FALSE = 412 Precondition Failed */
687 : static int http_response_ok(struct Con *con, struct Httpd_req_data *req,
688 : unsigned int *http_ret_code,
689 : const char ** http_ret_line)
690 22089 : {
691 22089 : const Vstr_base *hdrs = con->evnt->io_r;
692 22089 : time_t mtime = req->f_stat->st_mtime;
693 22089 : Vstr_sect_node *h_ims = req->http_hdrs->hdr_if_modified_since;
694 22089 : Vstr_sect_node *h_ir = req->http_hdrs->hdr_if_range;
695 22089 : Vstr_sect_node *h_iums = req->http_hdrs->hdr_if_unmodified_since;
696 22089 : Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
697 22089 : Vstr_base *comb = req->http_hdrs->multi->comb;
698 22089 : Vstr_sect_node *h_im = req->http_hdrs->multi->hdr_if_match;
699 22089 : Vstr_sect_node *h_inm = req->http_hdrs->multi->hdr_if_none_match;
700 22089 : int h_ir_tst = FALSE;
701 22089 : int h_iums_tst = FALSE;
702 22089 : int req_if_range = FALSE;
703 22089 : int cached_output = FALSE;
704 22089 : const char *date = NULL;
705 :
706 22089 : if (HTTPD_VER_GE_1_1(req) && h_iums->pos)
707 432 : h_iums_tst = TRUE;
708 :
709 22089 : if (HTTPD_VER_GE_1_1(req) && h_ir->pos && h_r->pos)
710 936 : h_ir_tst = TRUE;
711 :
712 : /* assumes time doesn't go backwards ... From rfc2616:
713 : *
714 : * Note: When handling an If-Modified-Since header field, some
715 : * servers will use an exact date comparison function, rather than a
716 : * less-than function, for deciding whether to send a 304 (Not
717 : * Modified) response. To get best results when sending an If-
718 : * Modified-Since header field for cache validation, clients are
719 : * advised to use the exact date string received in a previous Last-
720 : * Modified header field whenever possible.
721 : */
722 22089 : if (difftime(req->now, mtime) > 0)
723 : { /* if mtime in future, or now ... don't allow checking */
724 22088 : Date_store *ds = httpd_opts->date;
725 :
726 22088 : date = date_rfc1123(ds, mtime);
727 22088 : if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
728 312 : cached_output = TRUE;
729 22088 : if (h_iums_tst && HTTPD__HD_EQ(iums))
730 144 : return (FALSE);
731 21944 : if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
732 312 : req_if_range = TRUE;
733 :
734 21944 : date = date_rfc850(ds, mtime);
735 21944 : if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
736 144 : cached_output = TRUE;
737 21944 : if (h_iums_tst && HTTPD__HD_EQ(iums))
738 144 : return (FALSE);
739 21800 : if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
740 144 : req_if_range = TRUE;
741 :
742 21800 : date = date_asctime(ds, mtime);
743 21800 : if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
744 144 : cached_output = TRUE;
745 21800 : if (h_iums_tst && HTTPD__HD_EQ(iums))
746 144 : return (FALSE);
747 21656 : if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
748 144 : req_if_range = TRUE;
749 : }
750 :
751 30862 : if (HTTPD_VER_GE_1_1(req))
752 : {
753 19017 : const Vstr_base *vs1 = NULL;
754 19017 : size_t pos = 0;
755 19017 : size_t len = 0;
756 :
757 19017 : if (req->policy->etag_auto_type && !req->etag_vs1)
758 : {
759 1344 : if (!httpd__req_etag_auto(req))
760 : {
761 0 : con->evnt->io_w->conf->malloc_bad = TRUE;
762 0 : return (TRUE); /* dealt with in http_req_op_get, can't continue */
763 : }
764 :
765 1344 : req->etag_time = mtime;
766 : }
767 19017 : if (req->etag_vs1 && (req->etag_time >= mtime))
768 : {
769 2912 : vs1 = req->etag_vs1;
770 2912 : pos = req->etag_pos;
771 2912 : len = req->etag_len;
772 : }
773 :
774 19017 : if (h_ir_tst && !req_if_range &&
775 : httpd_match_etags(req,
776 : hdrs, h_ir->pos, h_ir->len, vs1, pos, len, FALSE))
777 192 : req_if_range = TRUE;
778 :
779 19017 : if (h_ir_tst && !req_if_range)
780 144 : h_r->pos = 0;
781 :
782 : /* #13.3.3 says: don't trust weak for "complex" queries, ie. byteranges */
783 19017 : if (h_inm->pos && (VEQ(hdrs, h_inm->pos, h_inm->len, "*") ||
784 : httpd_match_etags(req, comb, h_inm->pos, h_inm->len,
785 : vs1, pos, len, !h_r->pos)))
786 1000 : cached_output = TRUE; /* Note: should return 412 for POST/PUT */
787 :
788 : /* #14.24 says: must use strong comparison, and also...
789 : If the request would, without the If-Match header field, result in
790 : anything other than a 2xx or 412 status, then the If-Match header
791 : MUST be ignored. */
792 19017 : if (!cached_output &&
793 : h_im->pos && !(VEQ(hdrs, h_im->pos, h_im->len, "*") ||
794 : httpd_match_etags(req, comb, h_im->pos, h_im->len,
795 : vs1, pos, len, FALSE)))
796 592 : return (FALSE);
797 : }
798 2640 : else if (h_ir_tst && !req_if_range)
799 0 : h_r->pos = 0;
800 :
801 21065 : if (cached_output)
802 : {
803 1600 : req->head_op = TRUE;
804 1600 : *http_ret_code = 304;
805 1600 : *http_ret_line = "Not Modified";
806 : }
807 19465 : else if (h_r->pos)
808 : {
809 3288 : *http_ret_code = 206;
810 3288 : *http_ret_line = "Partial content";
811 : }
812 :
813 21065 : return (TRUE);
814 : }
815 :
816 : int http_req_1_x(struct Con *con, Httpd_req_data *req,
817 : unsigned int *http_ret_code,
818 : const char **http_ret_line)
819 22665 : {
820 22665 : Vstr_base *out = con->evnt->io_w;
821 22665 : Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
822 22665 : time_t mtime = -1;
823 :
824 22665 : if (HTTPD_VER_GE_1_1(req) && req->http_hdrs->hdr_expect->len)
825 : /* I'm pretty sure we can ignore 100-continue, as no request will
826 : * have a body */
827 144 : HTTPD_ERR_RET(req, 417, FALSE);
828 :
829 22521 : httpd_parse_sc_try_fd_encoding(con, req, req->f_stat, &req->f_stat_st_size,
830 : req->fname);
831 :
832 22521 : if (req->policy->use_err_406 &&
833 : !req->content_encoding_identity &&
834 : !req->content_encoding_bzip2 && !req->content_encoding_gzip)
835 144 : HTTPD_ERR_MSG_RET(req, 406, "Encoding", FALSE);
836 :
837 22377 : if (h_r->pos)
838 : {
839 5336 : int ret_code = 0;
840 :
841 5384 : if (!(req->policy->use_range &&
842 : (HTTPD_VER_GE_1_1(req) || req->policy->use_range_1_0)))
843 48 : h_r->pos = 0;
844 5288 : else if (!(ret_code = http_parse_range(con, req)))
845 1424 : h_r->pos = 0;
846 2668 : ASSERT(!ret_code || (ret_code == 200) || (ret_code == 416));
847 5336 : if (ret_code == 416)
848 : {
849 432 : if (!req->http_hdrs->hdr_if_range->pos)
850 288 : HTTPD_ERR_RET(req, 416, FALSE);
851 144 : h_r->pos = 0;
852 : }
853 : }
854 :
855 22089 : if (!http_response_ok(con, req, http_ret_code, http_ret_line))
856 1024 : HTTPD_ERR_RET(req, 412, FALSE);
857 :
858 21065 : if (!h_r->pos)
859 17777 : httpd_serv_file_sects_none(con, req, req->f_stat_st_size);
860 :
861 21065 : httpd_serv_call_file_init(con, req, http_ret_code, http_ret_line);
862 :
863 10540 : ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
864 10540 : ASSERT(!con->fs_off);
865 :
866 21065 : mtime = req->f_stat->st_mtime;
867 21065 : http_app_def_hdrs(con, req, *http_ret_code, *http_ret_line,
868 : mtime, NULL, TRUE, http_serv_file_len(con, req));
869 21065 : if (h_r->pos && !con->use_mpbr)
870 3096 : http_app_hdr_fmt(out, "Content-Range", "%s %ju-%ju/%ju", "bytes",
871 : con->fs->off, con->fs->off + (con->fs->len - 1),
872 : (uintmax_t)req->f_stat_st_size);
873 21065 : if (req->content_location_vs1)
874 0 : http_app_hdr_vstr_def(out, "Content-Location",
875 : HTTP__XTRA_HDR_PARAMS(req, content_location));
876 21065 : http_app_hdrs_url(con, req);
877 21065 : http_app_hdrs_file(con, req);
878 :
879 21065 : http_app_end_hdrs(out);
880 :
881 21065 : if (!req->head_op && h_r->pos && con->use_mpbr)
882 : {
883 128 : con->mpbr_fs_len = req->f_stat_st_size;
884 128 : http_app_hdrs_mpbr(con, con->fs);
885 : }
886 :
887 21065 : return (TRUE);
888 : }
889 :
890 : static int httpd_req_add_vhost(struct Con *con, struct Httpd_req_data *req)
891 33505 : {
892 33505 : Vstr_base *data = con->evnt->io_r;
893 33505 : Vstr_base *fname = req->fname;
894 33505 : Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
895 33505 : size_t h_h_pos = h_h->pos;
896 33505 : size_t h_h_len = h_h->len;
897 33505 : size_t orig_len = 0;
898 33505 : size_t dots = 0;
899 :
900 : /* a lot of clients will pass example.com. for example.com ... fix them
901 : * this can happen more than one time Eg. "wget www.and.org.." */
902 33505 : if (h_h_len)
903 : {
904 23249 : dots = vstr_spn_cstr_chrs_rev(data, h_h_pos, h_h_len, ".");
905 23249 : if (dots == h_h_len)
906 352 : h_h_len = 1; /* give 400s to hostname "." */
907 : else
908 : {
909 11456 : ASSERT(dots < h_h_len);
910 :
911 22897 : h_h_len -= dots;
912 :
913 22897 : if (req->policy->use_canonize_host)
914 : {
915 288 : if (VIPREFIX(data, h_h_pos, h_h_len, "www."))
916 144 : { h_h_len -= CLEN("www."); h_h_pos += CLEN("www."); }
917 : }
918 : }
919 :
920 23249 : h_h->pos = h_h_pos;
921 23249 : h_h->len = h_h_len;
922 : }
923 :
924 33505 : if (!req->policy->use_vhosts_name)
925 929 : return (TRUE);
926 :
927 32576 : orig_len = fname->len;
928 32576 : if (!http_serv_add_vhost(con, req, fname, 0, TRUE))
929 608 : return (FALSE);
930 :
931 31968 : vstr_add_cstr_ptr(fname, 0, "/");
932 :
933 31968 : req->vhost_prefix_len = (fname->len - orig_len);
934 :
935 31968 : return (TRUE);
936 : }
937 :
938 : /* Decode url-path,
939 : check url-path for a bunch of problems,
940 : if vhosts is on add vhost prefix,
941 : Note we don't do dir_filename additions yet */
942 : int http_req_make_path(struct Con *con, Httpd_req_data *req)
943 34657 : {
944 34657 : Vstr_base *data = con->evnt->io_r;
945 34657 : Vstr_base *fname = req->fname;
946 :
947 17336 : ASSERT(!fname->len);
948 :
949 17336 : assert(VPREFIX(data, req->path_pos, req->path_len, "/") ||
950 : VEQ(data, req->path_pos, req->path_len, "*"));
951 :
952 34657 : if (req->chk_encoded_slash &&
953 : vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2f"))
954 1152 : HTTPD_ERR_MSG_RET(req, 403, "Path has encoded /", FALSE);
955 33505 : if (req->chk_encoded_dot &&
956 : vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2e"))
957 0 : HTTPD_ERR_MSG_RET(req, 403, "Path has encoded .", FALSE);
958 33505 : req->chked_encoded_path = TRUE;
959 :
960 33505 : vstr_add_vstr(fname, 0,
961 : data, req->path_pos, req->path_len, VSTR_TYPE_ADD_BUF_PTR);
962 33505 : vstr_conv_decode_uri(fname, 1, fname->len);
963 :
964 33505 : if (fname->conf->malloc_bad) /* dealt with as errmem_req() later */
965 0 : return (TRUE);
966 :
967 : /* NOTE: need to split function here so we can more efficently alter the
968 : * path. */
969 33505 : if (!httpd_req_add_vhost(con, req))
970 608 : return (FALSE);
971 :
972 : /* check posix path ... including hostname, for NIL and path escapes */
973 32897 : if (vstr_srch_chr_fwd(fname, 1, fname->len, 0))
974 144 : HTTPD_ERR_MSG_RET(req, 403, "Path has NIL", FALSE);
975 :
976 : /* web servers don't have relative paths, so /./ and /../ aren't "special" */
977 32753 : if (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/../") ||
978 : VSUFFIX(req->fname, 1, req->fname->len, "/.."))
979 432 : HTTPD_ERR_MSG_RET(req, 403, "Path has /../", FALSE);
980 32321 : if (req->policy->chk_dot_dir &&
981 : (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/./") ||
982 : VSUFFIX(req->fname, 1, req->fname->len, "/.")))
983 0 : HTTPD_ERR_MSG_RET(req, 403, "Path has /./", FALSE);
984 :
985 16168 : ASSERT(fname->len);
986 16168 : assert(VPREFIX(fname, 1, fname->len, "/") ||
987 : VEQ(fname, 1, fname->len, "*") ||
988 : fname->conf->malloc_bad);
989 :
990 32321 : if (fname->conf->malloc_bad)
991 0 : return (TRUE);
992 :
993 32321 : return (TRUE);
994 : }
995 :
996 : size_t http_req_xtra_content(Httpd_req_data *req, const Vstr_base *s1,
997 : size_t pos, size_t *len)
998 13376 : {
999 13376 : Vstr_base *xs1 = req->xtra_content;
1000 13376 : size_t ret = 0;
1001 :
1002 6688 : ASSERT(len);
1003 6688 : ASSERT((s1 == xs1) || !s1);
1004 6688 : ASSERT(s1 || !*len);
1005 :
1006 13376 : if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL)))
1007 0 : return (0);
1008 13376 : xs1 = req->xtra_content;
1009 :
1010 13376 : if (!s1 || !*len)
1011 11008 : return (xs1->len + 1);
1012 :
1013 2368 : if (vstr_sc_poslast(pos, *len) == xs1->len)
1014 640 : return (pos); /* we are last, so just overwrite */
1015 :
1016 : /* we aren't, so copy to last place */
1017 1728 : ret = xs1->len + 1;
1018 1728 : if (!vstr_add_vstr(xs1, xs1->len, s1, pos, *len, VSTR_TYPE_ADD_BUF_REF))
1019 0 : return (0);
1020 :
1021 1728 : return (ret);
1022 : }
|