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 HTTPD APIs, only really 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 : #ifndef POSIX_FADV_SEQUENTIAL
40 : # define posix_fadvise64(x1, x2, x3, x4) (errno = ENOSYS, -1)
41 : #endif
42 :
43 : #if ! COMPILE_DEBUG
44 : # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
45 : # define HTTP_CONF_SAFE_PRINT_REQ TRUE
46 : #else
47 : # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
48 : # define HTTP_CONF_SAFE_PRINT_REQ FALSE
49 : #endif
50 : #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
51 :
52 : #define HTTPD_CONF_ZIP_LIMIT_MIN 8
53 :
54 : #define CLEN COMPILE_STRLEN
55 :
56 : /* is the cstr a prefix of the vstr */
57 : #define VPREFIX(vstr, p, l, cstr) \
58 : (((l) >= CLEN(cstr)) && \
59 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
60 : /* is the cstr a suffix of the vstr */
61 : #define VSUFFIX(vstr, p, l, cstr) \
62 : (((l) >= CLEN(cstr)) && \
63 : vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
64 :
65 : /* is the cstr a prefix of the vstr, no case */
66 : #define VIPREFIX(vstr, p, l, cstr) \
67 : (((l) >= CLEN(cstr)) && \
68 : vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
69 :
70 : /* for simplicity */
71 : #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
72 : #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
73 :
74 : #define HTTP__HDR_SET(req, h, p, l) do { \
75 : (req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
76 : (req)-> http_hdrs -> hdr_ ## h ->len = (l); \
77 : } while (FALSE)
78 : #define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
79 : (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
80 : (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
81 : } while (FALSE)
82 :
83 : #define HTTP__XTRA_HDR_INIT(x) do { \
84 : req-> x ## _vs1 = NULL; \
85 : req-> x ## _pos = 0; \
86 : req-> x ## _len = 0; \
87 : } while (FALSE)
88 :
89 : #include <syslog.h>
90 :
91 : HTTPD_CONF_MAIN_DECL_OPTS(httpd_opts);
92 :
93 : static Vlg *vlg = NULL;
94 :
95 :
96 : void httpd_init(Vlg *passed_vlg)
97 64 : {
98 32 : ASSERT(passed_vlg && !vlg);
99 64 : vlg = passed_vlg;
100 64 : httpd_app_init(vlg);
101 64 : httpd_parse_init(vlg);
102 64 : httpd_req_init(vlg);
103 64 : }
104 :
105 : void httpd_exit(void)
106 44 : {
107 22 : ASSERT(vlg);
108 44 : vlg = NULL;
109 44 : httpd_parse_exit();
110 44 : httpd_req_exit();
111 44 : httpd_app_exit();
112 44 : }
113 :
114 : /* prints out headers in human friedly way for log files */
115 : #define PCUR (pos + (base->len - orig_len))
116 : static int http_app_vstr_escape(Vstr_base *base, size_t pos,
117 : Vstr_base *sf, size_t sf_pos, size_t sf_len)
118 178076 : {
119 178076 : unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
120 : Vstr_iter iter[1];
121 178076 : size_t orig_len = base->len;
122 178076 : int saved_malloc_bad = FALSE;
123 178076 : size_t norm_chr = 0;
124 :
125 178076 : if (!sf_len) /* hack around !sf_pos */
126 93570 : return (TRUE);
127 :
128 84506 : if (!vstr_iter_fwd_beg(sf, sf_pos, sf_len, iter))
129 0 : return (FALSE);
130 :
131 84506 : saved_malloc_bad = base->conf->malloc_bad;
132 84506 : base->conf->malloc_bad = FALSE;
133 771418 : while (sf_len)
134 : { /* assumes ASCII */
135 602406 : char scan = vstr_iter_fwd_chr(iter, NULL);
136 :
137 1204412 : if ((scan >= ' ') && (scan <= '~') && (scan != '"') && (scan != '\\'))
138 602006 : ++norm_chr;
139 : else
140 : {
141 400 : vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
142 400 : sf_pos += norm_chr;
143 400 : norm_chr = 0;
144 :
145 : if (0) {}
146 400 : else if (scan == '"') vstr_add_cstr_buf(base, PCUR, "\\\"");
147 144 : else if (scan == '\\') vstr_add_cstr_buf(base, PCUR, "\\\\");
148 144 : else if (scan == '\t') vstr_add_cstr_buf(base, PCUR, "\\t");
149 144 : else if (scan == '\v') vstr_add_cstr_buf(base, PCUR, "\\v");
150 144 : else if (scan == '\r') vstr_add_cstr_buf(base, PCUR, "\\r");
151 144 : else if (scan == '\n') vstr_add_cstr_buf(base, PCUR, "\\n");
152 144 : else if (scan == '\b') vstr_add_cstr_buf(base, PCUR, "\\b");
153 : else
154 144 : vstr_add_sysfmt(base, PCUR, "\\x%02hhx", scan);
155 400 : ++sf_pos;
156 : }
157 :
158 602406 : --sf_len;
159 : }
160 :
161 84506 : vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
162 :
163 84506 : if (base->conf->malloc_bad)
164 0 : return (FALSE);
165 :
166 84506 : base->conf->malloc_bad = saved_malloc_bad;
167 84506 : return (TRUE);
168 : }
169 : #undef PCUR
170 :
171 : static int http__fmt__add_vstr_add_vstr(Vstr_base *base, size_t pos,
172 : Vstr_fmt_spec *spec)
173 159524 : {
174 159524 : Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
175 159524 : size_t sf_pos = VSTR_FMT_CB_ARG_VAL(spec, size_t, 1);
176 159524 : size_t sf_len = VSTR_FMT_CB_ARG_VAL(spec, size_t, 2);
177 :
178 159524 : return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
179 : }
180 : int http_fmt_add_vstr_add_vstr(Vstr_conf *conf, const char *name)
181 1344 : {
182 1344 : return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_vstr,
183 : VSTR_TYPE_FMT_PTR_VOID,
184 : VSTR_TYPE_FMT_SIZE_T,
185 : VSTR_TYPE_FMT_SIZE_T,
186 : VSTR_TYPE_FMT_END));
187 : }
188 : static int http__fmt__add_vstr_add_sect_vstr(Vstr_base *base, size_t pos,
189 : Vstr_fmt_spec *spec)
190 18552 : {
191 18552 : Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
192 18552 : Vstr_sects *sects = VSTR_FMT_CB_ARG_PTR(spec, 1);
193 18552 : unsigned int num = VSTR_FMT_CB_ARG_VAL(spec, unsigned int, 2);
194 18552 : size_t sf_pos = VSTR_SECTS_NUM(sects, num)->pos;
195 18552 : size_t sf_len = VSTR_SECTS_NUM(sects, num)->len;
196 :
197 18552 : return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
198 : }
199 : int http_fmt_add_vstr_add_sect_vstr(Vstr_conf *conf, const char *name)
200 1344 : {
201 1344 : return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_sect_vstr,
202 : VSTR_TYPE_FMT_PTR_VOID,
203 : VSTR_TYPE_FMT_PTR_VOID,
204 : VSTR_TYPE_FMT_UINT,
205 : VSTR_TYPE_FMT_END));
206 : }
207 :
208 : static void http_vlg_def(struct Con *con, struct Httpd_req_data *req, int meth)
209 39881 : {
210 39881 : Vstr_base *data = con->evnt->io_r;
211 39881 : Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
212 39881 : Vstr_sect_node *h_ua = req->http_hdrs->hdr_ua;
213 39881 : Vstr_sect_node *h_r = req->http_hdrs->hdr_referer;
214 :
215 39881 : vlg_info(vlg, (" host[\"$<http-esc.vstr:%p%zu%zu>\"]"
216 : " UA[\"$<http-esc.vstr:%p%zu%zu>\"]"
217 : " ref[\"$<http-esc.vstr:%p%zu%zu>\"]"),
218 : data, h_h->pos, h_h->len,
219 : data, h_ua->pos, h_ua->len,
220 : data, h_r->pos, h_r->len);
221 :
222 39881 : if (meth && (req->sects->num >= 1))
223 17784 : vlg_info(vlg, " meth[\"$<http-esc.vstr.sect:%p%p%u>\"]",
224 : data, req->sects, 1U);
225 :
226 39881 : if (req->ver_0_9)
227 672 : vlg_info(vlg, " ver[\"HTTP/0.9\"]");
228 : else
229 : {
230 19612 : ASSERT(req->sects->num >= 3);
231 39209 : vlg_info(vlg, " ver[\"$<vstr.sect:%p%p%u>\"]", data, req->sects, 3);
232 : }
233 :
234 39881 : vlg_info(vlg, ": $<http-esc.vstr:%p%zu%zu>\n",
235 : data, req->path_pos, req->path_len);
236 39881 : }
237 :
238 : static struct File_sect *httpd__fd_next(struct Con *con)
239 47125 : {
240 47125 : struct File_sect *fs = NULL;
241 :
242 23570 : ASSERT(con->fs &&
243 : (con->fs_off <= con->fs_num) && (con->fs_num <= con->fs_sz));
244 :
245 47125 : if (++con->fs_off >= con->fs_num)
246 : {
247 46741 : fs = &con->fs[con->fs_off - 1];
248 46741 : fs->len = 0;
249 46741 : if (fs->fd != -1)
250 25641 : close(fs->fd);
251 46741 : fs->fd = -1;
252 :
253 46741 : fs = con->fs;
254 46741 : fs->fd = -1;
255 46741 : fs->len = 0;
256 :
257 46741 : con->fs_off = 0;
258 46741 : con->fs_num = 0;
259 :
260 46741 : con->use_mpbr = FALSE;
261 23378 : ASSERT(!(con->mpbr_fs_len = 0));
262 :
263 46741 : if (con->mpbr_ct)
264 448 : vstr_del(con->mpbr_ct, 1, con->mpbr_ct->len);
265 :
266 46741 : return (NULL);
267 : }
268 :
269 : /* only allow multipart/byterange atm. where all fd's are the same */
270 192 : ASSERT(con->fs[con->fs_off - 1].fd == con->fs[con->fs_off].fd);
271 :
272 384 : fs = &con->fs[con->fs_off];
273 384 : if (con->use_posix_fadvise)
274 288 : posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
275 :
276 384 : return (fs);
277 : }
278 :
279 : void httpd_fin_fd_close(struct Con *con)
280 46613 : {
281 46613 : con->use_posix_fadvise = FALSE;
282 46709 : while (httpd__fd_next(con))
283 : { /* do nothing */ }
284 46613 : }
285 :
286 : static int http_fin_req(struct Con *con, Httpd_req_data *req)
287 40231 : {
288 40231 : Vstr_base *out = con->evnt->io_w;
289 :
290 20123 : ASSERT(!out->conf->malloc_bad);
291 40231 : http_parse_clear_hdrs(req);
292 :
293 : /* Note: Not resetting con->parsed_method_ver_1_0,
294 : * if it's non-0.9 now it should continue to be */
295 :
296 40231 : if (!con->keep_alive) /* all input is here */
297 : {
298 11870 : evnt_wait_cntl_del(con->evnt, POLLIN);
299 11870 : req->len = con->evnt->io_r->len; /* delete it all */
300 : }
301 :
302 40231 : vstr_del(con->evnt->io_r, 1, req->len);
303 :
304 40231 : evnt_put_pkt(con->evnt);
305 :
306 40231 : if (req->policy->use_tcp_cork)
307 39815 : evnt_fd_set_cork(con->evnt, TRUE);
308 :
309 40231 : http_req_free(req);
310 :
311 40231 : return (httpd_serv_send(con));
312 : }
313 :
314 : static int http_fin_fd_req(struct Con *con, Httpd_req_data *req)
315 23273 : {
316 11644 : ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
317 11644 : ASSERT(!con->fs_off);
318 :
319 35261 : if (req->head_op || con->use_mmap || !con->fs->len)
320 11988 : httpd_fin_fd_close(con);
321 11285 : else if ((con->use_posix_fadvise = req->policy->use_posix_fadvise))
322 : {
323 11285 : struct File_sect *fs = con->fs;
324 11285 : posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
325 : }
326 :
327 23273 : return (http_fin_req(con, req));
328 : }
329 :
330 : static int http_con_cleanup(struct Con *con, Httpd_req_data *req)
331 32 : {
332 32 : con->evnt->io_r->conf->malloc_bad = FALSE;
333 32 : con->evnt->io_w->conf->malloc_bad = FALSE;
334 :
335 32 : http_parse_clear_hdrs(req);
336 32 : http_req_free(req);
337 :
338 32 : return (FALSE);
339 : }
340 :
341 : static int http_con_close_cleanup(struct Con *con, Httpd_req_data *req)
342 0 : {
343 0 : httpd_fin_fd_close(con);
344 0 : return (http_con_cleanup(con, req));
345 : }
346 :
347 : static void httpd__disable_sendfile(void)
348 0 : {
349 0 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
350 :
351 0 : while (scan)
352 : {
353 0 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
354 0 : tmp->use_sendfile = FALSE;
355 0 : scan = scan->next;
356 : }
357 0 : }
358 :
359 : static void httpd__disable_mmap(void)
360 0 : {
361 0 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
362 :
363 0 : while (scan)
364 : {
365 0 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
366 0 : tmp->use_mmap = FALSE;
367 0 : scan = scan->next;
368 : }
369 0 : }
370 :
371 : void httpd_disable_getxattr(void)
372 0 : {
373 0 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
374 :
375 0 : while (scan)
376 : {
377 0 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
378 0 : tmp->use_mime_xattr = FALSE;
379 0 : scan = scan->next;
380 : }
381 0 : }
382 :
383 : static void httpd_serv_call_mmap(struct Con *con, struct Httpd_req_data *req,
384 : struct File_sect *fs)
385 12217 : {
386 : static long pagesz = 0;
387 12217 : Vstr_base *data = con->evnt->io_r;
388 12217 : uintmax_t mmoff = fs->off;
389 12217 : uintmax_t mmlen = fs->len;
390 :
391 6116 : ASSERT(!req->f_mmap || !req->f_mmap->len);
392 6116 : ASSERT(!con->use_mmap);
393 6116 : ASSERT(!req->head_op);
394 :
395 12217 : if (con->use_sendfile)
396 7509 : return;
397 :
398 4708 : if (con->fs_num > 1)
399 0 : return;
400 :
401 4708 : if (!pagesz)
402 10 : pagesz = sysconf(_SC_PAGESIZE);
403 4708 : if (pagesz == -1)
404 0 : httpd__disable_mmap();
405 :
406 4708 : if (!req->policy->use_mmap ||
407 : (mmlen < HTTP_CONF_MMAP_LIMIT_MIN) || (mmlen > HTTP_CONF_MMAP_LIMIT_MAX))
408 2326 : return;
409 :
410 : /* mmap offset needs to be aligned - so tweak offset before and after */
411 388 : mmoff /= pagesz;
412 388 : mmoff *= pagesz;
413 360 : ASSERT(mmoff <= fs->off);
414 388 : mmlen += fs->off - mmoff;
415 :
416 388 : if (!req->f_mmap && !(req->f_mmap = vstr_make_base(data->conf)))
417 0 : VLG_WARN_RET_VOID((vlg, /* fall back to read */
418 : "failed to allocate mmap Vstr.\n"));
419 360 : ASSERT(!req->f_mmap->len);
420 :
421 388 : if (!vstr_sc_mmap_fd(req->f_mmap, 0, fs->fd, mmoff, mmlen, NULL))
422 : {
423 0 : if (errno == ENOSYS) /* also logs it */
424 0 : httpd__disable_mmap();
425 :
426 0 : VLG_WARN_RET_VOID((vlg, /* fall back to read */
427 : "mmap($<http-esc.vstr:%p%zu%zu>,"
428 : "(%ju,%ju)->(%ju,%ju)): %m\n",
429 : req->fname, (size_t)1, req->fname->len,
430 : fs->off, fs->len, mmoff, mmlen));
431 : }
432 :
433 388 : con->use_mmap = TRUE;
434 388 : vstr_del(req->f_mmap, 1, fs->off - mmoff); /* remove alignment */
435 :
436 360 : ASSERT(req->f_mmap->len == fs->len);
437 : }
438 :
439 : static int httpd_serv_call_seek(struct Con *con, struct File_sect *fs)
440 10745 : {
441 10745 : if (con->use_mmap || con->use_sendfile)
442 6425 : return (TRUE);
443 :
444 4320 : if (fs->off && fs->len && (lseek64(fs->fd, fs->off, SEEK_SET) == -1))
445 0 : return (FALSE);
446 :
447 4320 : return (TRUE);
448 : }
449 :
450 : static void httpd__serv_call_seek(struct Con *con, struct Httpd_req_data *req,
451 : struct File_sect *fs,
452 : unsigned int *http_ret_code,
453 : const char ** http_ret_line)
454 10457 : {
455 5236 : ASSERT(!req->head_op);
456 :
457 10457 : if (!httpd_serv_call_seek(con, fs))
458 : { /* this should be impossible for normal files AFAIK */
459 0 : vlg_warn(vlg, "lseek($<http-esc.vstr:%p%zu%zu>,off=%ju): %m\n",
460 : req->fname, (size_t)1, req->fname->len, fs->off);
461 : /* opts->use_range - turn off? */
462 0 : req->http_hdrs->hdr_range->pos = 0;
463 0 : *http_ret_code = 200;
464 0 : *http_ret_line = "OK - Range Failed";
465 : }
466 10457 : }
467 :
468 : static int http__conf_req(struct Con *con, Httpd_req_data *req, size_t del_len)
469 38777 : {
470 38777 : Conf_parse *conf = NULL;
471 :
472 38777 : if (!(conf = conf_parse_make(NULL)))
473 0 : return (FALSE);
474 :
475 38777 : if (!httpd_conf_req_parse_file(conf, con, req, del_len))
476 : {
477 224 : Vstr_base *s1 = req->policy->s->policy_name;
478 224 : Vstr_base *s2 = conf->tmp;
479 :
480 224 : if (!req->user_return_error_code)
481 0 : vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
482 : " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
483 224 : conf_parse_free(conf);
484 224 : return (TRUE);
485 : }
486 38553 : conf_parse_free(conf);
487 :
488 38553 : if (req->direct_uri)
489 : {
490 0 : Vstr_base *s1 = req->policy->s->policy_name;
491 0 : vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
492 : " Has URI.\n", CON_CEVNT_SA(con), s1);
493 0 : HTTPD_ERR_MSG_RET(req, 503, "Has URI", TRUE);
494 : }
495 :
496 38553 : return (TRUE);
497 : }
498 :
499 : static void http_prepend_doc_root(Vstr_base *fname, Httpd_req_data *req)
500 27745 : {
501 27745 : Vstr_base *dir = req->policy->document_root;
502 :
503 13880 : ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
504 13880 : ASSERT((fname->len >= 1) && vstr_cmp_cstr_eq(fname, 1, 1, "/"));
505 27745 : vstr_add_vstr(fname, 0, dir, 1, dir->len - 1, VSTR_TYPE_ADD_BUF_REF);
506 27745 : }
507 :
508 : /* characters valid in a hostname -- these are also safe unencoded in a URL */
509 : #define HTTPD__VALID_CSTR_CHRS_HOSTNAME \
510 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
511 : "abcdefghijklmnopqrstuvwxyz" \
512 : "0123456789" \
513 : ".-_"
514 : /* characters that are valid in a part of a URL _and_ in a file basename ...
515 : * without encoding */
516 : #define HTTPD__VALID_CSTR_CHRS_URL_FILENAME \
517 : "!$,:=~" HTTPD__VALID_CSTR_CHRS_HOSTNAME
518 :
519 : int httpd_valid_url_filename(Vstr_base *s1, size_t pos, size_t len)
520 138 : {
521 : static const char cstr[] = HTTPD__VALID_CSTR_CHRS_URL_FILENAME;
522 138 : return (vstr_spn_cstr_chrs_fwd(s1, pos, len, cstr) == len);
523 : }
524 :
525 : static unsigned short
526 : httpd__valid_hostname(Vstr_base *s1, size_t pos, size_t len, int allow_port)
527 22338 : {
528 : static const char cstr[] = HTTPD__VALID_CSTR_CHRS_HOSTNAME;
529 22338 : size_t tmp = 0;
530 :
531 : /* this is also checked via /./ path checking */
532 22338 : if (vstr_cmp_cstr_eq(s1, pos, len, "."))
533 288 : return (0);
534 :
535 22050 : if (vstr_srch_cstr_buf_fwd(s1, pos, len, ".."))
536 0 : return (0); /* example..com */
537 :
538 22050 : if (VPREFIX(s1, pos, len, ".")) return (0); /* .example.com */
539 :
540 : /* this is always removed in httpd_req_add_vhost */
541 11025 : assert(!VSUFFIX(s1, pos, len, ".")); /* example.com. */
542 :
543 22050 : tmp = vstr_spn_cstr_chrs_fwd(s1, pos, len, cstr);
544 22050 : if (tmp == len)
545 20466 : return (80);
546 :
547 1584 : if (!allow_port)
548 0 : return (0);
549 :
550 1584 : len -= tmp; pos += tmp;
551 1584 : if (vstr_export_chr(s1, pos) == ':') /* here both before and after port rm */
552 1584 : return (httpd_parse_host_port(s1, pos, len));
553 :
554 0 : return (0);
555 : }
556 :
557 : static int httpd__chk_vhost(const Httpd_policy_opts *popts,
558 : Vstr_base *lfn, size_t pos, size_t len)
559 22512 : {
560 22512 : const char *vhost = NULL;
561 : struct stat64 v_stat[1];
562 22512 : const Vstr_base *def_hname = popts->default_hostname;
563 22512 : int ret = -1;
564 :
565 11256 : ASSERT(pos);
566 :
567 22512 : if (popts->use_internal_host_chk)
568 : {
569 22160 : if (!httpd__valid_hostname(lfn, pos, len, TRUE))
570 288 : return (FALSE);
571 : }
572 :
573 22224 : if (!popts->use_host_chk)
574 192 : return (TRUE);
575 :
576 22032 : if (vstr_cmp_eq(lfn, pos, len, def_hname, 1, def_hname->len))
577 13544 : return (TRUE); /* don't do lots of work for nothing */
578 :
579 8488 : vstr_add_vstr(lfn, pos - 1,
580 : popts->document_root, 1, popts->document_root->len,
581 : VSTR_TYPE_ADD_BUF_PTR);
582 8488 : len += popts->document_root->len;
583 :
584 8488 : if (lfn->conf->malloc_bad || !(vhost = vstr_export_cstr_ptr(lfn, pos, len)))
585 0 : return (TRUE); /* dealt with as errmem_req() later */
586 :
587 8488 : ret = stat64(vhost, v_stat);
588 8488 : vstr_del(lfn, pos, popts->document_root->len);
589 :
590 8488 : if (ret == -1)
591 224 : return (FALSE);
592 :
593 8264 : if (!S_ISDIR(v_stat->st_mode))
594 144 : return (FALSE);
595 :
596 8120 : return (TRUE);
597 : }
598 :
599 : int http_serv_add_vhost(struct Con *con, Httpd_req_data *req,
600 : Vstr_base *s1, size_t pos, int chk)
601 43216 : {
602 43216 : size_t tmp = s1->len;
603 :
604 43216 : httpd_sc_add_hostname(con, req, s1, pos);
605 :
606 43216 : if (!req->http_hdrs->hdr_host->len)
607 16400 : return (TRUE); /* don't bother checking valid vhost for default */
608 :
609 26816 : tmp = s1->len - tmp; /* length of added data */
610 26816 : if (chk && !s1->conf->malloc_bad &&
611 : !httpd__chk_vhost(req->policy, s1, pos + 1, tmp))
612 : {
613 656 : if (req->policy->use_host_err_400) /* rfc2616 5.2 */
614 608 : HTTPD_ERR_MSG_RET(req, 400, "Hostname not local", FALSE);
615 : else
616 : { /* what everything else does ... *sigh* */
617 48 : if (s1->conf->malloc_bad)
618 0 : return (TRUE);
619 :
620 48 : req->http_hdrs->hdr_host->len = 0;
621 48 : vstr_del(s1, pos + 1, tmp);
622 48 : httpd_sc_add_default_hostname(con, req, s1, pos);
623 : }
624 : }
625 :
626 26208 : return (TRUE);
627 : }
628 :
629 : static void http_app_err_file(struct Con *con, Httpd_req_data *req,
630 : Vstr_base *fname, size_t *vhost_prefix_len)
631 10904 : {
632 10904 : Vstr_base *dir = NULL;
633 10904 : size_t orig_len = 0;
634 :
635 5452 : ASSERT(con && req && fname);
636 5452 : ASSERT(vhost_prefix_len && !*vhost_prefix_len);
637 :
638 10904 : orig_len = fname->len;
639 10904 : dir = req->policy->req_err_dir;
640 5452 : ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
641 10904 : HTTPD_APP_REF_ALLVSTR(fname, dir);
642 :
643 10904 : if (req->policy->use_vhosts_name)
644 : {
645 10640 : http_serv_add_vhost(con, req, fname, fname->len, FALSE);
646 10640 : vstr_add_cstr_ptr(fname, fname->len, "/");
647 : }
648 :
649 : /* FIXME: This kind of looks like a hack, we tell the rest of the code that
650 : * the err req dir and any vhost info. is all part of the
651 : * "non-path prefix" basically so = does the right thing with limits.
652 : * Ie. path-eq 404 ... vhost_prefix_len should probably be called
653 : * something else. */
654 10904 : *vhost_prefix_len = (fname->len - orig_len);
655 :
656 10904 : vstr_add_fmt(fname, fname->len, "%u.html", req->error_code);
657 10904 : }
658 :
659 : uintmax_t http_serv_file_len(struct Con *con, Httpd_req_data *req)
660 44338 : {
661 : #ifndef NDEBUG
662 22184 : uintmax_t len = 0;
663 22184 : unsigned int num = con->fs_num;
664 :
665 66936 : while (num > 0)
666 22568 : len += con->fs[--num].len;
667 :
668 22184 : ASSERT(len == req->fs_len);
669 : #endif
670 :
671 44338 : return (req->fs_len);
672 : }
673 :
674 : static void http__err_vlg_msg(struct Con *con, Httpd_req_data *req)
675 18134 : {
676 18134 : uintmax_t tmp = 0;
677 :
678 18134 : if (!req->head_op)
679 11694 : tmp = req->error_len;
680 :
681 18134 : vlg_info(vlg, "ERREQ from[$<sa:%p>] err[%03u %s%s%s] sz[${BKMG.ju:%ju}:%ju]",
682 : CON_CEVNT_SA(con), req->error_code, req->error_line,
683 : *req->error_xmsg ? " | " : "", req->error_xmsg, tmp, tmp);
684 35918 : if ((req->sects->num >= 2) && (req->ver_0_9 || (req->sects->num >= 3)))
685 17784 : http_vlg_def(con, req, TRUE);
686 : else
687 350 : vlg_info(vlg, "%s", "\n");
688 18134 : }
689 :
690 : void httpd_serv_call_file_init(struct Con *con, Httpd_req_data *req,
691 : unsigned int *http_ret_code,
692 : const char ** http_ret_line)
693 21257 : {
694 10636 : ASSERT(req);
695 :
696 21257 : con->use_mmap = FALSE;
697 21257 : if (!req->head_op)
698 : {
699 10457 : httpd_serv_call_mmap(con, req, con->fs);
700 10457 : httpd__serv_call_seek(con, req, con->fs, http_ret_code, http_ret_line);
701 : }
702 21257 : }
703 :
704 : void httpd_serv_file_sects_none(struct Con *con, Httpd_req_data *req,
705 : off64_t len)
706 19985 : {
707 19985 : con->use_mpbr = FALSE;
708 19985 : con->fs_num = 1;
709 19985 : con->fs->off = 0;
710 19985 : con->fs->len = len;
711 19985 : req->fs_len = len;
712 19985 : }
713 :
714 : /* if the attacker gives a user a URL like:
715 : http://example.com/">x</a><script>...</script>
716 : * and we're redirecting everything for example.com to www.example.com
717 : * that'll get output, with html redirects, as:
718 : <a href="http://example.com/">x</a><script>...</script>">here</a>
719 : * ...browsers probably can't be relied upon to not do the stupid thing so
720 : * we escape ' " < > ... ' is just because. None of them mean anything special
721 : * in URLs so we should be fine */
722 : static unsigned int http__safe_html_url(Vstr_base *loc)
723 5632 : {
724 : static const char unsafe[] = "'\"<>"; /* include " " for configs? */
725 5632 : size_t pos = 1;
726 5632 : size_t len = loc->len;
727 5632 : size_t chrs = 0;
728 5632 : unsigned int ret = 0;
729 :
730 2816 : ASSERT(loc->len);
731 :
732 9280 : while ((chrs = vstr_cspn_cstr_chrs_fwd(loc, pos, len, unsafe)) != len)
733 : {
734 832 : const char *safe = "...";
735 :
736 832 : ++ret;
737 :
738 832 : pos += chrs;
739 832 : switch (vstr_export_chr(loc, pos))
740 : {
741 0 : case ' ': safe = "%20"; break; /* NOTE: not included above */
742 128 : case '"': safe = "%22"; break;
743 0 : case '\'': safe = "%27"; break;
744 288 : case '<': safe = "%3c"; break;
745 416 : case '>': safe = "%3e";
746 208 : ASSERT_NO_SWITCH_DEF();
747 : }
748 :
749 416 : ASSERT(strlen(safe) == 3);
750 832 : if (!vstr_sub_buf(loc, pos, 1, safe, 3))
751 0 : return (0);
752 :
753 832 : if (pos == len)
754 0 : break;
755 :
756 832 : pos += 3;
757 832 : len = vstr_sc_posdiff(pos, loc->len);
758 : }
759 :
760 2816 : ASSERT(vstr_cspn_cstr_chrs_fwd(loc, 1, loc->len, unsafe) == loc->len);
761 :
762 5632 : return (ret);
763 : }
764 :
765 : int http_fin_err_req(struct Con *con, Httpd_req_data *req)
766 18550 : {
767 18550 : Vstr_base *out = con->evnt->io_w;
768 18550 : int cust_err_msg = FALSE;
769 18550 : int deleted_req_data = FALSE;
770 :
771 9275 : ASSERT(req->error_code);
772 :
773 9275 : ASSERT(!con->use_mpbr); /* done as part of close() */
774 9275 : ASSERT(con->fs && !con->fs_num);
775 :
776 18550 : req->content_encoding_gzip = FALSE;
777 18550 : req->content_encoding_bzip2 = FALSE;
778 18550 : req->content_encoding_xgzip = FALSE;
779 :
780 : /* These are done before keep-alive parsing is done */
781 9275 : ASSERT((req->error_code != 411) || (con->keep_alive == HTTP_NON_KEEP_ALIVE));
782 9275 : ASSERT((req->error_code != 413) || (con->keep_alive == HTTP_NON_KEEP_ALIVE));
783 :
784 18550 : if ((req->error_code == 400) || (req->error_code == 405) ||
785 : (req->error_code == 411) || (req->error_code == 413) ||
786 : (req->error_code == 500) || (req->error_code == 501))
787 5614 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
788 :
789 18550 : if (req->malloc_bad)
790 : { /* delete all input to give more room */
791 0 : ASSERT(req->error_code == 500);
792 0 : http__err_vlg_msg(con, req);
793 0 : vstr_del(con->evnt->io_r, 1, con->evnt->io_r->len);
794 0 : deleted_req_data = TRUE;
795 : }
796 18550 : else if (!HTTPD_ERR_REDIR(req->error_code) && /* don't do as href is passed */
797 : req->policy->req_err_dir->len)
798 : { /* custom err message */
799 11480 : Vstr_base *fname = NULL;
800 11480 : size_t vhost_prefix_len = 0;
801 11480 : const char *fname_cstr = NULL;
802 : struct stat64 f_stat[1];
803 11480 : int open_flags = O_NONBLOCK;
804 :
805 11480 : if (HTTPD_ERR_MATCH_RESP(req->error_code))
806 : {
807 8504 : req->req_user_return_error_code = req->user_return_error_code;
808 8504 : req->user_return_error_code = FALSE;
809 8504 : req->req_error_code = req->error_code;
810 8504 : req->error_code = 0;
811 8504 : req->req_error_xmsg = req->error_xmsg;
812 8504 : req->error_xmsg = "";
813 :
814 8504 : if (!httpd_policy_response(con, req,
815 : httpd_opts->conf, httpd_opts->match_response))
816 : {
817 672 : Vstr_base *s1 = httpd_opts->conf->tmp;
818 672 : if (!req->user_return_error_code)
819 0 : vlg_info(vlg, "CONF-MATCH-RESP-ERR from[$<sa:%p>]:"
820 : " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1);
821 : /* fall through */
822 : }
823 : else
824 : {
825 3916 : ASSERT(!req->user_return_error_code);
826 3916 : ASSERT(!req->error_code);
827 3916 : ASSERT(!*req->error_xmsg);
828 7832 : req->user_return_error_code = req->req_user_return_error_code;
829 7832 : req->error_code = req->req_error_code;
830 7832 : req->error_xmsg = req->req_error_xmsg;
831 :
832 7832 : if (con->evnt->flag_q_closed)
833 : {
834 0 : Vstr_base *s1 = req->policy->s->policy_name;
835 :
836 0 : vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
837 : CON_CEVNT_SA(con), s1);
838 0 : return (http_con_cleanup(con, req));
839 : }
840 :
841 7832 : if (req->direct_uri)
842 : {
843 0 : Vstr_base *s1 = req->policy->s->policy_name;
844 0 : vlg_info(vlg, "CONF-MATCH-RESP-ERR from[$<sa:%p>]: "
845 : "policy $<vstr.all:%p> Has URI.\n", CON_CEVNT_SA(con), s1);
846 0 : HTTPD_ERR_MSG_RET(req, 503, "Has URI", TRUE);
847 : }
848 : }
849 :
850 8504 : if (!HTTPD_ERR_MATCH_RESP(req->error_code))
851 416 : return (http_fin_err_req(con, req)); /* don't loop, just fall through */
852 : }
853 :
854 11064 : if (!(fname = vstr_make_base(req->fname->conf)))
855 0 : goto fail_custom_err;
856 :
857 : /* NOTE that vary_* is the union of both the req and the resp. processing */
858 :
859 11064 : if (!req->user_return_error_code)
860 : {
861 9688 : req->cache_control_vs1 = NULL;
862 9688 : req->expires_vs1 = NULL;
863 9688 : req->p3p_vs1 = NULL;
864 : }
865 :
866 11144 : if (req->user_return_error_code && req->direct_filename)
867 : {
868 160 : HTTPD_APP_REF_ALLVSTR(fname, req->fname);
869 160 : if (!req->skip_document_root)
870 160 : http_prepend_doc_root(fname, req);
871 : }
872 : else
873 : {
874 10904 : int conf_ret = FALSE;
875 10904 : unsigned int code = req->error_code;
876 10904 : unsigned int ncode = 0;
877 10904 : const char *xmsg = req->error_xmsg;
878 :
879 10904 : http_app_err_file(con, req, fname, &vhost_prefix_len);
880 :
881 10904 : req->content_type_vs1 = NULL;
882 10904 : req->content_type_pos = 0;
883 10904 : req->content_type_len = 0;
884 10904 : req->error_code = 0;
885 10904 : req->skip_document_root = FALSE;
886 10904 : req->direct_uri = FALSE;
887 10904 : req->neg_content_type_done = FALSE;
888 10904 : req->neg_content_lang_done = FALSE;
889 :
890 10904 : SWAP_TYPE(fname, req->fname, Vstr_base *);
891 10904 : SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
892 :
893 10904 : req->conf_flags = HTTPD_CONF_REQ_FLAGS_PARSE_FILE_UERR;
894 10904 : conf_ret = http__conf_req(con, req, CLEN(".html"));
895 :
896 10904 : SWAP_TYPE(fname, req->fname, Vstr_base *);
897 10904 : SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
898 :
899 10904 : ncode = req->error_code;
900 10904 : switch (code)
901 : { /* restore default error info. in case of failure */
902 2728 : case 400: HTTPD_ERR(req, 400); break;
903 640 : case 401: HTTPD_ERR(req, 401); break;
904 2176 : case 403: HTTPD_ERR(req, 403); break;
905 1680 : case 404: HTTPD_ERR(req, 404); break;
906 96 : case 405: HTTPD_ERR(req, 405); break;
907 1120 : case 406: HTTPD_ERR(req, 406); break;
908 224 : case 410: HTTPD_ERR(req, 410); break;
909 288 : case 411: HTTPD_ERR(req, 411); break;
910 832 : case 412: HTTPD_ERR(req, 412); break;
911 96 : case 413: HTTPD_ERR(req, 413); break;
912 0 : case 414: HTTPD_ERR(req, 414); break;
913 0 : case 415: HTTPD_ERR(req, 415); break;
914 192 : case 416: HTTPD_ERR(req, 416); break;
915 96 : case 417: HTTPD_ERR(req, 417); break;
916 32 : case 500: HTTPD_ERR(req, 500); break;
917 512 : case 501: HTTPD_ERR(req, 501); break;
918 96 : case 503: HTTPD_ERR(req, 503); break;
919 96 : case 505: HTTPD_ERR(req, 505);
920 48 : ASSERT_NO_SWITCH_DEF();
921 : }
922 10904 : req->error_xmsg = xmsg; /* restore xmsg too */
923 :
924 10904 : if (!conf_ret)
925 0 : goto fail_custom_err;
926 10904 : if (ncode)
927 0 : goto fail_custom_err; /* don't allow remapping errors -- loops */
928 : }
929 11064 : fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
930 :
931 11064 : if (fname->conf->malloc_bad)
932 0 : goto fail_custom_err;
933 :
934 11064 : if (req->policy->use_noatime)
935 0 : open_flags |= O_NOATIME;
936 :
937 5532 : ASSERT(con->fs && (con->fs->fd == -1));
938 11064 : if ((con->fs->fd = io__open(fname_cstr, open_flags)) == -1)
939 9048 : goto fail_custom_err;
940 :
941 3024 : if (fstat64(con->fs->fd, f_stat) == -1)
942 0 : goto fail_custom_err;
943 2016 : if (req->policy->use_public_only && !(f_stat->st_mode & S_IROTH))
944 0 : goto fail_custom_err;
945 2016 : if (!S_ISREG(f_stat->st_mode))
946 0 : goto fail_custom_err;
947 :
948 2016 : con->fs_off = 0;
949 2016 : con->fs_num = 0;
950 2016 : con->fs->len = f_stat->st_size;
951 2016 : req->fs_len = 0;
952 :
953 2016 : if (!req->ver_0_9)
954 2016 : httpd_parse_sc_try_fd_encoding(con, req, f_stat, &f_stat->st_size, fname);
955 :
956 : /* FIXME: gzipped error documents weren't tested for */
957 2016 : httpd_serv_file_sects_none(con, req, f_stat->st_size);
958 :
959 2016 : con->use_mmap = FALSE;
960 :
961 2016 : if (!req->head_op)
962 1760 : httpd_serv_call_mmap(con, req, con->fs);
963 :
964 2016 : req->error_len = http_serv_file_len(con, req);
965 2016 : cust_err_msg = TRUE;
966 :
967 11064 : fail_custom_err:
968 11064 : if (!cust_err_msg)
969 9048 : httpd_fin_fd_close(con);
970 11064 : fname->conf->malloc_bad = FALSE;
971 11064 : vstr_free_base(fname);
972 : }
973 :
974 18134 : if (!cust_err_msg)
975 : {
976 16118 : req->content_type_vs1 = NULL;
977 16118 : req->content_type_pos = 0;
978 16118 : req->content_type_len = 0;
979 : }
980 :
981 : /* HEAD op might seem normal, but again HTTP asks that GET and HEAD
982 : produce identical headers */
983 18134 : if (!req->policy->use_text_redirect)
984 17110 : switch (req->error_code)
985 : { /* make sure browsers don't allow XSS */
986 : case 301:
987 : {
988 1920 : unsigned int repl = http__safe_html_url(req->fname);
989 :
990 960 : ASSERT((req->error_len + (repl * 2)) == CONF_MSG_LEN_301(req->fname));
991 :
992 1920 : req->error_len = CONF_MSG_LEN_301(req->fname);
993 : }
994 1920 : break;
995 :
996 : case 302:
997 : {
998 256 : unsigned int repl = http__safe_html_url(req->fname);
999 :
1000 128 : ASSERT((req->error_len + (repl * 2)) == CONF_MSG_LEN_302(req->fname));
1001 :
1002 256 : req->error_len = CONF_MSG_LEN_302(req->fname);
1003 : }
1004 256 : break;
1005 :
1006 : case 303:
1007 : {
1008 256 : unsigned int repl = http__safe_html_url(req->fname);
1009 :
1010 128 : ASSERT((req->error_len + (repl * 2)) == CONF_MSG_LEN_303(req->fname));
1011 :
1012 256 : req->error_len = CONF_MSG_LEN_303(req->fname);
1013 : }
1014 256 : break;
1015 :
1016 : case 307:
1017 : {
1018 384 : unsigned int repl = http__safe_html_url(req->fname);
1019 :
1020 192 : ASSERT((req->error_len + (repl * 2)) == CONF_MSG_LEN_307(req->fname));
1021 :
1022 384 : req->error_len = CONF_MSG_LEN_307(req->fname);
1023 : }
1024 : break;
1025 : }
1026 :
1027 18134 : if (!req->ver_0_9)
1028 : { /* use_range is dealt with inside */
1029 17654 : const char *content_type = "text/html";
1030 :
1031 17654 : switch (req->error_code)
1032 : {
1033 : case 301: case 302: case 303: case 307:
1034 3648 : if (req->policy->use_text_redirect)
1035 832 : content_type = "text/plain";
1036 : else /* make sure browsers don't allow XSS */
1037 2816 : http__safe_html_url(req->fname);
1038 : }
1039 :
1040 17654 : http_app_def_hdrs(con, req, req->error_code, req->error_line,
1041 : httpd_opts->beg_time, content_type, TRUE, req->error_len);
1042 :
1043 17654 : if (req->error_code == 416)
1044 288 : http_app_hdr_fmt(out, "Content-Range", "%s */%ju", "bytes",
1045 : (uintmax_t)req->f_stat_st_size);
1046 :
1047 17654 : if (req->error_code == 401)
1048 640 : http_app_hdr_fmt(out, "WWW-Authenticate",
1049 : "Basic realm=\"$<vstr.all:%p>\"",
1050 : req->policy->auth_realm);
1051 :
1052 17654 : if ((req->error_code == 405) || (req->error_code == 501))
1053 576 : HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
1054 :
1055 17654 : switch (req->error_code)
1056 : {
1057 : case 301: case 302: case 303: case 307:
1058 : { /* make sure we haven't screwed up and allowed response splitting */
1059 3648 : Vstr_base *loc = req->fname;
1060 :
1061 3648 : http_app_hdr_vstr(out, "Location",
1062 : loc, 1, loc->len, VSTR_TYPE_ADD_ALL_BUF);
1063 : }
1064 : }
1065 :
1066 17654 : if (req->user_return_error_code || cust_err_msg)
1067 6144 : http_app_hdrs_url(con, req);
1068 17654 : if (cust_err_msg)
1069 2016 : http_app_hdrs_file(con, req);
1070 :
1071 17654 : http_app_end_hdrs(out);
1072 : }
1073 :
1074 18134 : if (!deleted_req_data)
1075 18134 : http__err_vlg_msg(con, req);
1076 :
1077 18134 : if (cust_err_msg)
1078 : {
1079 2016 : if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
1080 : req->f_mmap, 1, req->f_mmap->len))
1081 0 : return (http_con_close_cleanup(con, req));
1082 :
1083 2016 : vlg_dbg3(vlg, "ERROR CUSTOM-ERR REPLY:\n$<vstr.all:%p>\n", out);
1084 :
1085 2016 : if (out->conf->malloc_bad)
1086 0 : return (http_con_close_cleanup(con, req));
1087 :
1088 2016 : return (http_fin_fd_req(con, req));
1089 : }
1090 :
1091 16118 : if (!req->head_op)
1092 : { /* default internal error message */
1093 9934 : Vstr_base *loc = req->fname;
1094 :
1095 9934 : switch (req->error_code)
1096 : {
1097 : case 301:
1098 1392 : if (!req->policy->use_text_redirect)
1099 : {
1100 472 : ASSERT(req->error_len == CONF_MSG_LEN_301(loc));
1101 944 : vstr_add_fmt(out, out->len, CONF_MSG_FMT_301,
1102 : CONF_MSG__FMT_301_BEG,
1103 : loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1104 : CONF_MSG__FMT_30x_END);
1105 944 : break;
1106 : }
1107 : case 302:
1108 448 : if (!req->policy->use_text_redirect)
1109 : {
1110 0 : ASSERT(req->error_len == CONF_MSG_LEN_302(loc));
1111 0 : vstr_add_fmt(out, out->len, CONF_MSG_FMT_302,
1112 : CONF_MSG__FMT_302_BEG,
1113 : loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1114 : CONF_MSG__FMT_30x_END);
1115 0 : break;
1116 : }
1117 : case 303:
1118 448 : if (!req->policy->use_text_redirect)
1119 : {
1120 0 : ASSERT(req->error_len == CONF_MSG_LEN_303(loc));
1121 0 : vstr_add_fmt(out, out->len, CONF_MSG_FMT_303,
1122 : CONF_MSG__FMT_303_BEG,
1123 : loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1124 : CONF_MSG__FMT_30x_END);
1125 0 : break;
1126 : }
1127 : case 307:
1128 448 : if (!req->policy->use_text_redirect)
1129 : {
1130 0 : ASSERT(req->error_len == CONF_MSG_LEN_307(loc));
1131 0 : vstr_add_fmt(out, out->len, CONF_MSG_FMT_307,
1132 : CONF_MSG__FMT_307_BEG,
1133 : loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1134 : CONF_MSG__FMT_30x_END);
1135 0 : break;
1136 : }
1137 :
1138 : /* if using text/plain just output the URL */
1139 224 : ASSERT((req->error_len - 1) == loc->len);
1140 448 : vstr_add_vstr(out, out->len,
1141 : loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF);
1142 448 : vstr_add_cstr_ptr(out, out->len, "\n");
1143 448 : break;
1144 :
1145 : default:
1146 4271 : assert(req->error_len < SIZE_MAX);
1147 8542 : vstr_add_ptr(out, out->len, req->error_msg, req->error_len);
1148 : }
1149 : }
1150 :
1151 16118 : vlg_dbg3(vlg, "ERROR REPLY:\n$<vstr.all:%p>\n", out);
1152 :
1153 16118 : if (out->conf->malloc_bad)
1154 0 : return (http_con_cleanup(con, req));
1155 :
1156 16118 : return (http_fin_req(con, req));
1157 : }
1158 :
1159 : int http_fin_errmem_req(struct Con *con, struct Httpd_req_data *req)
1160 0 : { /* try sending a 500 as the last msg */
1161 0 : Vstr_base *out = con->evnt->io_w;
1162 :
1163 : /* remove anything we can to free space */
1164 0 : vstr_sc_reduce(out, 1, out->len, out->len - req->orig_io_w_len);
1165 :
1166 0 : con->evnt->io_r->conf->malloc_bad = FALSE;
1167 0 : con->evnt->io_w->conf->malloc_bad = FALSE;
1168 0 : req->malloc_bad = TRUE;
1169 :
1170 0 : HTTPD_ERR_MSG_RET(req, 500, "Memory failure", http_fin_err_req(con, req));
1171 : }
1172 :
1173 : static int http_fin_err_close_req(struct Con *con, Httpd_req_data *req)
1174 1792 : {
1175 1792 : httpd_fin_fd_close(con);
1176 1792 : return (http_fin_err_req(con, req));
1177 : }
1178 :
1179 : int httpd_sc_add_default_hostname(struct Con *con,
1180 : Httpd_req_data *req,
1181 : Vstr_base *lfn, size_t pos)
1182 18080 : {
1183 18080 : const Httpd_policy_opts *opts = req->policy;
1184 18080 : const Vstr_base *d_h = opts->default_hostname;
1185 18080 : int ret = FALSE;
1186 :
1187 18080 : ret = vstr_add_vstr(lfn, pos, d_h, 1, d_h->len, VSTR_TYPE_ADD_DEF);
1188 :
1189 18080 : if (ret && req->policy->add_def_port)
1190 : { /* FIXME: ipv6 */
1191 0 : struct sockaddr_in *sinv4 = EVNT_ACPT_SA_IN4(con->evnt);
1192 :
1193 0 : ASSERT(sinv4->sin_family == AF_INET);
1194 :
1195 0 : if (ntohs(sinv4->sin_port) != 80)
1196 0 : ret = vstr_add_fmt(lfn, pos + d_h->len, ":%hu", ntohs(sinv4->sin_port));
1197 : }
1198 :
1199 18080 : return (ret);
1200 : }
1201 :
1202 : int httpd_sc_add_req_hostname(struct Con *con, Httpd_req_data *req,
1203 : Vstr_base *s1, size_t pos)
1204 28832 : {
1205 28832 : Vstr_base *http_data = con->evnt->io_r;
1206 28832 : Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
1207 :
1208 14416 : ASSERT(h_h->len);
1209 :
1210 28832 : if (vstr_add_vstr(s1, pos, http_data, h_h->pos, h_h->len, VSTR_TYPE_ADD_DEF))
1211 : {
1212 28832 : if (req->http_host_port != 80) /* if port 80, ignore it */
1213 1776 : vstr_add_sysfmt(s1, pos + h_h->len, ":%hu", req->http_host_port);
1214 28832 : vstr_conv_lowercase(s1, pos + 1, h_h->len);
1215 : }
1216 :
1217 28832 : return (!!s1->conf->malloc_bad);
1218 : }
1219 :
1220 : int httpd_sc_add_hostname(struct Con *con, Httpd_req_data *req,
1221 : Vstr_base *s1, size_t pos)
1222 46864 : {
1223 46864 : if (req->http_hdrs->hdr_host->len)
1224 28832 : return (httpd_sc_add_req_hostname(con, req, s1, pos));
1225 :
1226 18032 : return (httpd_sc_add_default_hostname(con, req, s1, pos));
1227 : }
1228 :
1229 :
1230 : static void httpd_sc_add_default_filename(Httpd_req_data *req, Vstr_base *fname)
1231 27873 : {
1232 27873 : if (vstr_export_chr(fname, fname->len) == '/')
1233 17648 : HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
1234 27873 : }
1235 :
1236 : /* turn //foo//bar// into /foo/bar/ */
1237 : int httpd_canon_path(Vstr_base *s1)
1238 606 : {
1239 606 : size_t tmp = 0;
1240 :
1241 1500 : while ((tmp = vstr_srch_cstr_buf_fwd(s1, 1, s1->len, "//")))
1242 : {
1243 288 : if (!vstr_del(s1, tmp, 1))
1244 0 : return (FALSE);
1245 : }
1246 :
1247 606 : return (TRUE);
1248 : }
1249 :
1250 : int httpd_canon_dir_path(Vstr_base *s1)
1251 414 : {
1252 414 : if (!httpd_canon_path(s1))
1253 0 : return (FALSE);
1254 :
1255 414 : if (s1->len && (vstr_export_chr(s1, s1->len) != '/'))
1256 256 : return (vstr_add_cstr_ptr(s1, s1->len, "/"));
1257 :
1258 158 : return (TRUE);
1259 : }
1260 :
1261 : int httpd_canon_abs_dir_path(Vstr_base *s1)
1262 0 : {
1263 0 : if (!httpd_canon_dir_path(s1))
1264 0 : return (FALSE);
1265 :
1266 0 : if (s1->len && (vstr_export_chr(s1, 1) != '/'))
1267 0 : return (vstr_add_cstr_ptr(s1, 0, "/"));
1268 :
1269 0 : return (TRUE);
1270 : }
1271 :
1272 :
1273 : static int http__policy_req(struct Con *con, Httpd_req_data *req)
1274 31841 : {
1275 31841 : if (!httpd_policy_request(con, req,
1276 : httpd_opts->conf, httpd_opts->match_request))
1277 : {
1278 3296 : Vstr_base *s1 = httpd_opts->conf->tmp;
1279 3296 : if (!req->user_return_error_code)
1280 64 : vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]:"
1281 : " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1);
1282 3296 : return (TRUE);
1283 : }
1284 :
1285 28545 : if (con->evnt->flag_q_closed)
1286 : {
1287 32 : Vstr_base *s1 = req->policy->s->policy_name;
1288 :
1289 32 : vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
1290 : CON_CEVNT_SA(con), s1);
1291 32 : return (http_con_cleanup(con, req));
1292 : }
1293 :
1294 28513 : if (req->direct_uri)
1295 : {
1296 0 : Vstr_base *s1 = req->policy->s->policy_name;
1297 0 : vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
1298 : " Has URI.\n", CON_CEVNT_SA(con), s1);
1299 0 : HTTPD_ERR_MSG_RET(req, 503, "Has URI", TRUE);
1300 : }
1301 :
1302 28513 : if (req->policy->auth_token->len) /* they need rfc2617 auth */
1303 : {
1304 1184 : Vstr_base *data = con->evnt->io_r;
1305 1184 : size_t pos = req->http_hdrs->hdr_authorization->pos;
1306 1184 : size_t len = req->http_hdrs->hdr_authorization->len;
1307 1184 : Vstr_base *auth_token = req->policy->auth_token;
1308 1184 : int auth_ok = TRUE;
1309 :
1310 1600 : if (!VIPREFIX(data, pos, len, "Basic"))
1311 416 : auth_ok = FALSE;
1312 : else
1313 : {
1314 768 : len -= CLEN("Basic"); pos += CLEN("Basic");
1315 768 : HTTP_SKIP_LWS(data, pos, len);
1316 768 : if (!vstr_cmp_eq(data, pos, len, auth_token, 1, auth_token->len))
1317 224 : auth_ok = FALSE;
1318 : }
1319 :
1320 1184 : if (!auth_ok)
1321 : {
1322 640 : req->user_return_error_code = FALSE;
1323 640 : HTTPD_ERR(req, 401);
1324 : }
1325 : }
1326 :
1327 28513 : return (TRUE);
1328 : }
1329 :
1330 : int http_req_op_get(struct Con *con, Httpd_req_data *req)
1331 32033 : { /* GET or HEAD ops */
1332 32033 : Vstr_base *data = con->evnt->io_r;
1333 32033 : Vstr_base *out = con->evnt->io_w;
1334 32033 : Vstr_base *fname = req->fname;
1335 32033 : const char *fname_cstr = NULL;
1336 32033 : unsigned int http_ret_code = 200;
1337 32033 : const char * http_ret_line = "OK";
1338 32033 : int open_flags = O_NONBLOCK;
1339 32033 : uintmax_t resp_len = 0;
1340 :
1341 32033 : if (fname->conf->malloc_bad)
1342 0 : goto malloc_err;
1343 :
1344 16024 : assert(VPREFIX(fname, 1, fname->len, "/"));
1345 :
1346 : /* final act of vengance, before policy */
1347 32033 : if (vstr_srch_cstr_buf_fwd(data, req->path_pos, req->path_len, "//"))
1348 : { /* in theory we can skip this if there is no policy */
1349 192 : vstr_sub_vstr(fname, 1, fname->len, data, req->path_pos, req->path_len, 0);
1350 192 : if (!httpd_canon_path(fname))
1351 0 : goto malloc_err;
1352 192 : httpd_req_absolute_uri(con, req, fname, 1, fname->len);
1353 192 : HTTPD_REDIR_MSG(req, 301, "Path contains //");
1354 :
1355 192 : return (http_fin_err_req(con, req));
1356 : }
1357 :
1358 31841 : if (!http__policy_req(con, req))
1359 32 : return (FALSE);
1360 31809 : if (req->error_code)
1361 3936 : return (http_fin_err_req(con, req));
1362 :
1363 27873 : httpd_sc_add_default_filename(req, fname);
1364 :
1365 27873 : req->conf_flags = HTTPD_CONF_REQ_FLAGS_PARSE_FILE_DEFAULT;
1366 27873 : if (!http__conf_req(con, req, 0))
1367 0 : return (FALSE);
1368 27873 : if (req->error_code)
1369 224 : return (http_fin_err_req(con, req));
1370 :
1371 : /* Add the document root now, this must be at least . so
1372 : * "///foo" becomes ".///foo" ... this is done now
1373 : * so nothing has to deal with document_root values */
1374 27649 : if (!req->skip_document_root)
1375 27585 : http_prepend_doc_root(fname, req);
1376 :
1377 27649 : fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
1378 :
1379 27649 : if (fname->conf->malloc_bad)
1380 0 : goto malloc_err;
1381 :
1382 27649 : if (!http_req_content_type(req))
1383 384 : return (http_fin_err_req(con, req));
1384 :
1385 : /* don't change vary_a/vary_al just because of 406 */
1386 27265 : if (req->parse_accept)
1387 : {
1388 22016 : if (!http_parse_accept(req, HTTP__XTRA_HDR_PARAMS(req, content_type)))
1389 1168 : HTTPD_ERR_MSG_RET(req, 406, "Type", http_fin_err_req(con, req));
1390 : }
1391 26097 : if (!req->content_language_vs1 || !req->content_language_len)
1392 24785 : req->parse_accept_language = FALSE;
1393 26097 : if (req->parse_accept_language)
1394 : {
1395 0 : if (!http_parse_accept_language(req,
1396 : HTTP__XTRA_HDR_PARAMS(req,
1397 : content_language)))
1398 0 : HTTPD_ERR_MSG_RET(req, 406, "Language", http_fin_err_req(con, req));
1399 : }
1400 :
1401 26097 : if (req->policy->use_noatime)
1402 0 : open_flags |= O_NOATIME;
1403 :
1404 13056 : ASSERT(con->fs && !con->fs_num && !con->fs_off && (con->fs->fd == -1));
1405 26097 : if ((con->fs->fd = io__open(fname_cstr, open_flags)) == -1)
1406 : {
1407 : if (0) { }
1408 2472 : else if (req->direct_filename && (errno == EISDIR)) /* don't allow */
1409 0 : HTTPD_ERR_MSG(req, 404, "Direct filename is DIR");
1410 2472 : else if (errno == EISDIR)
1411 0 : return (http_req_chk_dir(con, req, "open(EISDIR)"));
1412 2472 : else if (req->conf_friendly_file && (errno == ENOENT))
1413 32 : return (http_req_chk_dir(con, req, "open(ENOENT)"));
1414 2440 : else if (req->conf_friendly_file &&
1415 : (errno == ENOTDIR)) /* part of path was not a dir */
1416 0 : return (http_req_chk_dir(con, req, "open(ENOTDIR)"));
1417 2440 : else if (req->conf_friendly_file && (errno == ENAMETOOLONG)) /* 414 ? */
1418 0 : return (http_req_chk_dir(con, req, "open(ENAMETOOLONG)"));
1419 2440 : else if ((errno == ENOENT) && req->conf_friendly_dirs)
1420 0 : return (http_req_chk_file(con, req, "open(ENOENT)"));
1421 2440 : else if (errno == ENOTDIR) /* part of path was not a dir */
1422 64 : return (http_req_chk_file(con, req, "open(ENOTDIR)"));
1423 2376 : else if (errno == ENAMETOOLONG) /* 414 ? */
1424 0 : return (http_req_chk_file(con, req, "open(ENAMETOOLONG)"));
1425 2376 : else if (errno == EACCES)
1426 144 : HTTPD_ERR_MSG(req, 403, "open(EACCES)");
1427 2232 : else if (errno == ENOENT)
1428 2232 : HTTPD_ERR_MSG(req, 404, "open(ENOENT)");
1429 0 : else if (errno == ENODEV) /* device file, with no driver */
1430 0 : HTTPD_ERR_MSG(req, 404, "open(ENODEV)");
1431 0 : else if (errno == ENXIO) /* device file, with no driver */
1432 0 : HTTPD_ERR_MSG(req, 404, "open(ENXIO)");
1433 0 : else if (errno == ELOOP) /* symlinks */
1434 0 : HTTPD_ERR_MSG(req, 404, "open(ELOOP)");
1435 0 : else if (errno == EMFILE) /* can't create fd ... just close */
1436 0 : return (http_con_cleanup(con, req));
1437 : else
1438 : {
1439 0 : vlg_warn(vlg, "open(%s): %m\n", fname_cstr);
1440 0 : HTTPD_ERR_MSG(req, 500, "open()");
1441 : }
1442 :
1443 2376 : return (http_fin_err_req(con, req));
1444 : }
1445 23625 : if (fstat64(con->fs->fd, req->f_stat) == -1)
1446 0 : HTTPD_ERR_MSG_RET(req, 500, "fstat()", http_fin_err_close_req(con, req));
1447 23625 : req->f_stat_st_size = req->f_stat->st_size;
1448 23625 : if (req->policy->use_public_only && !(req->f_stat->st_mode & S_IROTH))
1449 48 : HTTPD_ERR_MSG_RET(req, 403, "Filename is not PUBLIC",
1450 : http_fin_err_close_req(con, req));
1451 :
1452 23577 : if (S_ISDIR(req->f_stat->st_mode))
1453 : {
1454 576 : if (req->direct_filename) /* don't allow */
1455 0 : HTTPD_ERR_MSG_RET(req, 404, "Direct filename is DIR",
1456 : http_fin_err_close_req(con, req));
1457 576 : httpd_fin_fd_close(con);
1458 576 : return (http_req_chk_dir(con, req, "ISDIR"));
1459 : }
1460 23001 : if (!S_ISREG(req->f_stat->st_mode))
1461 144 : HTTPD_ERR_MSG_RET(req, 403, "File not ISREG",
1462 : http_fin_err_close_req(con, req));
1463 :
1464 22857 : con->fs->len = req->f_stat_st_size;
1465 :
1466 22857 : if (req->ver_0_9)
1467 : {
1468 192 : httpd_serv_file_sects_none(con, req, req->f_stat_st_size);
1469 192 : httpd_serv_call_file_init(con, req, &http_ret_code, &http_ret_line);
1470 192 : http_ret_line = "OK - HTTP/0.9";
1471 : }
1472 22665 : else if (!http_req_1_x(con, req, &http_ret_code, &http_ret_line))
1473 1600 : return (http_fin_err_close_req(con, req));
1474 :
1475 21257 : if (out->conf->malloc_bad)
1476 0 : goto malloc_close_err;
1477 :
1478 21257 : vlg_dbg3(vlg, "REPLY:\n$<vstr.all:%p>\n", out);
1479 :
1480 21257 : if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
1481 : req->f_mmap, 1, req->f_mmap->len))
1482 0 : goto malloc_close_err;
1483 :
1484 : /* req->head_op is set for 304 returns */
1485 21257 : resp_len = http_serv_file_len(con, req);
1486 21257 : vlg_info(vlg, "REQ $<vstr.sect:%p%p%u> from[$<sa:%p>] ret[%03u %s]"
1487 : " sz[${BKMG.ju:%ju}:%ju]", data, req->sects, 1U, CON_CEVNT_SA(con),
1488 : http_ret_code, http_ret_line, resp_len, resp_len);
1489 21257 : http_vlg_def(con, req, FALSE);
1490 :
1491 21257 : return (http_fin_fd_req(con, req));
1492 :
1493 0 : malloc_close_err:
1494 0 : httpd_fin_fd_close(con);
1495 0 : malloc_err:
1496 0 : VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_get(): %m\n"));
1497 : }
1498 :
1499 : int http_req_op_opts(struct Con *con, Httpd_req_data *req)
1500 528 : {
1501 528 : Vstr_base *out = con->evnt->io_w;
1502 528 : Vstr_base *fname = req->fname;
1503 528 : uintmax_t tmp = 0;
1504 :
1505 528 : if (fname->conf->malloc_bad)
1506 0 : goto malloc_err;
1507 :
1508 264 : assert(VPREFIX(fname, 1, fname->len, "/") ||
1509 : !req->policy->use_vhosts_name ||
1510 : !req->policy->use_host_chk ||
1511 : !req->policy->use_host_err_400 ||
1512 : VEQ(con->evnt->io_r, req->path_pos, req->path_len, "*"));
1513 :
1514 : /* apache doesn't test for 404's here ... which seems weird */
1515 :
1516 528 : http_app_def_hdrs(con, req, 200, "OK", 0, NULL, TRUE, 0);
1517 528 : HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
1518 528 : http_app_end_hdrs(out);
1519 528 : if (out->conf->malloc_bad)
1520 0 : goto malloc_err;
1521 :
1522 528 : vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
1523 : "OPTIONS", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
1524 528 : http_vlg_def(con, req, FALSE);
1525 :
1526 528 : return (http_fin_req(con, req));
1527 :
1528 0 : malloc_err:
1529 0 : VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_opts(): %m\n"));
1530 : }
1531 :
1532 : int http_req_op_trace(struct Con *con, Httpd_req_data *req)
1533 312 : {
1534 312 : Vstr_base *data = con->evnt->io_r;
1535 312 : Vstr_base *out = con->evnt->io_w;
1536 312 : uintmax_t tmp = req->len;
1537 :
1538 312 : http_app_def_hdrs(con, req, 200, "OK", req->now,
1539 : "message/http", FALSE, tmp);
1540 312 : http_app_end_hdrs(out);
1541 312 : vstr_add_vstr(out, out->len, data, 1, req->len, VSTR_TYPE_ADD_DEF);
1542 312 : if (out->conf->malloc_bad)
1543 0 : VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_trace(): %m\n"));
1544 :
1545 312 : vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
1546 : "TRACE", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
1547 312 : http_vlg_def(con, req, FALSE);
1548 :
1549 312 : return (http_fin_req(con, req));
1550 : }
1551 :
1552 : int httpd_init_default_hostname(Opt_serv_policy_opts *sopts)
1553 178 : {
1554 178 : Httpd_policy_opts *popts = (Httpd_policy_opts *)sopts;
1555 178 : Vstr_base *nhn = popts->default_hostname;
1556 178 : Vstr_base *chn = NULL;
1557 :
1558 178 : if (!httpd__valid_hostname(nhn, 1, nhn->len, FALSE))
1559 0 : vstr_del(nhn, 1, nhn->len);
1560 :
1561 178 : if (nhn->len)
1562 142 : return (TRUE);
1563 :
1564 36 : if (sopts != sopts->beg->def_policy)
1565 6 : chn = ((Httpd_policy_opts *)sopts->beg->def_policy)->default_hostname;
1566 :
1567 36 : if (chn)
1568 6 : HTTPD_APP_REF_ALLVSTR(nhn, chn);
1569 : else
1570 : {
1571 30 : opt_serv_sc_append_hostname(nhn, 0);
1572 30 : vstr_conv_lowercase(nhn, 1, nhn->len);
1573 : }
1574 :
1575 36 : return (!nhn->conf->malloc_bad);
1576 : }
1577 :
1578 : static int httpd__serv_send_err(struct Con *con, const char *msg)
1579 0 : {
1580 0 : if (errno != EPIPE)
1581 0 : vlg_warn(vlg, "send(%s): %m\n", msg);
1582 : else
1583 0 : vlg_dbg2(vlg, "send(%s): SIGPIPE $<sa:%p>\n", msg, CON_CEVNT_SA(con));
1584 :
1585 0 : return (FALSE);
1586 : }
1587 :
1588 : static int httpd_serv_q_send(struct Con *con)
1589 17787532 : {
1590 17787532 : vlg_dbg2(vlg, "http Q send $<sa:%p>\n", CON_CEVNT_SA(con));
1591 17787532 : if (!evnt_send_add(con->evnt, TRUE, HTTPD_CONF_MAX_WAIT_SEND))
1592 0 : return (httpd__serv_send_err(con, "Q"));
1593 :
1594 : /* queued */
1595 17787532 : return (TRUE);
1596 : }
1597 :
1598 : static int httpd__serv_fin_send(struct Con *con)
1599 39559 : {
1600 39559 : if (con->keep_alive)
1601 : /* need to try immediately, as we might have already got the next req */
1602 27602 : return (http_parse_req(con));
1603 :
1604 11957 : return (evnt_shutdown_w(con->evnt));
1605 : }
1606 :
1607 : /* NOTE: lim is just a hint ... we can send more */
1608 : static int httpd__serv_send_lim(struct Con *con, const char *emsg,
1609 : unsigned int lim, int *cont)
1610 17840982 : {
1611 17840982 : Vstr_base *out = con->evnt->io_w;
1612 :
1613 17840982 : *cont = FALSE;
1614 :
1615 47467851 : while (out->len >= lim)
1616 : {
1617 23555725 : if (!con->io_limit_num--) return (httpd_serv_q_send(con));
1618 :
1619 11785887 : if (!evnt_send(con->evnt))
1620 0 : return (httpd__serv_send_err(con, emsg));
1621 : }
1622 :
1623 6071144 : *cont = TRUE;
1624 6071144 : return (TRUE);
1625 : }
1626 :
1627 : int httpd_serv_send(struct Con *con)
1628 17838664 : {
1629 17838664 : Vstr_base *out = con->evnt->io_w;
1630 17838664 : int cont = FALSE;
1631 17838664 : int ret = FALSE;
1632 17838664 : struct File_sect *fs = NULL;
1633 :
1634 3722343 : ASSERT(!out->conf->malloc_bad);
1635 :
1636 17838664 : if (!con->fs_num)
1637 : {
1638 1928817 : ASSERT(!con->fs_off);
1639 :
1640 11525516 : ret = httpd__serv_send_lim(con, "end", 1, &cont);
1641 11525516 : if (!cont)
1642 11485957 : return (ret);
1643 :
1644 39559 : return (httpd__serv_fin_send(con));
1645 : }
1646 :
1647 1793526 : ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
1648 6313148 : fs = &con->fs[con->fs_off];
1649 1793526 : ASSERT((fs->fd != -1) && fs->len);
1650 :
1651 6313148 : if (con->use_sendfile)
1652 : {
1653 6030501 : unsigned int ern = 0;
1654 :
1655 6030501 : ret = httpd__serv_send_lim(con, "sendfile", 1, &cont);
1656 6030501 : if (!cont)
1657 5362 : return (ret);
1658 :
1659 16343619 : while (fs->len)
1660 : {
1661 12041613 : if (!con->io_limit_num--) return (httpd_serv_q_send(con));
1662 :
1663 6023919 : if (!evnt_sendfile(con->evnt, fs->fd, &fs->off, &fs->len, &ern))
1664 : {
1665 3 : if (ern == VSTR_TYPE_SC_READ_FD_ERR_EOF)
1666 3 : goto file_eof_end;
1667 :
1668 0 : if (errno == EPIPE)
1669 : {
1670 0 : vlg_dbg2(vlg, "sendfile: SIGPIPE $<sa:%p>\n", CON_CEVNT_SA(con));
1671 0 : return (FALSE);
1672 : }
1673 :
1674 0 : if (errno == ENOSYS)
1675 0 : httpd__disable_sendfile();
1676 0 : vlg_warn(vlg, "sendfile: %m\n");
1677 :
1678 0 : if (lseek64(fs->fd, fs->off, SEEK_SET) == -1)
1679 0 : VLG_WARN_RET(FALSE,
1680 : (vlg, "lseek(<sendfile>,off=%ju): %m\n", fs->off));
1681 :
1682 0 : con->use_sendfile = FALSE;
1683 0 : return (httpd_serv_send(con)); /* recurse */
1684 : }
1685 : }
1686 :
1687 3713 : goto file_end;
1688 : }
1689 :
1690 507351 : while (fs->len)
1691 : {
1692 284965 : ret = httpd__serv_send_lim(con, "max", EX_MAX_W_DATA_INCORE, &cont);
1693 284965 : if (!cont)
1694 278519 : return (ret);
1695 :
1696 6446 : switch (evnt_sc_read_send(con->evnt, fs->fd, &fs->len))
1697 : {
1698 : case EVNT_IO_OK:
1699 1155 : ASSERT_NO_SWITCH_DEF();
1700 :
1701 : case EVNT_IO_READ_ERR:
1702 0 : vlg_warn(vlg, "read: %m\n");
1703 0 : out->conf->malloc_bad = FALSE;
1704 :
1705 2229 : case EVNT_IO_READ_FIN: goto file_end;
1706 1 : case EVNT_IO_READ_EOF: goto file_eof_end;
1707 :
1708 : case EVNT_IO_SEND_ERR:
1709 0 : return (httpd__serv_send_err(con, "io_get"));
1710 : }
1711 : }
1712 :
1713 0 : ASSERT_NOT_REACHED();
1714 :
1715 11568 : file_end:
1716 5626 : ASSERT(!fs->len);
1717 11568 : if (con->use_mpbr) /* multipart/byterange */
1718 : {
1719 208 : ASSERT(con->mpbr_ct);
1720 208 : ASSERT(con->mpbr_fs_len);
1721 :
1722 416 : if (!(fs = httpd__fd_next(con)))
1723 : {
1724 128 : vstr_add_cstr_ptr(out, out->len, "--SEP--" HTTP_EOL);
1725 128 : return (httpd_serv_send(con)); /* restart with a read, or finish */
1726 : }
1727 288 : con->use_mmap = FALSE;
1728 288 : if (!httpd_serv_call_seek(con, fs))
1729 0 : VLG_WARN_RET(FALSE, (vlg, "lseek(<mpbr>,off=%ju): %m\n", fs->off));
1730 :
1731 288 : http_app_hdrs_mpbr(con, fs);
1732 288 : return (httpd_serv_send(con)); /* start outputting next file section */
1733 : }
1734 :
1735 11157 : file_eof_end:
1736 11157 : if (fs->len) /* something bad happened, just kill the connection */
1737 5 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
1738 :
1739 11157 : httpd_fin_fd_close(con);
1740 11157 : return (httpd_serv_send(con)); /* restart with a read, or finish */
1741 : }
1742 :
1743 : int httpd_serv_recv(struct Con *con)
1744 50488 : {
1745 50488 : unsigned int ern = 0;
1746 50488 : int ret = 0;
1747 50488 : Vstr_base *data = con->evnt->io_r;
1748 :
1749 24463 : ASSERT(!con->evnt->io_r_shutdown);
1750 :
1751 50488 : if (!con->io_limit_num--)
1752 0 : return (TRUE);
1753 :
1754 50488 : if (!(ret = evnt_recv(con->evnt, &ern)))
1755 : {
1756 11938 : if (ern != VSTR_TYPE_SC_READ_FD_ERR_EOF)
1757 : {
1758 0 : vlg_dbg2(vlg, "RECV ERR from[$<sa:%p>]: %u\n", CON_CEVNT_SA(con), ern);
1759 0 : goto con_cleanup;
1760 : }
1761 11938 : if (!evnt_shutdown_r(con->evnt, TRUE))
1762 11638 : goto con_cleanup;
1763 : }
1764 :
1765 38850 : if (con->fs_num) /* may need to stop input, until we can deal with next req */
1766 : {
1767 569 : ASSERT(con->keep_alive || con->parsed_method_ver_1_0);
1768 :
1769 804 : if (con->policy->max_header_sz && (data->len > con->policy->max_header_sz))
1770 74 : evnt_wait_cntl_del(con->evnt, POLLIN);
1771 :
1772 804 : return (TRUE);
1773 : }
1774 :
1775 38046 : if (http_parse_req(con))
1776 37850 : return (TRUE);
1777 :
1778 11834 : con_cleanup:
1779 11834 : con->evnt->io_r->conf->malloc_bad = FALSE;
1780 11834 : con->evnt->io_w->conf->malloc_bad = FALSE;
1781 :
1782 11834 : return (FALSE);
1783 : }
1784 :
1785 : int httpd_con_init(struct Con *con, struct Acpt_listener *acpt_listener)
1786 12052 : {
1787 12052 : int ret = TRUE;
1788 12052 : Httpd_policy_opts *po = (Httpd_policy_opts *)httpd_opts->s->def_policy;
1789 :
1790 12052 : con->mpbr_ct = NULL;
1791 :
1792 12052 : con->fs = con->fs_store;
1793 12052 : con->fs->len = 0;
1794 12052 : con->fs->fd = -1;
1795 12052 : con->fs_off = 0;
1796 12052 : con->fs_num = 0;
1797 12052 : con->fs_sz = 1;
1798 :
1799 12052 : con->vary_star = FALSE;
1800 12052 : con->keep_alive = HTTP_NON_KEEP_ALIVE;
1801 12052 : con->evnt->acpt_sa_ref = vstr_ref_add(acpt_listener->ref);
1802 12052 : con->use_mpbr = FALSE;
1803 12052 : con->use_mmap = FALSE;
1804 :
1805 12052 : con->parsed_method_ver_1_0 = FALSE;
1806 :
1807 12052 : if (acpt_listener->def_policy)
1808 416 : po = acpt_listener->def_policy->ptr;
1809 :
1810 12052 : httpd_policy_change_con(con, po);
1811 :
1812 12052 : if (!httpd_policy_connection(con,
1813 : httpd_opts->conf, httpd_opts->match_connection))
1814 : {
1815 0 : Vstr_base *s1 = con->policy->s->policy_name;
1816 0 : Vstr_base *s2 = httpd_opts->conf->tmp;
1817 :
1818 0 : vlg_info(vlg, "CONF-MAIN-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
1819 : " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
1820 0 : ret = FALSE;
1821 : }
1822 12052 : else if (con->evnt->flag_q_closed)
1823 : {
1824 0 : Vstr_base *s1 = con->policy->s->policy_name;
1825 0 : vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
1826 : CON_CEVNT_SA(con), s1);
1827 0 : ret = FALSE;
1828 : }
1829 :
1830 12052 : return (ret);
1831 : }
|