1 /*
2 * Copyright (C) 2004, 2005 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_USE_NONBLOCKING_OPEN 1
29 #define EX_UTILS_RET_FAIL 1
30 #include "ex_utils.h"
31
32 #include "mk.h"
33
34 #include "vlg.h"
35
36 #define HTTPD_HAVE_GLOBAL_OPTS 1
37 #include "httpd.h"
38 #include "httpd_policy.h"
39
40 #ifndef POSIX_FADV_SEQUENTIAL
41 # define posix_fadvise64(x1, x2, x3, x4) (errno = ENOSYS, -1)
42 #endif
43
44 #ifdef VSTR_AUTOCONF_NDEBUG
45 # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
46 # define HTTP_CONF_SAFE_PRINT_REQ TRUE
47 #else
48 # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
49 # define HTTP_CONF_SAFE_PRINT_REQ FALSE
50 #endif
51 #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
52
53 #define CLEN VSTR__AT_COMPILE_STRLEN
54
55 /* is the cstr a prefix of the vstr */
56 #define VPREFIX(vstr, p, l, cstr) \
57 (((l) >= CLEN(cstr)) && \
58 vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
59 /* is the cstr a suffix of the vstr */
60 #define VSUFFIX(vstr, p, l, cstr) \
61 (((l) >= CLEN(cstr)) && \
62 vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
63
64 /* is the cstr a prefix of the vstr, no case */
65 #define VIPREFIX(vstr, p, l, cstr) \
66 (((l) >= CLEN(cstr)) && \
67 vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
68
69 /* for simplicity */
70 #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
71 #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
72
73 #ifndef SWAP_TYPE
74 #define SWAP_TYPE(x, y, type) do { \
75 type internal_local_tmp = (x); \
76 (x) = (y); \
77 (y) = internal_local_tmp; \
78 } while (FALSE)
79 #endif
80
81 #define HTTP__HDR_SET(req, h, p, l) do { \
82 (req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
83 (req)-> http_hdrs -> hdr_ ## h ->len = (l); \
84 } while (FALSE)
85 #define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
86 (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
87 (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
88 } while (FALSE)
89
90 #define HTTP__XTRA_HDR_INIT(x) do { \
91 req-> x ## _vs1 = NULL; \
92 req-> x ## _pos = 0; \
93 req-> x ## _len = 0; \
94 } while (FALSE)
95
96 #define HTTP__XTRA_HDR_PARAMS(req, x) \
97 (req)-> x ## _vs1, (req)-> x ## _pos, (req)-> x ## _len
98
99
100 HTTPD_CONF_MAIN_DECL_OPTS(httpd_opts);
101
102 static Vlg *vlg = NULL;
103
104
httpd_init(Vlg * passed_vlg)105 void httpd_init(Vlg *passed_vlg)
106 {
107 ASSERT(passed_vlg && !vlg);
108 vlg = passed_vlg;
109 }
110
httpd_exit(void)111 void httpd_exit(void)
112 {
113 ASSERT(vlg);
114 vlg = NULL;
115 }
116
http__clear_hdrs(struct Httpd_req_data * req)117 static void http__clear_hdrs(struct Httpd_req_data *req)
118 {
119 Vstr_base *tmp = req->http_hdrs->multi->combiner_store;
120
121 ASSERT(tmp);
122
123 HTTP__HDR_SET(req, ua, 0, 0);
124 HTTP__HDR_SET(req, referer, 0, 0);
125
126 HTTP__HDR_SET(req, expect, 0, 0);
127 HTTP__HDR_SET(req, host, 0, 0);
128 HTTP__HDR_SET(req, if_modified_since, 0, 0);
129 HTTP__HDR_SET(req, if_range, 0, 0);
130 HTTP__HDR_SET(req, if_unmodified_since, 0, 0);
131 HTTP__HDR_SET(req, authorization, 0, 0);
132
133 vstr_del(tmp, 1, tmp->len);
134 HTTP__HDR_MULTI_SET(req, accept, 0, 0);
135 HTTP__HDR_MULTI_SET(req, accept_charset, 0, 0);
136 HTTP__HDR_MULTI_SET(req, accept_encoding, 0, 0);
137 HTTP__HDR_MULTI_SET(req, accept_language, 0, 0);
138 HTTP__HDR_MULTI_SET(req, connection, 0, 0);
139 HTTP__HDR_MULTI_SET(req, if_match, 0, 0);
140 HTTP__HDR_MULTI_SET(req, if_none_match, 0, 0);
141 HTTP__HDR_MULTI_SET(req, range, 0, 0);
142 }
143
http__clear_xtra(struct Httpd_req_data * req)144 static void http__clear_xtra(struct Httpd_req_data *req)
145 {
146 if (req->xtra_content)
147 vstr_del(req->xtra_content, 1, req->xtra_content->len);
148
149 HTTP__XTRA_HDR_INIT(content_type);
150 HTTP__XTRA_HDR_INIT(content_disposition);
151 HTTP__XTRA_HDR_INIT(content_language);
152 HTTP__XTRA_HDR_INIT(content_location);
153 HTTP__XTRA_HDR_INIT(content_md5);
154 HTTP__XTRA_HDR_INIT(gzip_content_md5);
155 HTTP__XTRA_HDR_INIT(bzip2_content_md5);
156 HTTP__XTRA_HDR_INIT(cache_control);
157 HTTP__XTRA_HDR_INIT(etag);
158 HTTP__XTRA_HDR_INIT(gzip_etag);
159 HTTP__XTRA_HDR_INIT(bzip2_etag);
160 HTTP__XTRA_HDR_INIT(expires);
161 HTTP__XTRA_HDR_INIT(link);
162 HTTP__XTRA_HDR_INIT(p3p);
163 HTTP__XTRA_HDR_INIT(ext_vary_a);
164 HTTP__XTRA_HDR_INIT(ext_vary_ac);
165 HTTP__XTRA_HDR_INIT(ext_vary_al);
166 }
167
http_req_make(struct Con * con)168 Httpd_req_data *http_req_make(struct Con *con)
169 {
170 static Httpd_req_data real_req[1];
171 Httpd_req_data *req = real_req;
172 const Httpd_policy_opts *policy = NULL;
173
174 ASSERT(!req->using_req);
175
176 if (!req->done_once)
177 {
178 Vstr_conf *conf = NULL;
179
180 if (con)
181 conf = con->evnt->io_w->conf;
182
183 if (!(req->fname = vstr_make_base(conf)) ||
184 !(req->http_hdrs->multi->combiner_store = vstr_make_base(conf)) ||
185 !(req->sects = vstr_sects_make(8)))
186 return (NULL);
187
188 req->f_mmap = NULL;
189 req->xtra_content = NULL;
190 }
191
192 http__clear_hdrs(req);
193
194 http__clear_xtra(req);
195
196 req->http_hdrs->multi->comb = con ? con->evnt->io_r : NULL;
197
198 vstr_del(req->fname, 1, req->fname->len);
199
200 req->now = evnt_sc_time();
201
202 req->len = 0;
203
204 req->path_pos = 0;
205 req->path_len = 0;
206
207 req->error_code = 0;
208 req->error_line = "";
209 req->error_msg = "";
210 req->error_len = 0;
211
212 req->sects->num = 0;
213 /* f_stat */
214 if (con)
215 req->orig_io_w_len = con->evnt->io_w->len;
216
217 /* NOTE: These should probably be allocated at init time, depending on the
218 * option flags given */
219 ASSERT(!req->f_mmap || !req->f_mmap->len);
220 if (req->f_mmap)
221 vstr_del(req->f_mmap, 1, req->f_mmap->len);
222 ASSERT(!req->xtra_content || !req->xtra_content->len);
223 if (req->xtra_content)
224 vstr_del(req->xtra_content, 1, req->xtra_content->len);
225
226 req->vhost_prefix_len = 0;
227
228 req->sects->malloc_bad = FALSE;
229
230 req->content_encoding_gzip = FALSE;
231 req->content_encoding_bzip2 = FALSE;
232 req->content_encoding_identity = TRUE;
233
234 req->output_keep_alive_hdr = FALSE;
235
236 req->user_return_error_code = FALSE;
237
238 req->vary_star = con ? con->vary_star : FALSE;
239 req->vary_a = FALSE;
240 req->vary_ac = FALSE;
241 req->vary_ae = FALSE;
242 req->vary_al = FALSE;
243 req->vary_rf = FALSE;
244 req->vary_ua = FALSE;
245
246 req->direct_uri = FALSE;
247 req->direct_filename = FALSE;
248 req->skip_document_root = FALSE;
249
250 req->ver_0_9 = FALSE;
251 req->ver_1_1 = FALSE;
252 req->head_op = FALSE;
253
254 req->chked_encoded_path = FALSE;
255
256 req->neg_content_type_done = FALSE;
257 req->neg_content_lang_done = FALSE;
258
259 req->conf_secure_dirs = FALSE;
260 req->conf_friendly_dirs = FALSE;
261
262 req->done_once = TRUE;
263 req->using_req = TRUE;
264
265 req->malloc_bad = FALSE;
266
267 if (con)
268 policy = con->policy;
269 else
270 policy = (Httpd_policy_opts *)httpd_opts->s->def_policy;
271 httpd_policy_change_req(req, policy);
272
273 return (req);
274 }
275
http_req_free(Httpd_req_data * req)276 void http_req_free(Httpd_req_data *req)
277 {
278 if (!req) /* for if/when move to malloc/free */
279 return;
280
281 ASSERT(req->done_once && req->using_req);
282
283 /* we do vstr deletes here to return the nodes back to the pool */
284 vstr_del(req->fname, 1, req->fname->len);
285 ASSERT(!req->http_hdrs->multi->combiner_store->len);
286 if (req->f_mmap)
287 vstr_del(req->f_mmap, 1, req->f_mmap->len);
288
289 http__clear_xtra(req);
290
291 req->http_hdrs->multi->comb = NULL;
292
293 req->using_req = FALSE;
294 }
295
http_req_exit(void)296 void http_req_exit(void)
297 {
298 Httpd_req_data *req = http_req_make(NULL);
299 struct Http_hdrs__multi *tmp = NULL;
300
301 if (!req)
302 return;
303
304 tmp = req->http_hdrs->multi;
305
306 vstr_free_base(req->fname); req->fname = NULL;
307 vstr_free_base(tmp->combiner_store); tmp->combiner_store = NULL;
308 vstr_free_base(req->f_mmap); req->f_mmap = NULL;
309 vstr_free_base(req->xtra_content); req->xtra_content = NULL;
310 vstr_sects_free(req->sects); req->sects = NULL;
311
312 req->done_once = FALSE;
313 req->using_req = FALSE;
314 }
315
316
317 /* HTTP crack -- Implied linear whitespace between tokens, note that it
318 * is *LWS == *([CRLF] 1*(SP | HT)) */
http__skip_lws(const Vstr_base * s1,size_t * pos,size_t * len)319 static void http__skip_lws(const Vstr_base *s1, size_t *pos, size_t *len)
320 {
321 size_t lws__len = 0;
322
323 ASSERT(s1 && pos && len);
324
325 while (TRUE)
326 {
327 if (VPREFIX(s1, *pos, *len, HTTP_EOL))
328 {
329 *len -= CLEN(HTTP_EOL); *pos += CLEN(HTTP_EOL);
330 }
331 else if (lws__len)
332 break;
333
334 if (!(lws__len = vstr_spn_cstr_chrs_fwd(s1, *pos, *len, HTTP_LWS)))
335 break;
336 *len -= lws__len;
337 *pos += lws__len;
338 }
339 }
340
341 /* prints out headers in human friedly way for log files */
342 #define PCUR (pos + (base->len - orig_len))
http_app_vstr_escape(Vstr_base * base,size_t pos,Vstr_base * sf,size_t sf_pos,size_t sf_len)343 static int http_app_vstr_escape(Vstr_base *base, size_t pos,
344 Vstr_base *sf, size_t sf_pos, size_t sf_len)
345 {
346 unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
347 Vstr_iter iter[1];
348 size_t orig_len = base->len;
349 int saved_malloc_bad = FALSE;
350 size_t norm_chr = 0;
351
352 if (!sf_len) /* hack around !sf_pos */
353 return (TRUE);
354
355 if (!vstr_iter_fwd_beg(sf, sf_pos, sf_len, iter))
356 return (FALSE);
357
358 saved_malloc_bad = base->conf->malloc_bad;
359 base->conf->malloc_bad = FALSE;
360 while (sf_len)
361 { /* assumes ASCII */
362 char scan = vstr_iter_fwd_chr(iter, NULL);
363
364 if ((scan >= ' ') && (scan <= '~') && (scan != '"') && (scan != '\\'))
365 ++norm_chr;
366 else
367 {
368 vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
369 sf_pos += norm_chr;
370 norm_chr = 0;
371
372 if (0) {}
373 else if (scan == '"') vstr_add_cstr_buf(base, PCUR, "\\\"");
374 else if (scan == '\\') vstr_add_cstr_buf(base, PCUR, "\\\\");
375 else if (scan == '\t') vstr_add_cstr_buf(base, PCUR, "\\t");
376 else if (scan == '\v') vstr_add_cstr_buf(base, PCUR, "\\v");
377 else if (scan == '\r') vstr_add_cstr_buf(base, PCUR, "\\r");
378 else if (scan == '\n') vstr_add_cstr_buf(base, PCUR, "\\n");
379 else if (scan == '\b') vstr_add_cstr_buf(base, PCUR, "\\b");
380 else
381 vstr_add_sysfmt(base, PCUR, "\\x%02hhx", scan);
382 ++sf_pos;
383 }
384
385 --sf_len;
386 }
387
388 vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
389
390 if (base->conf->malloc_bad)
391 return (FALSE);
392
393 base->conf->malloc_bad = saved_malloc_bad;
394 return (TRUE);
395 }
396 #undef PCUR
397
http__fmt__add_vstr_add_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)398 static int http__fmt__add_vstr_add_vstr(Vstr_base *base, size_t pos,
399 Vstr_fmt_spec *spec)
400 {
401 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
402 size_t sf_pos = VSTR_FMT_CB_ARG_VAL(spec, size_t, 1);
403 size_t sf_len = VSTR_FMT_CB_ARG_VAL(spec, size_t, 2);
404
405 return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
406 }
http_fmt_add_vstr_add_vstr(Vstr_conf * conf,const char * name)407 int http_fmt_add_vstr_add_vstr(Vstr_conf *conf, const char *name)
408 {
409 return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_vstr,
410 VSTR_TYPE_FMT_PTR_VOID,
411 VSTR_TYPE_FMT_SIZE_T,
412 VSTR_TYPE_FMT_SIZE_T,
413 VSTR_TYPE_FMT_END));
414 }
http__fmt__add_vstr_add_sect_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)415 static int http__fmt__add_vstr_add_sect_vstr(Vstr_base *base, size_t pos,
416 Vstr_fmt_spec *spec)
417 {
418 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
419 Vstr_sects *sects = VSTR_FMT_CB_ARG_PTR(spec, 1);
420 unsigned int num = VSTR_FMT_CB_ARG_VAL(spec, unsigned int, 2);
421 size_t sf_pos = VSTR_SECTS_NUM(sects, num)->pos;
422 size_t sf_len = VSTR_SECTS_NUM(sects, num)->len;
423
424 return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
425 }
http_fmt_add_vstr_add_sect_vstr(Vstr_conf * conf,const char * name)426 int http_fmt_add_vstr_add_sect_vstr(Vstr_conf *conf, const char *name)
427 {
428 return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_sect_vstr,
429 VSTR_TYPE_FMT_PTR_VOID,
430 VSTR_TYPE_FMT_PTR_VOID,
431 VSTR_TYPE_FMT_UINT,
432 VSTR_TYPE_FMT_END));
433 }
434
http__app_hdr_hdr(Vstr_base * out,const char * hdr)435 static void http__app_hdr_hdr(Vstr_base *out, const char *hdr)
436 {
437 vstr_add_cstr_buf(out, out->len, hdr);
438 vstr_add_cstr_buf(out, out->len, ": ");
439 }
http__app_hdr_eol(Vstr_base * out)440 static void http__app_hdr_eol(Vstr_base *out)
441 {
442 vstr_add_cstr_buf(out, out->len, HTTP_EOL);
443 }
444
445 /* use single node */
446 #define HTTP_APP_HDR_CONST_CSTR(o, h, c) \
447 vstr_add_cstr_ptr(out, (out)->len, h ": " c HTTP_EOL)
http_app_hdr_cstr(Vstr_base * out,const char * hdr,const char * data)448 static void http_app_hdr_cstr(Vstr_base *out, const char *hdr, const char *data)
449 {
450 http__app_hdr_hdr(out, hdr);
451 vstr_add_cstr_buf(out, out->len, data);
452 http__app_hdr_eol(out);
453 }
454
http_app_hdr_vstr(Vstr_base * out,const char * hdr,const Vstr_base * s1,size_t vpos,size_t vlen,unsigned int type)455 static void http_app_hdr_vstr(Vstr_base *out, const char *hdr,
456 const Vstr_base *s1, size_t vpos, size_t vlen,
457 unsigned int type)
458 {
459 http__app_hdr_hdr(out, hdr);
460 vstr_add_vstr(out, out->len, s1, vpos, vlen, type);
461 http__app_hdr_eol(out);
462 }
463
http_app_hdr_vstr_def(Vstr_base * out,const char * hdr,const Vstr_base * s1,size_t vpos,size_t vlen)464 static void http_app_hdr_vstr_def(Vstr_base *out, const char *hdr,
465 const Vstr_base *s1, size_t vpos, size_t vlen)
466 {
467 http__app_hdr_hdr(out, hdr);
468 vstr_add_vstr(out, out->len, s1, vpos, vlen, VSTR_TYPE_ADD_DEF);
469 http__app_hdr_eol(out);
470 }
471
http_app_hdr_conf_vstr(Vstr_base * out,const char * hdr,const Vstr_base * s1)472 static void http_app_hdr_conf_vstr(Vstr_base *out, const char *hdr,
473 const Vstr_base *s1)
474 {
475 http__app_hdr_hdr(out, hdr);
476 vstr_add_vstr(out, out->len, s1, 1, s1->len, VSTR_TYPE_ADD_DEF);
477 http__app_hdr_eol(out);
478 }
479
480 static void http_app_hdr_fmt(Vstr_base *out, const char *hdr,
481 const char *fmt, ...)
482 VSTR__COMPILE_ATTR_FMT(3, 4);
http_app_hdr_fmt(Vstr_base * out,const char * hdr,const char * fmt,...)483 static void http_app_hdr_fmt(Vstr_base *out, const char *hdr,
484 const char *fmt, ...)
485 {
486 va_list ap;
487
488 http__app_hdr_hdr(out, hdr);
489
490 va_start(ap, fmt);
491 vstr_add_vfmt(out, out->len, fmt, ap);
492 va_end(ap);
493
494 http__app_hdr_eol(out);
495 }
496
http_app_hdr_uintmax(Vstr_base * out,const char * hdr,VSTR_AUTOCONF_uintmax_t data)497 static void http_app_hdr_uintmax(Vstr_base *out, const char *hdr,
498 VSTR_AUTOCONF_uintmax_t data)
499 {
500 http__app_hdr_hdr(out, hdr);
501 vstr_add_fmt(out, out->len, "%ju", data);
502 http__app_hdr_eol(out);
503 }
504
505 #define HTTP__VARY_ADD(x, y) do { \
506 ASSERT((x) < (sizeof(varies_ptr) / sizeof(varies_ptr[0]))); \
507 \
508 varies_ptr[(x)] = (y); \
509 varies_len[(x)] = CLEN(y); \
510 ++(x); \
511 } while (FALSE)
512
http_app_def_hdrs(struct Con * con,struct Httpd_req_data * req,unsigned int http_ret_code,const char * http_ret_line,time_t mtime,const char * custom_content_type,int use_range,VSTR_AUTOCONF_uintmax_t content_length)513 void http_app_def_hdrs(struct Con *con, struct Httpd_req_data *req,
514 unsigned int http_ret_code,
515 const char *http_ret_line, time_t mtime,
516 const char *custom_content_type,
517 int use_range,
518 VSTR_AUTOCONF_uintmax_t content_length)
519 {
520 Vstr_base *out = con->evnt->io_w;
521 Date_store *ds = httpd_opts->date;
522
523 if (use_range)
524 use_range = req->policy->use_range;
525
526 vstr_add_fmt(out, out->len, "%s %03u %s" HTTP_EOL,
527 "HTTP/1.1", http_ret_code, http_ret_line);
528 http_app_hdr_cstr(out, "Date", date_rfc1123(ds, req->now));
529 http_app_hdr_conf_vstr(out, "Server", req->policy->server_name);
530
531 if (mtime)
532 { /* if mtime in future, chop it #14.29
533 * for cache validation we don't cmp last-modified == now either */
534 if (difftime(req->now, mtime) <= 0)
535 mtime = req->now;
536 http_app_hdr_cstr(out, "Last-Modified", date_rfc1123(ds, mtime));
537 }
538
539 switch (con->keep_alive)
540 {
541 case HTTP_NON_KEEP_ALIVE:
542 HTTP_APP_HDR_CONST_CSTR(out, "Connection", "close");
543 break;
544
545 case HTTP_1_0_KEEP_ALIVE:
546 HTTP_APP_HDR_CONST_CSTR(out, "Connection", "Keep-Alive");
547 /* FALLTHROUGH */
548 case HTTP_1_1_KEEP_ALIVE: /* max=xxx ? */
549 if (req->output_keep_alive_hdr)
550 http_app_hdr_fmt(out, "Keep-Alive",
551 "%s=%u", "timeout", req->policy->s->idle_timeout);
552
553 ASSERT_NO_SWITCH_DEF();
554 }
555
556 switch (http_ret_code)
557 {
558 case 200: /* OK */
559 /* case 206: */ /* OK - partial -- needed? */
560 case 301: case 302: case 303: case 307: /* Redirects */
561 case 304: /* Not modified */
562 case 406: /* Not accept - a */
563 /* case 410: */ /* Gone - like 404 or 301 ? */
564 case 412: /* Not accept - precondition */
565 case 413: /* Too large */
566 case 416: /* Bad range */
567 case 417: /* Not accept - expect contained something */
568 if (use_range)
569 HTTP_APP_HDR_CONST_CSTR(out, "Accept-Ranges", "bytes");
570 }
571
572 if (req->vary_star)
573 HTTP_APP_HDR_CONST_CSTR(out, "Vary", "*");
574 else if (req->vary_a || req->vary_ac || req->vary_ae || req->vary_al ||
575 req->vary_rf || req->vary_ua)
576 {
577 const char *varies_ptr[6];
578 size_t varies_len[6];
579 unsigned int num = 0;
580
581 if (req->vary_ua) HTTP__VARY_ADD(num, "User-Agent");
582 if (req->vary_rf) HTTP__VARY_ADD(num, "Referer");
583 if (req->vary_al) HTTP__VARY_ADD(num, "Accept-Language");
584 if (req->vary_ae) HTTP__VARY_ADD(num, "Accept-Encoding");
585 if (req->vary_ac) HTTP__VARY_ADD(num, "Accept-Charset");
586 if (req->vary_a) HTTP__VARY_ADD(num, "Accept");
587
588 ASSERT(num && (num <= 5));
589
590 http__app_hdr_hdr(out, "Vary");
591 while (num-- > 1)
592 {
593 vstr_add_buf(out, out->len, varies_ptr[num], varies_len[num]);
594 vstr_add_cstr_buf(out, out->len, ",");
595 }
596 ASSERT(num == 0);
597
598 vstr_add_buf(out, out->len, varies_ptr[0], varies_len[0]);
599 http__app_hdr_eol(out);
600 }
601
602 if (con->use_mpbr)
603 {
604 ASSERT(con->mpbr_ct && !con->mpbr_ct->len);
605 if (req->content_type_vs1 && req->content_type_len)
606 http_app_hdr_vstr_def(con->mpbr_ct, "Content-Type",
607 HTTP__XTRA_HDR_PARAMS(req, content_type));
608 http_app_hdr_cstr(out, "Content-Type",
609 "multipart/byteranges; boundary=SEP");
610 }
611 else if (req->content_type_vs1 && req->content_type_len)
612 http_app_hdr_vstr_def(out, "Content-Type",
613 HTTP__XTRA_HDR_PARAMS(req, content_type));
614 else if (custom_content_type) /* possible we don't send one */
615 http_app_hdr_cstr(out, "Content-Type", custom_content_type);
616
617 if (req->content_encoding_bzip2)
618 HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "bzip2");
619 else if (req->content_encoding_gzip)
620 {
621 if (req->content_encoding_xgzip)
622 HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "x-gzip");
623 else
624 HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "gzip");
625 }
626
627 if (!con->use_mpbr)
628 http_app_hdr_uintmax(out, "Content-Length", content_length);
629 }
630 #undef HTTP__VARY_ADD
631
http_app_end_hdrs(Vstr_base * out)632 static void http_app_end_hdrs(Vstr_base *out)
633 { /* apache contains a workaround for buggy Netscape 2.x, 3.x and 4.0beta */
634 http__app_hdr_eol(out);
635 }
636
http_vlg_def(struct Con * con,struct Httpd_req_data * req)637 static void http_vlg_def(struct Con *con, struct Httpd_req_data *req)
638 {
639 Vstr_base *data = con->evnt->io_r;
640 Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
641 Vstr_sect_node *h_ua = req->http_hdrs->hdr_ua;
642 Vstr_sect_node *h_r = req->http_hdrs->hdr_referer;
643
644 vlg_info(vlg, (" host[\"$<http-esc.vstr:%p%zu%zu>\"]"
645 " UA[\"$<http-esc.vstr:%p%zu%zu>\"]"
646 " ref[\"$<http-esc.vstr:%p%zu%zu>\"]"),
647 data, h_h->pos, h_h->len,
648 data, h_ua->pos, h_ua->len,
649 data, h_r->pos, h_r->len);
650
651 if (req->ver_0_9)
652 vlg_info(vlg, " ver[\"HTTP/0.9]\"");
653 else
654 vlg_info(vlg, " ver[\"$<vstr.sect:%p%p%u>\"]", data, req->sects, 3);
655
656 vlg_info(vlg, ": $<http-esc.vstr:%p%zu%zu>\n",
657 data, req->path_pos, req->path_len);
658 }
659
httpd__fd_next(struct Con * con)660 static struct File_sect *httpd__fd_next(struct Con *con)
661 {
662 struct File_sect *fs = NULL;
663
664 ASSERT(con->fs &&
665 (con->fs_off <= con->fs_num) && (con->fs_num <= con->fs_sz));
666
667 if (++con->fs_off >= con->fs_num)
668 {
669 fs = &con->fs[con->fs_off - 1];
670 fs->len = 0;
671 if (fs->fd != -1)
672 close(fs->fd);
673 fs->fd = -1;
674
675 fs = con->fs;
676 fs->fd = -1;
677 fs->len = 0;
678
679 con->fs_off = 0;
680 con->fs_num = 0;
681
682 con->use_mpbr = FALSE;
683 if (con->mpbr_ct)
684 vstr_del(con->mpbr_ct, 1, con->mpbr_ct->len);
685
686 return (NULL);
687 }
688
689 /* only allow multipart/byterange atm. where all fd's are the same */
690 ASSERT(con->fs[con->fs_off - 1].fd == con->fs[con->fs_off].fd);
691
692 fs = &con->fs[con->fs_off];
693 if (con->use_posix_fadvise)
694 posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
695
696 return (fs);
697 }
698
httpd_fin_fd_close(struct Con * con)699 void httpd_fin_fd_close(struct Con *con)
700 {
701 con->use_posix_fadvise = FALSE;
702 while (httpd__fd_next(con))
703 { /* do nothing */ }
704 }
705
http_fin_req(struct Con * con,Httpd_req_data * req)706 static int http_fin_req(struct Con *con, Httpd_req_data *req)
707 {
708 Vstr_base *out = con->evnt->io_w;
709
710 ASSERT(!out->conf->malloc_bad);
711 http__clear_hdrs(req);
712
713 /* Note: Not resetting con->parsed_method_ver_1_0,
714 * if it's non-0.9 now it should continue to be */
715
716 if (!con->keep_alive) /* all input is here */
717 {
718 evnt_wait_cntl_del(con->evnt, POLLIN);
719 req->len = con->evnt->io_r->len; /* delete it all */
720 }
721
722 vstr_del(con->evnt->io_r, 1, req->len);
723
724 evnt_put_pkt(con->evnt);
725
726 if (req->policy->use_tcp_cork)
727 evnt_fd_set_cork(con->evnt, TRUE);
728
729 http_req_free(req);
730
731 return (httpd_serv_send(con));
732 }
733
http_fin_fd_req(struct Con * con,Httpd_req_data * req)734 static int http_fin_fd_req(struct Con *con, Httpd_req_data *req)
735 {
736 ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
737 ASSERT(!con->fs_off);
738
739 if (req->head_op || con->use_mmap || !con->fs->len)
740 httpd_fin_fd_close(con);
741 else if ((con->use_posix_fadvise = req->policy->use_posix_fadvise))
742 {
743 struct File_sect *fs = con->fs;
744 posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
745 }
746
747 return (http_fin_req(con, req));
748 }
749
http_con_cleanup(struct Con * con,Httpd_req_data * req)750 static int http_con_cleanup(struct Con *con, Httpd_req_data *req)
751 {
752 con->evnt->io_r->conf->malloc_bad = FALSE;
753 con->evnt->io_w->conf->malloc_bad = FALSE;
754
755 http__clear_hdrs(req);
756 http_req_free(req);
757
758 return (FALSE);
759 }
760
http_con_close_cleanup(struct Con * con,Httpd_req_data * req)761 static int http_con_close_cleanup(struct Con *con, Httpd_req_data *req)
762 {
763 httpd_fin_fd_close(con);
764 return (http_con_cleanup(con, req));
765 }
766
767 /* try to use gzip content-encoding on entity */
http__try_encoded_content(struct Con * con,Httpd_req_data * req,const struct stat64 * req_f_stat,Vstr_base * fname,const char * zip_ext,size_t zip_len)768 static int http__try_encoded_content(struct Con *con, Httpd_req_data *req,
769 const struct stat64 *req_f_stat,
770 Vstr_base *fname,
771 const char *zip_ext, size_t zip_len)
772 {
773 const char *fname_cstr = NULL;
774 int fd = -1;
775 int ret = FALSE;
776
777 vstr_add_cstr_ptr(fname, fname->len, zip_ext);
778 fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
779
780 if (fname->conf->malloc_bad)
781 vlg_warn(vlg, "Failed to export cstr for '%s'\n", zip_ext);
782 else if ((fd = io_open_nonblock(fname_cstr)) == -1)
783 vstr_sc_reduce(fname, 1, fname->len, zip_len);
784 else
785 {
786 struct stat64 f_stat[1];
787
788 if (fstat64(fd, f_stat) == -1)
789 vlg_warn(vlg, "fstat: %m\n");
790 else if ((req->policy->use_public_only && !(f_stat->st_mode & S_IROTH)) ||
791 (S_ISDIR(f_stat->st_mode)) || (!S_ISREG(f_stat->st_mode)) ||
792 (req_f_stat->st_mtime > f_stat->st_mtime) ||
793 !f_stat->st_size || /* zero sized compressed files aren't valid */
794 (req_f_stat->st_size <= f_stat->st_size))
795 { /* ignore the encoded version */ }
796 else
797 {
798 ASSERT(con->fs && !con->fs_num && !con->fs_off);
799
800 /* swap, close the old fd (later) and use the new */
801 SWAP_TYPE(con->fs->fd, fd, int);
802
803 ASSERT(con->fs->len == (VSTR_AUTOCONF_uintmax_t)req_f_stat->st_size);
804 /* _only_ copy the new size over, mtime etc. is from the original file */
805 con->fs->len = req->f_stat->st_size = f_stat->st_size;
806 req->encoded_mtime = f_stat->st_mtime;
807 ret = TRUE;
808 }
809 close(fd);
810 }
811
812 return (ret);
813 }
814
815 #define HTTP__PARSE_CHK_RET_OK() do { \
816 HTTP_SKIP_LWS(data, pos, len); \
817 \
818 if (!len || \
819 VPREFIX(data, pos, len, ",") || \
820 (allow_more && VPREFIX(data, pos, len, ";"))) \
821 { \
822 *passed_pos = pos; \
823 *passed_len = len; \
824 \
825 return (TRUE); \
826 } \
827 } while (FALSE)
828
829 /* What is the quality parameter, value between 0 and 1000 inclusive.
830 * returns TRUE on success, FALSE on failure. */
http_parse_quality(Vstr_base * data,size_t * passed_pos,size_t * passed_len,int allow_more,unsigned int * val)831 static int http_parse_quality(Vstr_base *data,
832 size_t *passed_pos, size_t *passed_len,
833 int allow_more, unsigned int *val)
834 {
835 size_t pos = *passed_pos;
836 size_t len = *passed_len;
837
838 ASSERT(val);
839
840 *val = 1000;
841
842 HTTP_SKIP_LWS(data, pos, len);
843
844 *passed_pos = pos;
845 *passed_len = len;
846
847 if (!len || VPREFIX(data, pos, len, ","))
848 return (TRUE);
849 else if (VPREFIX(data, pos, len, ";"))
850 {
851 int lead_zero = FALSE;
852 unsigned int num_len = 0;
853 unsigned int parse_flags = VSTR_FLAG02(PARSE_NUM, NO_BEG_PM, NO_NEGATIVE);
854
855 len -= 1; pos += 1;
856 HTTP_SKIP_LWS(data, pos, len);
857
858 if (!VPREFIX(data, pos, len, "q"))
859 return (!!allow_more);
860
861 len -= 1; pos += 1;
862 HTTP_SKIP_LWS(data, pos, len);
863
864 if (!VPREFIX(data, pos, len, "="))
865 return (!!allow_more);
866
867 len -= 1; pos += 1;
868 HTTP_SKIP_LWS(data, pos, len);
869
870 /* if it's 0[.00?0?] TRUE, 0[.\d\d?\d?] or 1[.00?0?] is FALSE */
871 if (!(lead_zero = VPREFIX(data, pos, len, "0")) &&
872 !VPREFIX(data, pos, len, "1"))
873 return (FALSE);
874 *val = (!lead_zero) * 1000;
875
876 len -= 1; pos += 1;
877
878 HTTP__PARSE_CHK_RET_OK();
879
880 if (!VPREFIX(data, pos, len, "."))
881 return (FALSE);
882
883 len -= 1; pos += 1;
884 HTTP_SKIP_LWS(data, pos, len);
885
886 *val += vstr_parse_uint(data, pos, len, 10 | parse_flags, &num_len, NULL);
887 if (!num_len || (num_len > 3) || (*val > 1000))
888 return (FALSE);
889 if (num_len < 3) *val *= 10;
890 if (num_len < 2) *val *= 10;
891 ASSERT(*val <= 1000);
892
893 len -= num_len; pos += num_len;
894
895 HTTP__PARSE_CHK_RET_OK();
896 }
897
898 return (FALSE);
899 }
900 #undef HTTP__PARSE_CHK_RET_OK
901
http_parse_accept_encoding(struct Httpd_req_data * req)902 static int http_parse_accept_encoding(struct Httpd_req_data *req)
903 {
904 Vstr_base *data = req->http_hdrs->multi->comb;
905 size_t pos = 0;
906 size_t len = 0;
907 unsigned int num = 0;
908 unsigned int gzip_val = 1001;
909 unsigned int bzip2_val = 1001;
910 unsigned int identity_val = 1001;
911 unsigned int star_val = 1001;
912 unsigned int dummy_val = 1001;
913
914 pos = req->http_hdrs->multi->hdr_accept_encoding->pos;
915 len = req->http_hdrs->multi->hdr_accept_encoding->len;
916
917 if (!req->policy->use_err_406 && !req->allow_accept_encoding)
918 return (FALSE);
919 req->vary_ae = TRUE;
920
921 if (!len)
922 return (FALSE);
923
924 req->content_encoding_xgzip = FALSE;
925
926 while (len)
927 {
928 size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
929 HTTP_EOL HTTP_LWS ";,");
930
931 ++num;
932
933 if (0) { }
934 else if (VEQ(data, pos, tmp, "identity"))
935 {
936 len -= tmp; pos += tmp;
937 if (!http_parse_quality(data, &pos, &len, FALSE, &identity_val))
938 return (FALSE);
939 }
940 else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "gzip"))
941 {
942 len -= tmp; pos += tmp;
943 req->content_encoding_xgzip = FALSE;
944 if (!http_parse_quality(data, &pos, &len, FALSE, &gzip_val))
945 return (FALSE);
946 }
947 else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "bzip2"))
948 {
949 len -= tmp; pos += tmp;
950 if (!http_parse_quality(data, &pos, &len, FALSE, &bzip2_val))
951 return (FALSE);
952 }
953 else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "x-gzip"))
954 {
955 len -= tmp; pos += tmp;
956 req->content_encoding_xgzip = TRUE;
957 if (!http_parse_quality(data, &pos, &len, FALSE, &gzip_val))
958 return (FALSE);
959 gzip_val = 1000; /* ignore quality on x-gzip - just parse for errors */
960 }
961 else if (VEQ(data, pos, tmp, "*"))
962 { /* "*;q=0,gzip" means TRUE ... and "*;q=1.0,gzip;q=0" means FALSE */
963 len -= tmp; pos += tmp;
964 if (!http_parse_quality(data, &pos, &len, FALSE, &star_val))
965 return (FALSE);
966 }
967 else
968 {
969 len -= tmp; pos += tmp;
970 if (!http_parse_quality(data, &pos, &len, FALSE, &dummy_val))
971 return (FALSE);
972 }
973
974 if (!len)
975 break;
976 assert(VPREFIX(data, pos, len, ","));
977 len -= 1; pos += 1;
978 HTTP_SKIP_LWS(data, pos, len);
979
980 if (req->policy->max_AE_nodes && (num >= req->policy->max_AE_nodes))
981 return (FALSE);
982 }
983
984 if (!req->allow_accept_encoding)
985 {
986 gzip_val = 0;
987 bzip2_val = 0;
988 }
989
990 if (gzip_val == 1001) gzip_val = star_val;
991 if (bzip2_val == 1001) bzip2_val = star_val;
992 if (identity_val == 1001) identity_val = star_val;
993
994 if (gzip_val == 1001) gzip_val = 0;
995 if (bzip2_val == 1001) bzip2_val = 0;
996 if (identity_val == 1001) identity_val = 1;
997
998 if (!identity_val)
999 req->content_encoding_identity = FALSE;
1000
1001 if ((identity_val > gzip_val) && (identity_val > bzip2_val))
1002 return (FALSE);
1003
1004 if (gzip_val <= bzip2_val)
1005 { /* currently bzip2 is "preferred" so this works well always */
1006 req->content_encoding_gzip = !!gzip_val;
1007 req->content_encoding_bzip2 = !!bzip2_val;
1008 }
1009 else
1010 { /* this doesn't "work well" if both are ok, and
1011 * only a *.bz2 file on disk. Maybe carry the quality values? */
1012 ASSERT(gzip_val);
1013 req->content_encoding_gzip = TRUE;
1014 req->content_encoding_bzip2 = FALSE;
1015 }
1016
1017 return (req->content_encoding_gzip || req->content_encoding_bzip2);
1018 }
1019
httpd__try_fd_encoding(struct Con * con,Httpd_req_data * req,const struct stat64 * fs,Vstr_base * fname)1020 static void httpd__try_fd_encoding(struct Con *con, Httpd_req_data *req,
1021 const struct stat64 *fs, Vstr_base *fname)
1022 { /* Might normally add "!req->head_op && ..." but
1023 * http://www.w3.org/TR/chips/#gl6 says that's bad */
1024 if (http_parse_accept_encoding(req))
1025 {
1026 if ( req->content_encoding_bzip2 &&
1027 !http__try_encoded_content(con, req, fs, fname, ".bz2", CLEN(".bz2")))
1028 req->content_encoding_bzip2 = FALSE;
1029 if (!req->content_encoding_bzip2 && req->content_encoding_gzip &&
1030 !http__try_encoded_content(con, req, fs, fname, ".gz", CLEN(".gz")))
1031 req->content_encoding_gzip = FALSE;
1032 }
1033 }
1034
httpd__disable_sendfile(void)1035 static void httpd__disable_sendfile(void)
1036 {
1037 Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
1038
1039 while (scan)
1040 {
1041 Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
1042 tmp->use_sendfile = FALSE;
1043 scan = scan->next;
1044 }
1045 }
1046
httpd__disable_mmap(void)1047 static void httpd__disable_mmap(void)
1048 {
1049 Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
1050
1051 while (scan)
1052 {
1053 Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
1054 tmp->use_mmap = FALSE;
1055 scan = scan->next;
1056 }
1057 }
1058
httpd_serv_call_mmap(struct Con * con,struct Httpd_req_data * req,struct File_sect * fs)1059 static void httpd_serv_call_mmap(struct Con *con, struct Httpd_req_data *req,
1060 struct File_sect *fs)
1061 {
1062 static long pagesz = 0;
1063 Vstr_base *data = con->evnt->io_r;
1064 VSTR_AUTOCONF_uintmax_t mmoff = fs->off;
1065 VSTR_AUTOCONF_uintmax_t mmlen = fs->len;
1066
1067 ASSERT(!req->f_mmap || !req->f_mmap->len);
1068 ASSERT(!con->use_mmap);
1069 ASSERT(!req->head_op);
1070
1071 if (con->use_sendfile)
1072 return;
1073
1074 if (con->fs_num > 1)
1075 return;
1076
1077 if (!pagesz)
1078 pagesz = sysconf(_SC_PAGESIZE);
1079 if (pagesz == -1)
1080 httpd__disable_mmap();
1081
1082 if (!req->policy->use_mmap ||
1083 (mmlen < HTTP_CONF_MMAP_LIMIT_MIN) || (mmlen > HTTP_CONF_MMAP_LIMIT_MAX))
1084 return;
1085
1086 /* mmap offset needs to be aligned - so tweak offset before and after */
1087 mmoff /= pagesz;
1088 mmoff *= pagesz;
1089 ASSERT(mmoff <= fs->off);
1090 mmlen += fs->off - mmoff;
1091
1092 if (!req->f_mmap && !(req->f_mmap = vstr_make_base(data->conf)))
1093 VLG_WARN_RET_VOID((vlg, /* fall back to read */
1094 "failed to allocate mmap Vstr.\n"));
1095 ASSERT(!req->f_mmap->len);
1096
1097 if (!vstr_sc_mmap_fd(req->f_mmap, 0, fs->fd, mmoff, mmlen, NULL))
1098 {
1099 if (errno == ENOSYS) /* also logs it */
1100 httpd__disable_mmap();
1101
1102 VLG_WARN_RET_VOID((vlg, /* fall back to read */
1103 "mmap($<http-esc.vstr:%p%zu%zu>,"
1104 "(%ju,%ju)->(%ju,%ju)): %m\n",
1105 req->fname, (size_t)1, req->fname->len,
1106 fs->off, fs->len, mmoff, mmlen));
1107 }
1108
1109 con->use_mmap = TRUE;
1110 vstr_del(req->f_mmap, 1, fs->off - mmoff); /* remove alignment */
1111
1112 ASSERT(req->f_mmap->len == fs->len);
1113 }
1114
httpd__serv_call_seek(struct Con * con,struct File_sect * fs)1115 static int httpd__serv_call_seek(struct Con *con, struct File_sect *fs)
1116 {
1117 if (con->use_mmap || con->use_sendfile)
1118 return (TRUE);
1119
1120 if (fs->off && fs->len && (lseek64(fs->fd, fs->off, SEEK_SET) == -1))
1121 return (FALSE);
1122
1123 return (TRUE);
1124 }
1125
httpd_serv_call_seek(struct Con * con,struct Httpd_req_data * req,struct File_sect * fs,unsigned int * http_ret_code,const char ** http_ret_line)1126 static void httpd_serv_call_seek(struct Con *con, struct Httpd_req_data *req,
1127 struct File_sect *fs,
1128 unsigned int *http_ret_code,
1129 const char ** http_ret_line)
1130 {
1131 ASSERT(!req->head_op);
1132
1133 if (!httpd__serv_call_seek(con, fs))
1134 { /* this should be impossible for normal files AFAIK */
1135 vlg_warn(vlg, "lseek($<http-esc.vstr:%p%zu%zu>,off=%ju): %m\n",
1136 req->fname, (size_t)1, req->fname->len, fs->off);
1137 /* opts->use_range - turn off? */
1138 req->http_hdrs->multi->hdr_range->pos = 0;
1139 *http_ret_code = 200;
1140 *http_ret_line = "OK - Range Failed";
1141 }
1142 }
1143
http__conf_req(struct Con * con,Httpd_req_data * req)1144 static int http__conf_req(struct Con *con, Httpd_req_data *req)
1145 {
1146 Conf_parse *conf = NULL;
1147
1148 if (!(conf = conf_parse_make(NULL)))
1149 return (FALSE);
1150
1151 if (!httpd_conf_req_parse_file(conf, con, req))
1152 {
1153 Vstr_base *s1 = req->policy->s->policy_name;
1154 Vstr_base *s2 = conf->tmp;
1155
1156 if (!req->user_return_error_code)
1157 vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
1158 " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
1159 conf_parse_free(conf);
1160 return (TRUE);
1161 }
1162 conf_parse_free(conf);
1163
1164 if (req->direct_uri)
1165 {
1166 Vstr_base *s1 = req->policy->s->policy_name;
1167 vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
1168 " Has URI.\n", CON_CEVNT_SA(con), s1);
1169 HTTPD_ERR_RET(req, 503, TRUE);
1170 }
1171
1172 return (TRUE);
1173 }
1174
http_app_hdrs_url(struct Con * con,Httpd_req_data * req)1175 static void http_app_hdrs_url(struct Con *con, Httpd_req_data *req)
1176 {
1177 Vstr_base *out = con->evnt->io_w;
1178
1179 if (req->cache_control_vs1)
1180 http_app_hdr_vstr_def(out, "Cache-Control",
1181 HTTP__XTRA_HDR_PARAMS(req, cache_control));
1182 if (req->expires_vs1 && (req->expires_time > req->f_stat->st_mtime))
1183 http_app_hdr_vstr_def(out, "Expires",
1184 HTTP__XTRA_HDR_PARAMS(req, expires));
1185 if (req->p3p_vs1)
1186 http_app_hdr_vstr_def(out, "P3P",
1187 HTTP__XTRA_HDR_PARAMS(req, p3p));
1188 }
1189
http_app_hdrs_file(struct Con * con,Httpd_req_data * req)1190 static void http_app_hdrs_file(struct Con *con, Httpd_req_data *req)
1191 {
1192 Vstr_base *out = con->evnt->io_w;
1193 time_t mtime = req->f_stat->st_mtime;
1194 time_t enc_mtime = req->encoded_mtime;
1195
1196 if (req->content_disposition_vs1)
1197 http_app_hdr_vstr_def(out, "Content-Disposition",
1198 HTTP__XTRA_HDR_PARAMS(req, content_disposition));
1199 if (req->content_language_vs1)
1200 http_app_hdr_vstr_def(out, "Content-Language",
1201 HTTP__XTRA_HDR_PARAMS(req, content_language));
1202 if (req->content_encoding_bzip2)
1203 {
1204 if (req->bzip2_content_md5_vs1 && (req->content_md5_time > enc_mtime))
1205 http_app_hdr_vstr_def(out, "Content-MD5",
1206 HTTP__XTRA_HDR_PARAMS(req, bzip2_content_md5));
1207 }
1208 else if (req->content_encoding_gzip)
1209 {
1210 if (req->gzip_content_md5_vs1 && (req->content_md5_time > enc_mtime))
1211 http_app_hdr_vstr_def(out, "Content-MD5",
1212 HTTP__XTRA_HDR_PARAMS(req, gzip_content_md5));
1213 }
1214 else if (req->content_md5_vs1 && (req->content_md5_time > mtime))
1215 http_app_hdr_vstr_def(out, "Content-MD5",
1216 HTTP__XTRA_HDR_PARAMS(req, content_md5));
1217
1218 if (req->content_encoding_bzip2)
1219 {
1220 if (req->bzip2_etag_vs1 && (req->etag_time > enc_mtime))
1221 http_app_hdr_vstr_def(out, "ETag",
1222 HTTP__XTRA_HDR_PARAMS(req, bzip2_etag));
1223 }
1224 else if (req->content_encoding_gzip)
1225 {
1226 if (req->gzip_etag_vs1 && (req->etag_time > enc_mtime))
1227 http_app_hdr_vstr_def(out, "ETag",
1228 HTTP__XTRA_HDR_PARAMS(req, gzip_etag));
1229 }
1230 else if (req->etag_vs1 && (req->etag_time > mtime))
1231 http_app_hdr_vstr_def(out, "ETag", HTTP__XTRA_HDR_PARAMS(req, etag));
1232
1233 if (req->link_vs1)
1234 http_app_hdr_vstr_def(out, "Link", HTTP__XTRA_HDR_PARAMS(req, link));
1235 }
1236
http_app_hdrs_mpbr(struct Con * con,struct File_sect * fs)1237 static void http_app_hdrs_mpbr(struct Con *con, struct File_sect *fs)
1238 {
1239 Vstr_base *out = con->evnt->io_w;
1240 VSTR_AUTOCONF_uintmax_t range_beg;
1241 VSTR_AUTOCONF_uintmax_t range_end;
1242
1243 ASSERT(fs && (fs->fd != -1));
1244
1245 range_beg = fs->off;
1246 range_end = range_beg + fs->len - 1;
1247
1248 vstr_add_cstr_ptr(out, out->len, "--SEP" HTTP_EOL);
1249 HTTPD_APP_REF_ALLVSTR(out, con->mpbr_ct);
1250 http_app_hdr_fmt(out, "Content-Range",
1251 "%s %ju-%ju/%ju", "bytes", range_beg, range_end,
1252 con->mpbr_fs_len);
1253 http_app_end_hdrs(out);
1254 }
1255
http_prepend_doc_root(Vstr_base * fname,Httpd_req_data * req)1256 static void http_prepend_doc_root(Vstr_base *fname, Httpd_req_data *req)
1257 {
1258 Vstr_base *dir = req->policy->document_root;
1259
1260 ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
1261 ASSERT((fname->len >= 1) && vstr_cmp_cstr_eq(fname, 1, 1, "/"));
1262 vstr_add_vstr(fname, 0, dir, 1, dir->len - 1, VSTR_TYPE_ADD_BUF_REF);
1263 }
1264
http_app_err_file(struct Con * con,Httpd_req_data * req,Vstr_base * fname,size_t * vhost_prefix_len)1265 static void http_app_err_file(struct Con *con, Httpd_req_data *req,
1266 Vstr_base *fname, size_t *vhost_prefix_len)
1267 {
1268 Vstr_base *dir = NULL;
1269
1270 ASSERT(con && req);
1271 ASSERT(vhost_prefix_len && !*vhost_prefix_len);
1272
1273 dir = req->policy->req_err_dir;
1274 ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, 1, 1, "/"));
1275 ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
1276 HTTPD_APP_REF_ALLVSTR(fname, dir);
1277
1278 vstr_add_fmt(fname, fname->len, "%u", req->error_code);
1279
1280 if (req->policy->use_vhosts_name)
1281 {
1282 Vstr_base *data = con->evnt->io_r;
1283 Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
1284 size_t orig_len = fname->len;
1285
1286 if (!h_h->len)
1287 httpd_sc_add_default_hostname(con, req, fname, 0);
1288 else if (vstr_add_vstr(fname, 0, data, /* add as buf's, for lowercase op */
1289 h_h->pos, h_h->len, VSTR_TYPE_ADD_DEF))
1290 vstr_conv_lowercase(fname, 1, h_h->len);
1291 vstr_add_cstr_ptr(fname, 0, "/");
1292
1293 *vhost_prefix_len = (fname->len - orig_len);
1294 }
1295 }
1296
http_fin_err_req(struct Con * con,Httpd_req_data * req)1297 static int http_fin_err_req(struct Con *con, Httpd_req_data *req)
1298 {
1299 Vstr_base *out = con->evnt->io_w;
1300 int use_cust_err_msg = FALSE;
1301
1302 ASSERT(req->error_code);
1303
1304 req->content_encoding_gzip = FALSE;
1305 req->content_encoding_bzip2 = FALSE;
1306
1307 if ((req->error_code == 400) || (req->error_code == 405) ||
1308 (req->error_code == 413) ||
1309 (req->error_code == 500) || (req->error_code == 501))
1310 con->keep_alive = HTTP_NON_KEEP_ALIVE;
1311
1312 ASSERT(con->fs && !con->fs_num);
1313
1314 vlg_info(vlg, "ERREQ from[$<sa:%p>] err[%03u %s]",
1315 CON_CEVNT_SA(con), req->error_code, req->error_line);
1316 if (req->sects->num >= 2)
1317 http_vlg_def(con, req);
1318 else
1319 vlg_info(vlg, "%s", "\n");
1320
1321 if (req->malloc_bad)
1322 { /* delete all input to give more room */
1323 ASSERT(req->error_code == 500);
1324 vstr_del(con->evnt->io_r, 1, con->evnt->io_r->len);
1325 }
1326 else if (((req->error_code == 400) ||
1327 (req->error_code == 403) ||
1328 (req->error_code == 404) ||
1329 (req->error_code == 410) ||
1330 (req->error_code == 500) ||
1331 (req->error_code == 503)) && req->policy->req_err_dir->len)
1332 { /* custom err message */
1333 Vstr_base *fname = vstr_make_base(req->fname->conf);
1334 size_t vhost_prefix_len = 0;
1335 const char *fname_cstr = NULL;
1336 struct stat64 f_stat[1];
1337
1338 if (!fname)
1339 goto fail_custom_err;
1340
1341 /*
1342 req->vary_star = con ? con->vary_star : FALSE;
1343 req->vary_a = FALSE;
1344 req->vary_ac = FALSE;
1345 req->vary_ae = FALSE;
1346 req->vary_al = FALSE;
1347 req->vary_rf = FALSE;
1348 req->vary_ua = FALSE;
1349 */
1350
1351 if (!req->user_return_error_code)
1352 {
1353 req->cache_control_vs1 = NULL;
1354 req->expires_vs1 = NULL;
1355 req->p3p_vs1 = NULL;
1356 }
1357
1358 if (req->user_return_error_code && req->direct_filename)
1359 {
1360 HTTPD_APP_REF_ALLVSTR(fname, req->fname);
1361 if (!req->skip_document_root)
1362 http_prepend_doc_root(fname, req);
1363 }
1364 else if (!req->policy->req_conf_dir)
1365 {
1366 http_app_err_file(con, req, fname, &vhost_prefix_len);
1367 http_prepend_doc_root(fname, req);
1368 }
1369 else
1370 {
1371 int conf_ret = FALSE;
1372 unsigned int code = req->error_code;
1373 unsigned int ncode = 0;
1374
1375 http_app_err_file(con, req, fname, &vhost_prefix_len);
1376
1377 req->content_type_vs1 = NULL;
1378 req->error_code = 0;
1379 req->skip_document_root = FALSE;
1380
1381 SWAP_TYPE(fname, req->fname, Vstr_base *);
1382 SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
1383 conf_ret = http__conf_req(con, req);
1384 SWAP_TYPE(fname, req->fname, Vstr_base *);
1385 SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
1386
1387 ncode = req->error_code;
1388 switch (code)
1389 {
1390 case 400: HTTPD_ERR(req, 400); break;
1391 case 403: HTTPD_ERR(req, 403); break;
1392 case 404: HTTPD_ERR(req, 404); break;
1393 case 410: HTTPD_ERR(req, 410); break;
1394 case 500: HTTPD_ERR(req, 500); break;
1395 case 503: HTTPD_ERR(req, 503);
1396 ASSERT_NO_SWITCH_DEF();
1397 }
1398 if (!conf_ret)
1399 goto fail_custom_err;
1400 if (ncode)
1401 goto fail_custom_err; /* don't allow remapping errors -- loops */
1402
1403 if (!req->skip_document_root)
1404 http_prepend_doc_root(fname, req);
1405 }
1406 fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
1407
1408 if (fname->conf->malloc_bad)
1409 goto fail_custom_err;
1410 ASSERT(con->fs && (con->fs->fd == -1));
1411 if ((con->fs->fd = io_open_nonblock(fname_cstr)) == -1)
1412 goto fail_custom_err;
1413
1414 if (fstat64(con->fs->fd, f_stat) == -1)
1415 {
1416 httpd_fin_fd_close(con);
1417 goto fail_custom_err;
1418 }
1419 if (req->policy->use_public_only && !(f_stat->st_mode & S_IROTH))
1420 {
1421 httpd_fin_fd_close(con);
1422 goto fail_custom_err;
1423 }
1424 if (!S_ISREG(f_stat->st_mode))
1425 {
1426 httpd_fin_fd_close(con);
1427 goto fail_custom_err;
1428 }
1429 con->fs_off = 0;
1430 con->fs_num = 1;
1431 con->fs->off = 0;
1432 con->fs->len = f_stat->st_size;
1433
1434 if (!req->ver_0_9)
1435 httpd__try_fd_encoding(con, req, f_stat, fname);
1436
1437 con->use_mmap = FALSE;
1438 if (!req->head_op)
1439 httpd_serv_call_mmap(con, req, con->fs);
1440
1441 req->error_len = con->fs->len;
1442 use_cust_err_msg = TRUE;
1443
1444 fail_custom_err:
1445 fname->conf->malloc_bad = FALSE;
1446 vstr_free_base(fname);
1447 }
1448
1449 if (!use_cust_err_msg)
1450 req->content_type_vs1 = NULL;
1451
1452 if (!req->ver_0_9)
1453 { /* use_range is dealt with inside */
1454 http_app_def_hdrs(con, req, req->error_code, req->error_line,
1455 httpd_opts->beg_time, "text/html", TRUE, req->error_len);
1456
1457 if (req->error_code == 416)
1458 http_app_hdr_fmt(out, "Content-Range", "%s */%ju", "bytes",
1459 (VSTR_AUTOCONF_uintmax_t)req->f_stat->st_size);
1460
1461 if (req->error_code == 401)
1462 http_app_hdr_fmt(out, "WWW-Authenticate",
1463 "Basic realm=\"$<vstr.all:%p>\"",
1464 req->policy->auth_realm);
1465
1466 if ((req->error_code == 405) || (req->error_code == 501))
1467 HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
1468
1469 if ((req->error_code == 301) || (req->error_code == 302) ||
1470 (req->error_code == 303) || (req->error_code == 307))
1471 { /* make sure we haven't screwed up and allowed response splitting */
1472 Vstr_base *tmp = req->fname;
1473
1474 ASSERT(!vstr_srch_cstr_chrs_fwd(tmp, 1, tmp->len, HTTP_EOL));
1475 http_app_hdr_vstr(out, "Location",
1476 tmp, 1, tmp->len, VSTR_TYPE_ADD_ALL_BUF);
1477 }
1478
1479 if (req->user_return_error_code || use_cust_err_msg)
1480 http_app_hdrs_url(con, req);
1481 if (use_cust_err_msg)
1482 http_app_hdrs_file(con, req);
1483
1484 http_app_end_hdrs(out);
1485 }
1486
1487 if (!req->head_op)
1488 {
1489 Vstr_base *loc = req->fname;
1490
1491 switch (req->error_code)
1492 {
1493 case 301: vstr_add_fmt(out, out->len, CONF_MSG_FMT_301,
1494 CONF_MSG__FMT_301_BEG,
1495 loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1496 CONF_MSG__FMT_30x_END); break;
1497 case 302: vstr_add_fmt(out, out->len, CONF_MSG_FMT_302,
1498 CONF_MSG__FMT_302_BEG,
1499 loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1500 CONF_MSG__FMT_30x_END); break;
1501 case 303: vstr_add_fmt(out, out->len, CONF_MSG_FMT_303,
1502 CONF_MSG__FMT_303_BEG,
1503 loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1504 CONF_MSG__FMT_30x_END); break;
1505 case 307: vstr_add_fmt(out, out->len, CONF_MSG_FMT_307,
1506 CONF_MSG__FMT_307_BEG,
1507 loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
1508 CONF_MSG__FMT_30x_END); break;
1509 default:
1510 if (use_cust_err_msg)
1511 {
1512 if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
1513 req->f_mmap, 1, req->f_mmap->len))
1514 return (http_con_close_cleanup(con, req));
1515
1516 vlg_dbg3(vlg, "ERROR CUSTOM-404 REPLY:\n$<vstr.all:%p>\n", out);
1517
1518 if (out->conf->malloc_bad)
1519 return (http_con_close_cleanup(con, req));
1520
1521 return (http_fin_fd_req(con, req));
1522 }
1523
1524 /* default internal error message */
1525 assert(req->error_len < SIZE_MAX);
1526 vstr_add_ptr(out, out->len, req->error_msg, req->error_len);
1527 }
1528 }
1529
1530 vlg_dbg3(vlg, "ERROR REPLY:\n$<vstr.all:%p>\n", out);
1531
1532 if (out->conf->malloc_bad)
1533 return (http_con_cleanup(con, req));
1534
1535 return (http_fin_req(con, req));
1536 }
1537
http_fin_errmem_req(struct Con * con,struct Httpd_req_data * req)1538 static int http_fin_errmem_req(struct Con *con, struct Httpd_req_data *req)
1539 { /* try sending a 500 as the last msg */
1540 Vstr_base *out = con->evnt->io_w;
1541
1542 /* remove anything we can to free space */
1543 vstr_sc_reduce(out, 1, out->len, out->len - req->orig_io_w_len);
1544
1545 con->evnt->io_r->conf->malloc_bad = FALSE;
1546 con->evnt->io_w->conf->malloc_bad = FALSE;
1547 req->malloc_bad = TRUE;
1548
1549 HTTPD_ERR_RET(req, 500, http_fin_err_req(con, req));
1550 }
1551
http_fin_err_close_req(struct Con * con,Httpd_req_data * req)1552 static int http_fin_err_close_req(struct Con *con, Httpd_req_data *req)
1553 {
1554 httpd_fin_fd_close(con);
1555 return (http_fin_err_req(con, req));
1556 }
1557
httpd_sc_add_default_hostname(struct Con * con,Httpd_req_data * req,Vstr_base * lfn,size_t pos)1558 int httpd_sc_add_default_hostname(struct Con *con,
1559 Httpd_req_data *req,
1560 Vstr_base *lfn, size_t pos)
1561 {
1562 const Httpd_policy_opts *opts = req->policy;
1563 const Vstr_base *d_h = opts->default_hostname;
1564 Acpt_data *acpt_data = con->acpt_sa_ref->ptr;
1565 struct sockaddr_in *sinv4 = ACPT_SA_IN4(acpt_data);
1566 int ret = FALSE;
1567
1568 ret = vstr_add_vstr(lfn, pos, d_h, 1, d_h->len, VSTR_TYPE_ADD_DEF);
1569
1570 ASSERT(sinv4->sin_family == AF_INET);
1571
1572 if (ret && req->policy->add_def_port && (ntohs(sinv4->sin_port) != 80))
1573 ret = vstr_add_fmt(lfn, pos + d_h->len, ":%hu", ntohs(sinv4->sin_port));
1574
1575 return (ret);
1576 }
1577
httpd_sc_add_default_filename(Httpd_req_data * req,Vstr_base * fname)1578 static void httpd_sc_add_default_filename(Httpd_req_data *req, Vstr_base *fname)
1579 {
1580 if (vstr_export_chr(fname, fname->len) == '/')
1581 HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
1582 }
1583
1584 /* turn //foo//bar// into /foo/bar/ */
httpd_canon_path(Vstr_base * s1)1585 int httpd_canon_path(Vstr_base *s1)
1586 {
1587 size_t tmp = 0;
1588
1589 while ((tmp = vstr_srch_cstr_buf_fwd(s1, 1, s1->len, "//")))
1590 {
1591 if (!vstr_del(s1, tmp, 1))
1592 return (FALSE);
1593 }
1594
1595 return (TRUE);
1596 }
1597
httpd_canon_dir_path(Vstr_base * s1)1598 int httpd_canon_dir_path(Vstr_base *s1)
1599 {
1600 if (!httpd_canon_path(s1))
1601 return (FALSE);
1602
1603 if (s1->len && (vstr_export_chr(s1, s1->len) != '/'))
1604 return (vstr_add_cstr_ptr(s1, s1->len, "/"));
1605
1606 return (TRUE);
1607 }
1608
httpd_canon_abs_dir_path(Vstr_base * s1)1609 int httpd_canon_abs_dir_path(Vstr_base *s1)
1610 {
1611 if (!httpd_canon_dir_path(s1))
1612 return (FALSE);
1613
1614 if (s1->len && (vstr_export_chr(s1, 1) != '/'))
1615 return (vstr_add_cstr_ptr(s1, 0, "/"));
1616
1617 return (TRUE);
1618 }
1619
httpd_req_absolute_uri(struct Con * con,Httpd_req_data * req,Vstr_base * lfn,size_t pos,size_t len)1620 void httpd_req_absolute_uri(struct Con *con, Httpd_req_data *req,
1621 Vstr_base *lfn, size_t pos, size_t len)
1622 {
1623 Vstr_base *data = con->evnt->io_r;
1624 Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
1625 size_t apos = pos - 1;
1626 size_t alen = lfn->len;
1627 int has_schema = TRUE;
1628 int has_abs_path = TRUE;
1629 int has_data = TRUE;
1630 unsigned int prev_num = 0;
1631
1632 if (!VPREFIX(lfn, pos, len, "http://"))
1633 {
1634 has_schema = FALSE;
1635 if (!VPREFIX(lfn, pos, len, "/")) /* relative pathname */
1636 {
1637 if (VPREFIX(lfn, pos, len, "./"))
1638 {
1639 has_data = TRUE;
1640 vstr_del(lfn, pos, CLEN("./")); len -= CLEN("./");
1641 alen = lfn->len;
1642 }
1643 else
1644 {
1645 while (VPREFIX(lfn, pos, len, "../"))
1646 {
1647 ++prev_num;
1648 vstr_del(lfn, pos, CLEN("../")); len -= CLEN("../");
1649 }
1650 if (prev_num)
1651 alen = lfn->len;
1652 else
1653 has_data = !!lfn->len;
1654 }
1655
1656 has_abs_path = FALSE;
1657 }
1658 }
1659
1660 if (!has_schema)
1661 {
1662 vstr_add_cstr_buf(lfn, apos, "http://");
1663 apos += lfn->len - alen;
1664 alen = lfn->len;
1665 if (!h_h->len)
1666 httpd_sc_add_default_hostname(con, req, lfn, apos);
1667 else
1668 vstr_add_vstr(lfn, apos,
1669 data, h_h->pos, h_h->len, VSTR_TYPE_ADD_ALL_BUF);
1670 apos += lfn->len - alen;
1671 }
1672
1673 if (!has_abs_path)
1674 {
1675 size_t path_len = req->path_len;
1676
1677 if (has_data || prev_num)
1678 {
1679 path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
1680
1681 while (path_len && prev_num--)
1682 {
1683 path_len -= vstr_spn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
1684 path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
1685 }
1686 if (!path_len) path_len = 1; /* make sure there is a / at the end */
1687 }
1688
1689 vstr_add_vstr(lfn, apos, data, req->path_pos, path_len, VSTR_TYPE_ADD_DEF);
1690 }
1691 }
1692
1693 /* doing http://www.example.com/foo/bar where bar is a dir is bad
1694 because all relative links will be relative to foo, not bar.
1695 Also note that location must be "http://www.example.com/foo/bar/" or maybe
1696 "http:/foo/bar/" (but we don't use the later anymore)
1697 */
http_req_chk_dir(struct Con * con,Httpd_req_data * req)1698 static int http_req_chk_dir(struct Con *con, Httpd_req_data *req)
1699 {
1700 Vstr_base *fname = req->fname;
1701
1702 /* fname == what was just passed to open() */
1703 ASSERT(fname->len);
1704
1705 if (req->policy->use_secure_dirs && !req->conf_secure_dirs)
1706 { /* check if file exists before redirecting without leaking info. */
1707 const char *fname_cstr = NULL;
1708 struct stat64 d_stat[1];
1709
1710 vstr_add_cstr_buf(fname, fname->len, "/");
1711 HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
1712
1713 fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
1714 if (fname->conf->malloc_bad)
1715 return (http_fin_errmem_req(con, req));
1716
1717 if ((stat64(fname_cstr, d_stat) == -1) || !S_ISREG(d_stat->st_mode))
1718 HTTPD_ERR_RET(req, 404, http_fin_err_req(con, req));
1719 }
1720
1721 vstr_del(fname, 1, fname->len);
1722 httpd_req_absolute_uri(con, req, fname, 1, fname->len);
1723
1724 /* we got: http://foo/bar/
1725 * and so tried: http://foo/bar/index.html
1726 *
1727 * but foo/bar/index.html is a directory (fun), so redirect to:
1728 * http://foo/bar/index.html/
1729 */
1730 if (fname->len && (vstr_export_chr(fname, fname->len) == '/'))
1731 HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
1732
1733 vstr_add_cstr_buf(fname, fname->len, "/");
1734
1735 HTTPD_ERR_301(req);
1736
1737 if (fname->conf->malloc_bad)
1738 return (http_fin_errmem_req(con, req));
1739
1740 return (http_fin_err_req(con, req));
1741 }
1742
1743 /* doing http://www.example.com/foo/bar/ when url is really
1744 http://www.example.com/foo/bar is a very simple mistake, so we almost
1745 certainly don't want a 404 */
http_req_chk_file(struct Con * con,Httpd_req_data * req)1746 static int http_req_chk_file(struct Con *con, Httpd_req_data *req)
1747 {
1748 Vstr_base *fname = req->fname;
1749 size_t len = 0;
1750
1751 /* fname == what was just passed to open() */
1752 ASSERT(fname->len);
1753
1754 if (!req->policy->use_friendly_dirs)
1755 HTTPD_ERR_RET(req, 404, http_fin_err_req(con, req));
1756 else if (!req->conf_friendly_dirs)
1757 { /* check if file exists before redirecting without leaking info. */
1758 const char *fname_cstr = NULL;
1759 struct stat64 d_stat[1];
1760 len = vstr_cspn_cstr_chrs_rev(fname, 1, fname->len, "/") + 1;
1761
1762 /* must be a filename, can't be toplevel */
1763 if ((len <= 1) || (len >= (fname->len - req->vhost_prefix_len)))
1764 HTTPD_ERR_RET(req, 404, http_fin_err_req(con, req));
1765
1766 vstr_sc_reduce(fname, 1, fname->len, len);
1767
1768 fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
1769 if (fname->conf->malloc_bad)
1770 return (http_fin_errmem_req(con, req));
1771 if ((stat64(fname_cstr, d_stat) == -1) && !S_ISREG(d_stat->st_mode))
1772 HTTPD_ERR_RET(req, 404, http_fin_err_req(con, req));
1773 }
1774
1775 vstr_sub_cstr_ptr(fname, 1, fname->len, "./");
1776 httpd_req_absolute_uri(con, req, fname, 1, fname->len);
1777 assert(VSUFFIX(fname, 1, fname->len, "/"));
1778 vstr_sc_reduce(fname, 1, fname->len, strlen("/"));
1779
1780 HTTPD_ERR_301(req);
1781
1782 if (fname->conf->malloc_bad)
1783 return (http_fin_errmem_req(con, req));
1784
1785 return (http_fin_err_req(con, req));
1786 }
1787
http_req_split_method(struct Con * con,struct Httpd_req_data * req)1788 static void http_req_split_method(struct Con *con, struct Httpd_req_data *req)
1789 {
1790 Vstr_base *s1 = con->evnt->io_r;
1791 size_t pos = 1;
1792 size_t len = req->len;
1793 size_t el = 0;
1794 size_t skip_len = 0;
1795 unsigned int orig_num = req->sects->num;
1796
1797 el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL);
1798 ASSERT(el >= pos);
1799 len = el - pos; /* only parse the first line */
1800
1801 /* split request */
1802 if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
1803 return;
1804 vstr_sects_add(req->sects, pos, el - pos);
1805 len -= (el - pos); pos += (el - pos);
1806
1807 /* just skip whitespace on method call... */
1808 if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
1809 { len -= skip_len; pos += skip_len; }
1810
1811 if (!len)
1812 {
1813 req->sects->num = orig_num;
1814 return;
1815 }
1816
1817 if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
1818 {
1819 vstr_sects_add(req->sects, pos, len);
1820 len = 0;
1821 }
1822 else
1823 {
1824 vstr_sects_add(req->sects, pos, el - pos);
1825 len -= (el - pos); pos += (el - pos);
1826
1827 /* just skip whitespace on method call... */
1828 if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
1829 { len -= skip_len; pos += skip_len; }
1830 }
1831
1832 if (len)
1833 vstr_sects_add(req->sects, pos, len);
1834 else
1835 req->ver_0_9 = TRUE;
1836 }
1837
http_req_split_hdrs(struct Con * con,struct Httpd_req_data * req)1838 static void http_req_split_hdrs(struct Con *con, struct Httpd_req_data *req)
1839 {
1840 Vstr_base *s1 = con->evnt->io_r;
1841 size_t pos = 1;
1842 size_t len = req->len;
1843 size_t el = 0;
1844 size_t hpos = 0;
1845
1846 ASSERT(req->sects->num >= 3);
1847
1848 /* skip first line */
1849 el = (VSTR_SECTS_NUM(req->sects, req->sects->num)->pos +
1850 VSTR_SECTS_NUM(req->sects, req->sects->num)->len);
1851
1852 assert(VEQ(s1, el, CLEN(HTTP_EOL), HTTP_EOL));
1853 len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
1854
1855 if (VPREFIX(s1, pos, len, HTTP_EOL))
1856 return; /* end of headers */
1857
1858 ASSERT(vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_END_OF_REQUEST));
1859 /* split headers */
1860 hpos = pos;
1861 while ((el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL)) != pos)
1862 {
1863 char chr = 0;
1864
1865 len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
1866
1867 chr = vstr_export_chr(s1, pos);
1868 if (chr == ' ' || chr == '\t') /* header continues to next line */
1869 continue;
1870
1871 vstr_sects_add(req->sects, hpos, el - hpos);
1872
1873 hpos = pos;
1874 }
1875 }
1876
1877 /* return the length of a quoted string (must be >= 2), or 0 on syntax error */
http__len_quoted_string(const Vstr_base * data,size_t pos,size_t len)1878 static size_t http__len_quoted_string(const Vstr_base *data,
1879 size_t pos, size_t len)
1880 {
1881 size_t orig_pos = pos;
1882
1883 if (!VPREFIX(data, pos, len, "\""))
1884 return (0);
1885
1886 len -= 1; pos += 1;
1887
1888 while (TRUE)
1889 {
1890 size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\");
1891
1892 len -= tmp; pos += tmp;
1893 if (!len)
1894 return (0);
1895
1896 if (vstr_export_chr(data, pos) == '"')
1897 return (vstr_sc_posdiff(orig_pos, pos));
1898
1899 ASSERT(vstr_export_chr(data, pos) == '\\');
1900 if (len < 3) /* must be at least <\X"> */
1901 return (0);
1902 len -= 2; pos += 2;
1903 }
1904
1905 assert_ret(FALSE, 0);
1906 }
1907
1908 /* skip a quoted string, or fail on syntax error */
http__skip_quoted_string(const Vstr_base * data,size_t * pos,size_t * len)1909 static int http__skip_quoted_string(const Vstr_base *data,
1910 size_t *pos, size_t *len)
1911 {
1912 size_t qlen = http__len_quoted_string(data, *pos, *len);
1913
1914 assert(VPREFIX(data, *pos, *len, "\""));
1915
1916 if (!qlen)
1917 return (FALSE);
1918
1919 *len -= qlen; *pos += qlen;
1920
1921 HTTP_SKIP_LWS(data, *pos, *len);
1922 return (TRUE);
1923 }
1924
1925 /* match non-week entity tags in both strings, return true if any match
1926 * only allow non-weak entity tags if allow_weak = FALSE */
httpd_match_etags(struct Httpd_req_data * req,const Vstr_base * hdr,size_t hpos,size_t hlen,const Vstr_base * vs1,size_t epos,size_t elen,int allow_weak)1927 static int httpd_match_etags(struct Httpd_req_data *req,
1928 const Vstr_base *hdr, size_t hpos, size_t hlen,
1929 const Vstr_base *vs1, size_t epos, size_t elen,
1930 int allow_weak)
1931 {
1932 int need_comma = FALSE;
1933
1934 ASSERT(hdr);
1935
1936 if (!vs1)
1937 return (FALSE);
1938
1939 while (hlen)
1940 {
1941 int weak = FALSE;
1942 size_t htlen = 0;
1943
1944 if (vstr_export_chr(hdr, hpos) == ',')
1945 {
1946 hlen -= 1; hpos += 1;
1947 HTTP_SKIP_LWS(hdr, hpos, hlen);
1948 need_comma = FALSE;
1949 continue;
1950 }
1951 else if (need_comma)
1952 return (FALSE);
1953
1954 if (VPREFIX(hdr, hpos, hlen, "W/"))
1955 {
1956 weak = TRUE;
1957 hlen -= CLEN("W/"); hpos += CLEN("W/");
1958 }
1959 if (!(htlen = http__len_quoted_string(hdr, hpos, hlen)))
1960 return (FALSE);
1961
1962 if (allow_weak || !weak)
1963 {
1964 size_t orig_epos = epos;
1965 size_t orig_elen = elen;
1966 unsigned int num = 0;
1967
1968 while (elen)
1969 {
1970 size_t etlen = 0;
1971
1972 if (vstr_export_chr(vs1, epos) == ',')
1973 {
1974 elen -= 1; epos += 1;
1975 HTTP_SKIP_LWS(vs1, epos, elen);
1976 need_comma = FALSE;
1977 continue;
1978 }
1979 else if (need_comma)
1980 return (FALSE);
1981
1982 ++num;
1983
1984 if (!VPREFIX(vs1, epos, elen, "W/"))
1985 weak = FALSE;
1986 else
1987 {
1988 weak = TRUE;
1989 elen -= CLEN("W/"); epos += CLEN("W/");
1990 }
1991 if (!(etlen = http__len_quoted_string(vs1, epos, elen)))
1992 return (FALSE);
1993
1994 if ((allow_weak || !weak) &&
1995 vstr_cmp_eq(hdr, hpos, htlen, vs1, epos, etlen))
1996 return (TRUE);
1997
1998 elen -= etlen; epos += etlen;
1999 HTTP_SKIP_LWS(vs1, epos, elen);
2000 need_comma = TRUE;
2001
2002 if (req->policy->max_etag_nodes && (num >= req->policy->max_etag_nodes))
2003 return (FALSE);
2004 }
2005
2006 epos = orig_epos;
2007 elen = orig_elen;
2008 }
2009
2010 hlen -= htlen; hpos += htlen;
2011 HTTP_SKIP_LWS(hdr, hpos, hlen);
2012 need_comma = TRUE;
2013 }
2014
2015 return (FALSE);
2016 }
2017
2018 #define HTTPD__HD_EQ(x) \
2019 VEQ(hdrs, h_ ## x ->pos, h_ ## x ->len, date)
2020
2021 /* gets here if the GET/HEAD response is ok, we test for caching etc. using the
2022 * if-* headers */
2023 /* FALSE = 412 Precondition Failed */
http_response_ok(struct Con * con,struct Httpd_req_data * req,unsigned int * http_ret_code,const char ** http_ret_line)2024 static int http_response_ok(struct Con *con, struct Httpd_req_data *req,
2025 unsigned int *http_ret_code,
2026 const char ** http_ret_line)
2027 {
2028 const Vstr_base *hdrs = con->evnt->io_r;
2029 time_t mtime = req->f_stat->st_mtime;
2030 Vstr_sect_node *h_ims = req->http_hdrs->hdr_if_modified_since;
2031 Vstr_sect_node *h_ir = req->http_hdrs->hdr_if_range;
2032 Vstr_sect_node *h_iums = req->http_hdrs->hdr_if_unmodified_since;
2033 Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
2034 Vstr_base *comb = req->http_hdrs->multi->comb;
2035 Vstr_sect_node *h_im = req->http_hdrs->multi->hdr_if_match;
2036 Vstr_sect_node *h_inm = req->http_hdrs->multi->hdr_if_none_match;
2037 int h_ir_tst = FALSE;
2038 int h_iums_tst = FALSE;
2039 int req_if_range = FALSE;
2040 int cached_output = FALSE;
2041 const char *date = NULL;
2042
2043 if (req->ver_1_1 && h_iums->pos)
2044 h_iums_tst = TRUE;
2045
2046 if (req->ver_1_1 && h_ir->pos && h_r->pos)
2047 h_ir_tst = TRUE;
2048
2049 /* assumes time doesn't go backwards ... From rfc2616:
2050 *
2051 Note: When handling an If-Modified-Since header field, some
2052 servers will use an exact date comparison function, rather than a
2053 less-than function, for deciding whether to send a 304 (Not
2054 Modified) response. To get best results when sending an If-
2055 Modified-Since header field for cache validation, clients are
2056 advised to use the exact date string received in a previous Last-
2057 Modified header field whenever possible.
2058 */
2059 if (difftime(req->now, mtime) > 0)
2060 { /* if mtime in future, or now ... don't allow checking */
2061 Date_store *ds = httpd_opts->date;
2062
2063 date = date_rfc1123(ds, mtime);
2064 if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
2065 cached_output = TRUE;
2066 if (h_iums_tst && HTTPD__HD_EQ(iums))
2067 return (FALSE);
2068 if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
2069 req_if_range = TRUE;
2070
2071 date = date_rfc850(ds, mtime);
2072 if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
2073 cached_output = TRUE;
2074 if (h_iums_tst && HTTPD__HD_EQ(iums))
2075 return (FALSE);
2076 if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
2077 req_if_range = TRUE;
2078
2079 date = date_asctime(ds, mtime);
2080 if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
2081 cached_output = TRUE;
2082 if (h_iums_tst && HTTPD__HD_EQ(iums))
2083 return (FALSE);
2084 if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
2085 req_if_range = TRUE;
2086 }
2087
2088 if (req->ver_1_1)
2089 {
2090 const Vstr_base *vs1 = NULL;
2091 size_t pos = 0;
2092 size_t len = 0;
2093
2094 if (req->content_encoding_bzip2) /* point to any entity tags we have */
2095 {
2096 if (req->bzip2_etag_vs1 && (req->etag_time > mtime))
2097 {
2098 vs1 = req->bzip2_etag_vs1;
2099 pos = req->bzip2_etag_pos;
2100 len = req->bzip2_etag_len;
2101 }
2102 }
2103 else if (req->content_encoding_gzip)
2104 {
2105 if (req->gzip_etag_vs1 && (req->etag_time > mtime))
2106 {
2107 vs1 = req->gzip_etag_vs1;
2108 pos = req->gzip_etag_pos;
2109 len = req->gzip_etag_len;
2110 }
2111 }
2112 else if (req->etag_vs1 && (req->etag_time > mtime))
2113 {
2114 vs1 = req->etag_vs1;
2115 pos = req->etag_pos;
2116 len = req->etag_len;
2117 }
2118
2119 if (h_ir_tst && !req_if_range &&
2120 httpd_match_etags(req,
2121 hdrs, h_ir->pos, h_ir->len, vs1, pos, len, FALSE))
2122 req_if_range = TRUE;
2123
2124 if (h_ir_tst && !req_if_range)
2125 h_r->pos = 0;
2126
2127 /* #13.3.3 says don't trust weak for "complex" queries, ie. byteranges */
2128 if (h_inm->pos && (VEQ(hdrs, h_inm->pos, h_inm->len, "*") ||
2129 httpd_match_etags(req, comb, h_inm->pos, h_inm->len,
2130 vs1, pos, len, !h_r->pos)))
2131 cached_output = TRUE;
2132
2133 if (h_im->pos && !(VEQ(hdrs, h_im->pos, h_im->len, "*") ||
2134 httpd_match_etags(req, comb, h_im->pos, h_im->len,
2135 vs1, pos, len, !h_r->pos)))
2136 return (FALSE);
2137 }
2138 else if (h_ir_tst && !req_if_range)
2139 h_r->pos = 0;
2140
2141 if (cached_output)
2142 {
2143 req->head_op = TRUE;
2144 *http_ret_code = 304;
2145 *http_ret_line = "Not Modified";
2146 }
2147 else if (h_r->pos)
2148 {
2149 *http_ret_code = 206;
2150 *http_ret_line = "Partial content";
2151 }
2152
2153 return (TRUE);
2154 }
2155
http__multi_hdr_fixup(Vstr_sect_node * hdr_ignore,Vstr_sect_node * hdr,size_t pos,size_t len)2156 static void http__multi_hdr_fixup(Vstr_sect_node *hdr_ignore,
2157 Vstr_sect_node *hdr, size_t pos, size_t len)
2158 {
2159 if (hdr == hdr_ignore)
2160 return;
2161
2162 if (hdr->pos <= pos)
2163 return;
2164
2165 hdr->pos += len;
2166 }
2167
http__multi_hdr_cp(Vstr_base * comb,Vstr_base * data,Vstr_sect_node * hdr)2168 static int http__multi_hdr_cp(Vstr_base *comb,
2169 Vstr_base *data, Vstr_sect_node *hdr)
2170 {
2171 size_t pos = comb->len + 1;
2172
2173 if (!hdr->len)
2174 return (TRUE);
2175
2176 if (!vstr_add_vstr(comb, comb->len,
2177 data, hdr->pos, hdr->len, VSTR_TYPE_ADD_BUF_PTR))
2178 return (FALSE);
2179
2180 hdr->pos = pos;
2181
2182 return (TRUE);
2183 }
2184
http__app_multi_hdr(Vstr_base * data,struct Http_hdrs * hdrs,Vstr_sect_node * hdr,size_t pos,size_t len)2185 static int http__app_multi_hdr(Vstr_base *data, struct Http_hdrs *hdrs,
2186 Vstr_sect_node *hdr, size_t pos, size_t len)
2187 {
2188 Vstr_base *comb = hdrs->multi->comb;
2189
2190 ASSERT(comb);
2191
2192 ASSERT((hdr == hdrs->multi->hdr_accept) ||
2193 (hdr == hdrs->multi->hdr_accept_charset) ||
2194 (hdr == hdrs->multi->hdr_accept_encoding) ||
2195 (hdr == hdrs->multi->hdr_accept_language) ||
2196 (hdr == hdrs->multi->hdr_connection) ||
2197 (hdr == hdrs->multi->hdr_if_match) ||
2198 (hdr == hdrs->multi->hdr_if_none_match) ||
2199 (hdr == hdrs->multi->hdr_range));
2200
2201 ASSERT((comb == data) || (comb == hdrs->multi->combiner_store));
2202
2203 if ((data == comb) && !hdr->pos)
2204 { /* Do the fast thing... */
2205 hdr->pos = pos;
2206 hdr->len = len;
2207 return (TRUE);
2208 }
2209
2210 if (data == comb)
2211 { /* OK, so we have a crap request and need to JOIN multiple headers... */
2212 comb = hdrs->multi->comb = hdrs->multi->combiner_store;
2213
2214 if (!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept) ||
2215 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_charset) ||
2216 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_encoding) ||
2217 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_language) ||
2218 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_connection) ||
2219 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_match) ||
2220 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_none_match) ||
2221 !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_range) ||
2222 FALSE)
2223 return (FALSE);
2224 }
2225
2226 if (!hdr->pos)
2227 {
2228 hdr->pos = comb->len + 1;
2229 hdr->len = len;
2230 return (vstr_add_vstr(comb, comb->len,
2231 data, pos, len, VSTR_TYPE_ADD_BUF_PTR));
2232 }
2233
2234 /* reverses the order, but that doesn't matter */
2235 if (!vstr_add_cstr_ptr(comb, hdr->pos - 1, ","))
2236 return (FALSE);
2237 if (!vstr_add_vstr(comb, hdr->pos - 1,
2238 data, pos, len, VSTR_TYPE_ADD_BUF_PTR))
2239 return (FALSE);
2240 hdr->len += ++len;
2241
2242 /* now need to "move" any hdrs after this one */
2243 pos = hdr->pos - 1;
2244 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept, pos, len);
2245 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_charset, pos, len);
2246 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_encoding, pos, len);
2247 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_language, pos, len);
2248 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_connection, pos, len);
2249 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_match, pos, len);
2250 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_none_match, pos, len);
2251 http__multi_hdr_fixup(hdr, hdrs->multi->hdr_range, pos, len);
2252
2253 return (TRUE);
2254 }
2255
2256 /* viprefix, with local knowledge */
http__hdr_eq(struct Con * con,size_t pos,size_t len,const char * hdr,size_t hdr_len,size_t * hdr_val_pos)2257 static int http__hdr_eq(struct Con *con, size_t pos, size_t len,
2258 const char *hdr, size_t hdr_len, size_t *hdr_val_pos)
2259 {
2260 Vstr_base *data = con->evnt->io_r;
2261
2262 ASSERT(CLEN(hdr) == hdr_len);
2263 ASSERT(hdr[hdr_len - 1] == ':');
2264
2265 if (!con->policy->use_non_spc_hdrs)
2266 --hdr_len;
2267
2268 if ((len < hdr_len) ||
2269 !vstr_cmp_case_buf_eq(data, pos, hdr_len, hdr, hdr_len))
2270 return (FALSE);
2271 len -= hdr_len; pos += hdr_len;
2272
2273 if (!con->policy->use_non_spc_hdrs)
2274 {
2275 HTTP_SKIP_LWS(data, pos, len);
2276 if (!len)
2277 return (FALSE);
2278
2279 if (vstr_export_chr(data, pos) != ':')
2280 return (FALSE);
2281 --len; ++pos;
2282 }
2283
2284 *hdr_val_pos = pos;
2285
2286 return (TRUE);
2287 }
2288
2289 /* remove LWS from front and end... what a craptastic std. */
http__hdr_fixup(Vstr_base * data,size_t * pos,size_t * len,size_t hdr_val_pos)2290 static void http__hdr_fixup(Vstr_base *data, size_t *pos, size_t *len,
2291 size_t hdr_val_pos)
2292 {
2293 size_t tmp = 0;
2294
2295 *len -= hdr_val_pos - *pos; *pos += hdr_val_pos - *pos;
2296 HTTP_SKIP_LWS(data, *pos, *len);
2297
2298 /* hand coding for a HTTP_SKIP_LWS() going backwards... */
2299 while ((tmp = vstr_spn_cstr_chrs_rev(data, *pos, *len, HTTP_LWS)))
2300 {
2301 *len -= tmp;
2302
2303 if (VSUFFIX(data, *pos, *len, HTTP_EOL))
2304 *len -= CLEN(HTTP_EOL);
2305 }
2306 }
2307
2308 /* for single headers, multiple ones aren't allowed ...
2309 * we can do last one wins, or just error (erroring is more secure, see
2310 * HTTP Request smuggling) */
2311 #define HDR__EQ(x) http__hdr_eq(con, pos, len, x ":", CLEN(x ":"), &hdr_val_pos)
2312 #define HDR__SET(h) do { \
2313 if (req->policy->use_x2_hdr_chk && http_hdrs-> hdr_ ## h ->pos) \
2314 HTTPD_ERR_RET(req, 400, FALSE); \
2315 http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
2316 http_hdrs-> hdr_ ## h ->pos = pos; \
2317 http_hdrs-> hdr_ ## h ->len = len; \
2318 } while (FALSE)
2319 #define HDR__MULTI_SET(h) do { \
2320 http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
2321 if (!http__app_multi_hdr(data, http_hdrs, \
2322 http_hdrs->multi-> hdr_ ## h, pos, len)) \
2323 { \
2324 req->malloc_bad = TRUE; \
2325 HTTPD_ERR_RET(req, 500, FALSE); \
2326 } \
2327 } while (FALSE)
2328
http__parse_hdrs(struct Con * con,Httpd_req_data * req)2329 static int http__parse_hdrs(struct Con *con, Httpd_req_data *req)
2330 {
2331 Vstr_base *data = con->evnt->io_r;
2332 struct Http_hdrs *http_hdrs = req->http_hdrs;
2333 unsigned int num = 3; /* skip "method URI version" */
2334 int got_content_length = FALSE;
2335 int got_transfer_encoding = FALSE;
2336
2337 while (++num <= req->sects->num)
2338 {
2339 size_t pos = VSTR_SECTS_NUM(req->sects, num)->pos;
2340 size_t len = VSTR_SECTS_NUM(req->sects, num)->len;
2341 size_t hdr_val_pos = 0;
2342
2343 if (0) { /* nothing */ }
2344 /* nothing headers ... use for logging only */
2345 else if (HDR__EQ("User-Agent")) HDR__SET(ua);
2346 else if (HDR__EQ("Referer")) HDR__SET(referer);
2347
2348 else if (HDR__EQ("Expect")) HDR__SET(expect);
2349 else if (HDR__EQ("Host")) HDR__SET(host);
2350 else if (HDR__EQ("If-Modified-Since")) HDR__SET(if_modified_since);
2351 else if (HDR__EQ("If-Range")) HDR__SET(if_range);
2352 else if (HDR__EQ("If-Unmodified-Since")) HDR__SET(if_unmodified_since);
2353 else if (HDR__EQ("Authorization")) HDR__SET(authorization);
2354
2355 /* allow continuations over multiple headers... *sigh* */
2356 else if (HDR__EQ("Accept")) HDR__MULTI_SET(accept);
2357 else if (HDR__EQ("Accept-Charset")) HDR__MULTI_SET(accept_charset);
2358 else if (HDR__EQ("Accept-Encoding")) HDR__MULTI_SET(accept_encoding);
2359 else if (HDR__EQ("Accept-Language")) HDR__MULTI_SET(accept_language);
2360 else if (HDR__EQ("Connection")) HDR__MULTI_SET(connection);
2361 else if (HDR__EQ("If-Match")) HDR__MULTI_SET(if_match);
2362 else if (HDR__EQ("If-None-Match")) HDR__MULTI_SET(if_none_match);
2363 else if (HDR__EQ("Range")) HDR__MULTI_SET(range);
2364
2365 /* allow a 0 (zero) length content-length, some clients do send these */
2366 else if (HDR__EQ("Content-Length"))
2367 {
2368 unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
2369 VSTR_FLAG_PARSE_NUM_OVERFLOW);
2370 size_t num_len = 0;
2371
2372 if (req->policy->use_x2_hdr_chk && got_content_length)
2373 HTTPD_ERR_RET(req, 400, FALSE);
2374 got_content_length = TRUE;
2375 http__hdr_fixup(data, &pos, &len, hdr_val_pos);
2376
2377 if (vstr_parse_uint(data, pos, len, num_flags, &num_len, NULL))
2378 HTTPD_ERR_RET(req, 413, FALSE);
2379 if (num_len != len)
2380 HTTPD_ERR_RET(req, 400, FALSE);
2381 }
2382
2383 /* in theory ,,identity;foo=bar;baz="zoom",, is ok ... who cares */
2384 else if (HDR__EQ("Transfer-Encoding"))
2385 {
2386 if (req->policy->use_x2_hdr_chk && got_transfer_encoding)
2387 HTTPD_ERR_RET(req, 400, FALSE);
2388 got_transfer_encoding = TRUE;
2389 http__hdr_fixup(data, &pos, &len, hdr_val_pos);
2390
2391 if (!VEQ(data, pos, len, "identity")) /* 3.6 says 501 */
2392 HTTPD_ERR_RET(req, 501, FALSE);
2393 }
2394 else
2395 {
2396 size_t tmp = 0;
2397
2398 /* all headers _must_ contain a ':' */
2399 tmp = vstr_srch_chr_fwd(data, pos, len, ':');
2400 if (!tmp)
2401 HTTPD_ERR_RET(req, 400, FALSE);
2402
2403 /* make sure unknown header is whitespace "valid" */
2404 tmp = vstr_sc_posdiff(pos, tmp);
2405 if (req->policy->use_non_spc_hdrs &&
2406 (vstr_cspn_cstr_chrs_fwd(data, pos, tmp, HTTP_LWS) != tmp))
2407 HTTPD_ERR_RET(req, 400, FALSE);
2408 }
2409 }
2410
2411 return (TRUE);
2412 }
2413 #undef HDR__EQ
2414 #undef HDR__SET
2415 #undef HDR__MUTLI_SET
2416
2417 /* might have been able to do it with string matches, but getting...
2418 * "HTTP/1.1" = OK
2419 * "HTTP/1.10" = OK
2420 * "HTTP/1.10000000000000" = BAD
2421 * ...seemed not as easy. It also seems like you have to accept...
2422 * "HTTP / 01 . 01" as "HTTP/1.1"
2423 */
http_req_parse_version(struct Con * con,struct Httpd_req_data * req)2424 static int http_req_parse_version(struct Con *con, struct Httpd_req_data *req)
2425 {
2426 Vstr_base *data = con->evnt->io_r;
2427 size_t op_pos = VSTR_SECTS_NUM(req->sects, 3)->pos;
2428 size_t op_len = VSTR_SECTS_NUM(req->sects, 3)->len;
2429 unsigned int major = 0;
2430 unsigned int minor = 0;
2431 size_t num_len = 0;
2432 unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
2433 VSTR_FLAG_PARSE_NUM_OVERFLOW);
2434
2435 if (!VPREFIX(data, op_pos, op_len, "HTTP"))
2436 HTTPD_ERR_RET(req, 400, FALSE);
2437
2438 op_len -= CLEN("HTTP"); op_pos += CLEN("HTTP");
2439 HTTP_SKIP_LWS(data, op_pos, op_len);
2440
2441 if (!VPREFIX(data, op_pos, op_len, "/"))
2442 HTTPD_ERR_RET(req, 400, FALSE);
2443 op_len -= CLEN("/"); op_pos += CLEN("/");
2444 HTTP_SKIP_LWS(data, op_pos, op_len);
2445
2446 major = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
2447 op_len -= num_len; op_pos += num_len;
2448 HTTP_SKIP_LWS(data, op_pos, op_len);
2449
2450 if (!num_len || !VPREFIX(data, op_pos, op_len, "."))
2451 HTTPD_ERR_RET(req, 400, FALSE);
2452
2453 op_len -= CLEN("."); op_pos += CLEN(".");
2454 HTTP_SKIP_LWS(data, op_pos, op_len);
2455
2456 minor = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
2457 op_len -= num_len; op_pos += num_len;
2458 HTTP_SKIP_LWS(data, op_pos, op_len);
2459
2460 if (!num_len || op_len)
2461 HTTPD_ERR_RET(req, 400, FALSE);
2462
2463 if (0) { } /* not allowing HTTP/0.9 here */
2464 else if ((major == 1) && (minor >= 1))
2465 req->ver_1_1 = TRUE;
2466 else if ((major == 1) && (minor == 0))
2467 { /* do nothing */ }
2468 else
2469 HTTPD_ERR_RET(req, 505, FALSE);
2470
2471 return (TRUE);
2472 }
2473
2474 #define HDR__CON_1_0_FIXUP(name, h) \
2475 else if (VIEQ(data, pos, tmp, name)) \
2476 do { \
2477 req -> http_hdrs -> hdr_ ## h ->pos = 0; \
2478 req -> http_hdrs -> hdr_ ## h ->len = 0; \
2479 } while (FALSE)
2480 #define HDR__CON_1_0_MULTI_FIXUP(name, h) \
2481 else if (VIEQ(data, pos, tmp, name)) \
2482 do { \
2483 req -> http_hdrs -> multi -> hdr_ ## h ->pos = 0; \
2484 req -> http_hdrs -> multi -> hdr_ ## h ->len = 0; \
2485 } while (FALSE)
http__parse_connection(struct Con * con,struct Httpd_req_data * req)2486 static void http__parse_connection(struct Con *con, struct Httpd_req_data *req)
2487 {
2488 Vstr_base *data = req->http_hdrs->multi->comb;
2489 size_t pos = 0;
2490 size_t len = 0;
2491 unsigned int num = 0;
2492
2493 pos = req->http_hdrs->multi->hdr_connection->pos;
2494 len = req->http_hdrs->multi->hdr_connection->len;
2495
2496 if (req->ver_1_1)
2497 con->keep_alive = HTTP_1_1_KEEP_ALIVE;
2498
2499 if (!len)
2500 return;
2501
2502 while (len)
2503 {
2504 size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
2505 HTTP_EOL HTTP_LWS ",");
2506
2507 ++num;
2508 if (req->ver_1_1)
2509 { /* this is all we have to do for HTTP/1.1 ... proxies understand it */
2510 if (VIEQ(data, pos, tmp, "close"))
2511 con->keep_alive = HTTP_NON_KEEP_ALIVE;
2512 }
2513 else if (VIEQ(data, pos, tmp, "keep-alive"))
2514 {
2515 if (req->policy->use_keep_alive_1_0)
2516 con->keep_alive = HTTP_1_0_KEEP_ALIVE;
2517 }
2518 /* now fixup connection headers for HTTP/1.0 proxies */
2519 HDR__CON_1_0_FIXUP("User-Agent", ua);
2520 HDR__CON_1_0_FIXUP("Referer", referer);
2521 HDR__CON_1_0_FIXUP("Expect", expect);
2522 HDR__CON_1_0_FIXUP("Host", host);
2523 HDR__CON_1_0_FIXUP("If-Modified-Since", if_modified_since);
2524 HDR__CON_1_0_FIXUP("If-Range", if_range);
2525 HDR__CON_1_0_FIXUP("If-Unmodified-Since", if_unmodified_since);
2526 HDR__CON_1_0_FIXUP("Authorization", authorization);
2527
2528 HDR__CON_1_0_MULTI_FIXUP("Accept", accept);
2529 HDR__CON_1_0_MULTI_FIXUP("Accept-Charset", accept_charset);
2530 HDR__CON_1_0_MULTI_FIXUP("Accept-Encoding", accept_encoding);
2531 HDR__CON_1_0_MULTI_FIXUP("Accept-Language", accept_language);
2532 HDR__CON_1_0_MULTI_FIXUP("If-Match", if_match);
2533 HDR__CON_1_0_MULTI_FIXUP("If-None-Match", if_none_match);
2534 HDR__CON_1_0_MULTI_FIXUP("Range", range);
2535
2536 /* skip to end, or after next ',' */
2537 tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, ",");
2538 len -= tmp; pos += tmp;
2539 if (!len)
2540 break;
2541
2542 assert(VPREFIX(data, pos, len, ","));
2543 len -= 1; pos += 1;
2544 HTTP_SKIP_LWS(data, pos, len);
2545
2546 if (req->policy->max_connection_nodes &&
2547 (num >= req->policy->max_connection_nodes))
2548 return;
2549 }
2550 }
2551 #undef HDR__CON_1_0_FIXUP
2552 #undef HDR__CON_1_0_MULTI_FIXUP
2553
2554 /* parse >= 1.0 things like, version and headers */
http__parse_1_x(struct Con * con,struct Httpd_req_data * req)2555 static int http__parse_1_x(struct Con *con, struct Httpd_req_data *req)
2556 {
2557 ASSERT(!req->ver_0_9);
2558
2559 if (!http_req_parse_version(con, req))
2560 return (FALSE);
2561
2562 if (!http__parse_hdrs(con, req))
2563 return (FALSE);
2564
2565 if (req->policy->max_requests &&
2566 (req->policy->max_requests <= con->evnt->acct.req_got) && req->ver_1_1)
2567 return (TRUE);
2568
2569 if (!req->policy->use_keep_alive && req->ver_1_1)
2570 return (TRUE);
2571
2572 http__parse_connection(con, req);
2573
2574 if (req->policy->max_requests &&
2575 (req->policy->max_requests <= con->evnt->acct.req_got))
2576 con->keep_alive = HTTP_NON_KEEP_ALIVE;
2577
2578 return (TRUE);
2579 }
2580
2581 /* because we only parse for a combined CRLF, and some proxies/clients parse for
2582 * either ... make sure we don't have embedded singles which could cause
2583 * response splitting */
http__chk_single_crlf(Vstr_base * data,size_t pos,size_t len)2584 static int http__chk_single_crlf(Vstr_base *data, size_t pos, size_t len)
2585 {
2586 if (vstr_srch_chr_fwd(data, pos, len, '\r') ||
2587 vstr_srch_chr_fwd(data, pos, len, '\n'))
2588 return (TRUE);
2589
2590 return (FALSE);
2591 }
2592
2593 /* convert a http://abcd/foo into /foo with host=abcd ...
2594 * also do sanity checking on the URI and host for valid characters */
http_parse_host(struct Con * con,struct Httpd_req_data * req)2595 static int http_parse_host(struct Con *con, struct Httpd_req_data *req)
2596 {
2597 Vstr_base *data = con->evnt->io_r;
2598 size_t op_pos = req->path_pos;
2599 size_t op_len = req->path_len;
2600
2601 /* check for absolute URIs */
2602 if (VIPREFIX(data, op_pos, op_len, "http://"))
2603 { /* ok, be forward compatible */
2604 size_t tmp = CLEN("http://");
2605
2606 op_len -= tmp; op_pos += tmp;
2607 tmp = vstr_srch_chr_fwd(data, op_pos, op_len, '/');
2608 if (!tmp)
2609 {
2610 HTTP__HDR_SET(req, host, op_pos, op_len);
2611 op_len = 1;
2612 --op_pos;
2613 }
2614 else
2615 { /* found end of host ... */
2616 size_t host_len = tmp - op_pos;
2617
2618 HTTP__HDR_SET(req, host, op_pos, host_len);
2619 op_len -= host_len; op_pos += host_len;
2620 }
2621 assert(VPREFIX(data, op_pos, op_len, "/"));
2622 }
2623
2624 /* HTTP/1.1 requires a host -- allow blank hostnames */
2625 if (req->ver_1_1 && !req->http_hdrs->hdr_host->pos)
2626 return (FALSE);
2627
2628 if (req->http_hdrs->hdr_host->len)
2629 { /* check host looks valid ... header must exist, but can be empty */
2630 size_t pos = req->http_hdrs->hdr_host->pos;
2631 size_t len = req->http_hdrs->hdr_host->len;
2632 size_t tmp = 0;
2633
2634 /* leaving out most checks for ".." or invalid chars in hostnames etc.
2635 as the default filename checks should catch them
2636 */
2637
2638 /* Check for Host header with extra / ...
2639 * Ie. only allow a single directory name.
2640 * We could just leave this (it's not a security check, /../ is checked
2641 * for at filepath time), but I feel like being anal and this way there
2642 * aren't multiple urls to a single path. */
2643 if (vstr_srch_chr_fwd(data, pos, len, '/'))
2644 return (FALSE);
2645
2646 if (http__chk_single_crlf(data, pos, len))
2647 return (FALSE);
2648
2649 if ((tmp = vstr_srch_chr_fwd(data, pos, len, ':')))
2650 { /* NOTE: not sure if we have to 400 if the port doesn't match
2651 * or if it's an "invalid" port number (Ie. == 0 || > 65535) */
2652 len -= tmp - pos; pos = tmp;
2653
2654 /* if it's port 80, pretend it's not there */
2655 if (VEQ(data, pos, len, ":80") || VEQ(data, pos, len, ":"))
2656 req->http_hdrs->hdr_host->len -= len;
2657 else
2658 {
2659 len -= 1; pos += 1; /* skip the ':' */
2660 if (vstr_spn_cstr_chrs_fwd(data, pos, len, "0123456789") != len)
2661 return (FALSE);
2662 }
2663 }
2664 }
2665
2666 if (http__chk_single_crlf(data, op_pos, op_len))
2667 return (FALSE);
2668
2669 /* uri#fragment ... craptastic clients pass this and assume it is ignored */
2670 if (req->policy->remove_url_frag)
2671 op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "#");
2672 /* uri?foo ... This is "ok" to pass, however if you move dynamic
2673 * resources to static ones you need to do this */
2674 if (req->policy->remove_url_query)
2675 op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "?");
2676
2677 req->path_pos = op_pos;
2678 req->path_len = op_len;
2679
2680 return (TRUE);
2681 }
2682
http__parse_skip_blanks(Vstr_base * data,size_t * passed_pos,size_t * passed_len)2683 static void http__parse_skip_blanks(Vstr_base *data,
2684 size_t *passed_pos, size_t *passed_len)
2685 {
2686 size_t pos = *passed_pos;
2687 size_t len = *passed_len;
2688
2689 HTTP_SKIP_LWS(data, pos, len);
2690 while (VPREFIX(data, pos, len, ",")) /* http crack */
2691 {
2692 len -= CLEN(","); pos += CLEN(",");
2693 HTTP_SKIP_LWS(data, pos, len);
2694 }
2695
2696 *passed_pos = pos;
2697 *passed_len = len;
2698 }
2699
httpd__file_sect_add(struct Con * con,Httpd_req_data * req,VSTR_AUTOCONF_uintmax_t range_beg,VSTR_AUTOCONF_uintmax_t range_end,size_t len)2700 static int httpd__file_sect_add(struct Con *con, Httpd_req_data *req,
2701 VSTR_AUTOCONF_uintmax_t range_beg,
2702 VSTR_AUTOCONF_uintmax_t range_end, size_t len)
2703 {
2704 struct File_sect *fs = NULL;
2705
2706 ASSERT(con->fs && (con->fs_sz >= 1));
2707 if (!con->fs_num)
2708 {
2709 ASSERT((con->fs == con->fs_store) || (con->fs_sz > 1));
2710 ASSERT(!con->use_mpbr);
2711
2712 goto file_sect_add;
2713 }
2714
2715 con->use_mpbr = TRUE;
2716
2717 if (con->fs == con->fs_store)
2718 {
2719 ASSERT(con->fs_num == 1);
2720
2721 if (!(con->mpbr_ct = vstr_make_base(con->evnt->io_w->conf)))
2722 return (FALSE);
2723
2724 if (!(fs = MK(sizeof(struct File_sect) * 16)))
2725 return (FALSE);
2726 con->fs = fs;
2727 con->fs_sz = 16;
2728
2729 con->fs->fd = con->fs_store->fd;
2730 con->fs->off = con->fs_store->off;
2731 con->fs->len = con->fs_store->len;
2732 ++fs;
2733 }
2734 else if (con->fs_num >= con->fs_sz)
2735 {
2736 unsigned int num = (con->fs_sz << 1) + 1;
2737
2738 ASSERT(con->fs_num == con->fs_sz);
2739
2740 if (!MV(con->fs, fs, sizeof(struct File_sect) * num))
2741 return (FALSE);
2742 con->fs_sz = num;
2743 }
2744
2745 file_sect_add:
2746 fs = con->fs + con->fs_num++;
2747
2748 fs->fd = con->fs->fd; /* copy to each one */
2749 fs->off = range_beg;
2750 fs->len = (range_end - range_beg) + 1;
2751
2752 return (!len || (req->policy->max_range_nodes > con->fs_num));
2753 }
2754
2755 /* Allow...
2756 bytes=NUM-NUM
2757 bytes=-NUM
2758 bytes=NUM-
2759 ...and due to LWS, http crapola parsing, even...
2760 bytes = , , NUM - NUM , ,
2761 ...allowing ability to disable multiple ranges at once, due to
2762 multipart/byteranges being too much crack, I think this is stds. compliant.
2763 */
http_parse_range(struct Con * con,Httpd_req_data * req)2764 static int http_parse_range(struct Con *con, Httpd_req_data *req)
2765 {
2766 Vstr_base *data = req->http_hdrs->multi->comb;
2767 Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
2768 size_t pos = h_r->pos;
2769 size_t len = h_r->len;
2770 VSTR_AUTOCONF_uintmax_t fsize = req->f_stat->st_size;
2771 unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
2772 VSTR_FLAG_PARSE_NUM_OVERFLOW);
2773 size_t num_len = 0;
2774
2775 if (!VPREFIX(data, pos, len, "bytes"))
2776 return (0);
2777 len -= CLEN("bytes"); pos += CLEN("bytes");
2778
2779 HTTP_SKIP_LWS(data, pos, len);
2780
2781 if (!VPREFIX(data, pos, len, "="))
2782 return (0);
2783 len -= CLEN("="); pos += CLEN("=");
2784
2785 http__parse_skip_blanks(data, &pos, &len);
2786
2787 while (len)
2788 {
2789 VSTR_AUTOCONF_uintmax_t range_beg = 0;
2790 VSTR_AUTOCONF_uintmax_t range_end = 0;
2791
2792 if (VPREFIX(data, pos, len, "-"))
2793 { /* num bytes at end */
2794 VSTR_AUTOCONF_uintmax_t tmp = 0;
2795
2796 len -= CLEN("-"); pos += CLEN("-");
2797 HTTP_SKIP_LWS(data, pos, len);
2798
2799 tmp = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
2800 len -= num_len; pos += num_len;
2801 if (!num_len)
2802 return (0);
2803
2804 if (!tmp)
2805 return (416);
2806
2807 if (tmp >= fsize)
2808 return (0);
2809
2810 range_beg = fsize - tmp;
2811 range_end = fsize - 1;
2812 }
2813 else
2814 { /* offset - [end] */
2815 range_beg = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
2816 len -= num_len; pos += num_len;
2817 HTTP_SKIP_LWS(data, pos, len);
2818
2819 if (!VPREFIX(data, pos, len, "-"))
2820 return (0);
2821 len -= CLEN("-"); pos += CLEN("-");
2822 HTTP_SKIP_LWS(data, pos, len);
2823
2824 if (!len || VPREFIX(data, pos, len, ","))
2825 range_end = fsize - 1;
2826 else
2827 {
2828 range_end = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, 0);
2829 len -= num_len; pos += num_len;
2830 if (!num_len)
2831 return (0);
2832
2833 if (range_end >= fsize)
2834 range_end = fsize - 1;
2835 }
2836
2837 if ((range_beg >= fsize) || (range_beg > range_end))
2838 return (416);
2839
2840 if ((range_beg == 0) &&
2841 (range_end == (fsize - 1)))
2842 return (0);
2843 }
2844
2845 http__parse_skip_blanks(data, &pos, &len);
2846
2847 if (!httpd__file_sect_add(con, req, range_beg, range_end, len))
2848 return (0); /* after all that, ignore if there is more than one range */
2849 }
2850
2851 return (200);
2852 }
2853
httpd_serv_file_sects_none(struct Con * con,Httpd_req_data * req)2854 static void httpd_serv_file_sects_none(struct Con *con, Httpd_req_data *req)
2855 {
2856 con->use_mpbr = FALSE;
2857 con->fs_num = 1;
2858 con->fs->off = 0;
2859 con->fs->len = req->f_stat->st_size;
2860 }
2861
httpd_serv_call_file_init(struct Con * con,Httpd_req_data * req,unsigned int * http_ret_code,const char ** http_ret_line)2862 static void httpd_serv_call_file_init(struct Con *con, Httpd_req_data *req,
2863 unsigned int *http_ret_code,
2864 const char ** http_ret_line)
2865 {
2866 ASSERT(req);
2867
2868 con->use_mmap = FALSE;
2869 if (!req->head_op)
2870 {
2871 httpd_serv_call_mmap(con, req, con->fs);
2872 httpd_serv_call_seek(con, req, con->fs, http_ret_code, http_ret_line);
2873 }
2874 }
2875
http_req_1_x(struct Con * con,Httpd_req_data * req,unsigned int * http_ret_code,const char ** http_ret_line)2876 static int http_req_1_x(struct Con *con, Httpd_req_data *req,
2877 unsigned int *http_ret_code,
2878 const char **http_ret_line)
2879 {
2880 Vstr_base *out = con->evnt->io_w;
2881 Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
2882 time_t mtime = -1;
2883
2884 if (req->ver_1_1 && req->http_hdrs->hdr_expect->len)
2885 /* I'm pretty sure we can ignore 100-continue, as no request will
2886 * have a body */
2887 HTTPD_ERR_RET(req, 417, FALSE);
2888
2889 httpd__try_fd_encoding(con, req, req->f_stat, req->fname);
2890
2891 if (req->policy->use_err_406 &&
2892 !req->content_encoding_identity &&
2893 !req->content_encoding_bzip2 && !req->content_encoding_gzip)
2894 HTTPD_ERR_RET(req, 406, FALSE);
2895
2896 if (h_r->pos)
2897 {
2898 int ret_code = 0;
2899
2900 if (!(req->policy->use_range &&
2901 (req->ver_1_1 || req->policy->use_range_1_0)))
2902 h_r->pos = 0;
2903 else if (!(ret_code = http_parse_range(con, req)))
2904 h_r->pos = 0;
2905 ASSERT(!ret_code || (ret_code == 200) || (ret_code == 416));
2906 if (ret_code == 416)
2907 {
2908 if (!req->http_hdrs->hdr_if_range->pos)
2909 HTTPD_ERR_RET(req, 416, FALSE);
2910 h_r->pos = 0;
2911 }
2912 }
2913
2914 if (!http_response_ok(con, req, http_ret_code, http_ret_line))
2915 HTTPD_ERR_RET(req, 412, FALSE);
2916
2917 if (!h_r->pos)
2918 httpd_serv_file_sects_none(con, req);
2919
2920 httpd_serv_call_file_init(con, req, http_ret_code, http_ret_line);
2921
2922 ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
2923 ASSERT(!con->fs_off);
2924
2925 mtime = req->f_stat->st_mtime;
2926 http_app_def_hdrs(con, req, *http_ret_code, *http_ret_line,
2927 mtime, NULL, TRUE, con->fs->len);
2928 if (h_r->pos && !con->use_mpbr)
2929 http_app_hdr_fmt(out, "Content-Range", "%s %ju-%ju/%ju", "bytes",
2930 con->fs->off, con->fs->off + (con->fs->len - 1),
2931 (VSTR_AUTOCONF_uintmax_t)req->f_stat->st_size);
2932 if (req->content_location_vs1)
2933 http_app_hdr_vstr_def(out, "Content-Location",
2934 HTTP__XTRA_HDR_PARAMS(req, content_location));
2935 http_app_hdrs_url(con, req);
2936 http_app_hdrs_file(con, req);
2937
2938 http_app_end_hdrs(out);
2939
2940 if (req->head_op)
2941 con->use_mpbr = FALSE;
2942 else if (h_r->pos && con->use_mpbr)
2943 {
2944 con->mpbr_fs_len = req->f_stat->st_size;
2945 http_app_hdrs_mpbr(con, con->fs);
2946 }
2947
2948 return (TRUE);
2949 }
2950
2951 /* skip, or fail on syntax error */
http__skip_parameters(Vstr_base * data,size_t * pos,size_t * len)2952 static int http__skip_parameters(Vstr_base *data, size_t *pos, size_t *len)
2953 {
2954 while (*len && (vstr_export_chr(data, *pos) != ','))
2955 { /* skip parameters */
2956 size_t tmp = 0;
2957
2958 if (vstr_export_chr(data, *pos) != ';')
2959 return (FALSE); /* syntax error */
2960
2961 *len -= 1; *pos += 1;
2962 HTTP_SKIP_LWS(data, *pos, *len);
2963 tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,=");
2964 *len -= tmp; *pos += tmp;
2965 if (!*len)
2966 break;
2967
2968 switch (vstr_export_chr(data, *pos))
2969 {
2970 case ';': break;
2971 case ',': break;
2972
2973 case '=': /* skip parameter value */
2974 *len -= 1; *pos += 1;
2975 HTTP_SKIP_LWS(data, *pos, *len);
2976 if (!*len)
2977 return (FALSE); /* syntax error */
2978 if (vstr_export_chr(data, *pos) == '"')
2979 {
2980 if (!http__skip_quoted_string(data, pos, len))
2981 return (FALSE); /* syntax error */
2982 }
2983 else
2984 {
2985 tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,");
2986 *len -= tmp; *pos += tmp;
2987 }
2988 break;
2989 }
2990 }
2991
2992 return (TRUE);
2993 }
2994
2995 /* returns quality of the passed content-type in the "Accept:" header,
2996 * if it isn't there or we get a syntax error we return 1001 for not avilable */
http_parse_accept(Httpd_req_data * req,const Vstr_base * ct_vs1,size_t ct_pos,size_t ct_len)2997 unsigned int http_parse_accept(Httpd_req_data *req,
2998 const Vstr_base *ct_vs1,
2999 size_t ct_pos, size_t ct_len)
3000 {
3001 Vstr_base *data = req->http_hdrs->multi->comb;
3002 size_t pos = 0;
3003 size_t len = 0;
3004 unsigned int num = 0;
3005 unsigned int quality = 1001;
3006 unsigned int dummy = 1001;
3007 int done_sub_type = FALSE;
3008 size_t ct_sub_len = 0;
3009
3010 pos = req->http_hdrs->multi->hdr_accept->pos;
3011 len = req->http_hdrs->multi->hdr_accept->len;
3012
3013 if (!len) /* no accept == accept all */
3014 return (1000);
3015
3016 ASSERT(ct_vs1);
3017
3018 if (!(ct_sub_len = vstr_srch_chr_fwd(ct_vs1, ct_pos, ct_len, '/')))
3019 { /* it's too weird, blank it */
3020 if (ct_vs1 == req->content_type_vs1)
3021 req->content_type_vs1 = NULL;
3022 return (1);
3023 }
3024 ct_sub_len = vstr_sc_posdiff(ct_pos, ct_sub_len);
3025
3026 while (len)
3027 {
3028 size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
3029 HTTP_EOL HTTP_LWS ";,");
3030
3031 ++num;
3032
3033 if (0) { }
3034 else if (vstr_cmp_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
3035 { /* full match */
3036 len -= tmp; pos += tmp;
3037 if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
3038 return (1);
3039 return (quality);
3040 }
3041 else if ((tmp == (ct_sub_len + 1)) &&
3042 vstr_cmp_eq(data, pos, ct_sub_len, ct_vs1, ct_pos, ct_sub_len) &&
3043 (vstr_export_chr(data, vstr_sc_poslast(pos, tmp)) == '*'))
3044 { /* sub match */
3045 len -= tmp; pos += tmp;
3046 if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
3047 return (1);
3048 done_sub_type = TRUE;
3049 }
3050 else if (!done_sub_type && VEQ(data, pos, tmp, "*/*"))
3051 {
3052 len -= tmp; pos += tmp;
3053 if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
3054 return (1);
3055 }
3056 else
3057 {
3058 len -= tmp; pos += tmp;
3059 if (!http_parse_quality(data, &pos, &len, TRUE, &dummy))
3060 return (1);
3061 }
3062
3063 if (!http__skip_parameters(data, &pos, &len))
3064 return (1);
3065 if (!len)
3066 break;
3067
3068 assert(VPREFIX(data, pos, len, ","));
3069 len -= 1; pos += 1;
3070 HTTP_SKIP_LWS(data, pos, len);
3071
3072 if (req->policy->max_A_nodes && (num >= req->policy->max_A_nodes))
3073 return (1);
3074 }
3075
3076 ASSERT(quality <= 1001);
3077 if (quality == 1001)
3078 return (0);
3079
3080 return (quality);
3081 }
3082
http__cmp_lang_eq(const Vstr_base * s1,size_t p1,size_t l1,const Vstr_base * s2,size_t p2,size_t l2)3083 static int http__cmp_lang_eq(const Vstr_base *s1, size_t p1, size_t l1,
3084 const Vstr_base *s2, size_t p2, size_t l2)
3085 {
3086 if (l1 == l2)
3087 return (FALSE);
3088
3089 if (l1 > l2)
3090 return ((vstr_export_chr(s1, p1 + l2) == '-') &&
3091 vstr_cmp_case_eq(s1, p1, l2, s2, p2, l2));
3092
3093 return ((vstr_export_chr(s2, p2 + l1) == '-') &&
3094 vstr_cmp_case_eq(s1, p1, l1, s2, p2, l1));
3095 }
3096
http_parse_accept_language(Httpd_req_data * req,const Vstr_base * ct_vs1,size_t ct_pos,size_t ct_len)3097 unsigned int http_parse_accept_language(Httpd_req_data *req,
3098 const Vstr_base *ct_vs1,
3099 size_t ct_pos, size_t ct_len)
3100 {
3101 Vstr_base *data = req->http_hdrs->multi->comb;
3102 size_t pos = 0;
3103 size_t len = 0;
3104 unsigned int num = 0;
3105 unsigned int quality = 1001;
3106 unsigned int dummy = 1001;
3107 size_t done_sub_type = 0;
3108
3109 pos = req->http_hdrs->multi->hdr_accept_language->pos;
3110 len = req->http_hdrs->multi->hdr_accept_language->len;
3111
3112 if (!len) /* no accept-language == accept all */
3113 return (1000);
3114
3115 ASSERT(ct_vs1);
3116 while (len)
3117 {
3118 size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
3119 HTTP_EOL HTTP_LWS ";,");
3120
3121 ++num;
3122
3123 if (0) { }
3124 else if (vstr_cmp_case_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
3125 { /* full match */
3126 len -= tmp; pos += tmp;
3127 if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
3128 return (1);
3129 return (quality);
3130 }
3131 else if ((tmp >= done_sub_type) &&
3132 http__cmp_lang_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
3133 { /* sub match - can be x-y-A;q=0, x-y-B, x-y
3134 rfc2616#14.4
3135 The language quality factor assigned to a language-tag by the
3136 Accept-Language field is the quality value of the longest
3137 language-range in the field that matches the language-tag.
3138 */
3139 unsigned int sub_type_qual = 0;
3140
3141 len -= tmp; pos += tmp;
3142 if (!http_parse_quality(data, &pos, &len, FALSE, &sub_type_qual))
3143 return (1);
3144
3145 ASSERT(sub_type_qual <= 1000);
3146 if ((tmp > done_sub_type) || (sub_type_qual > quality))
3147 quality = sub_type_qual;
3148
3149 done_sub_type = tmp;
3150 }
3151 else if (!done_sub_type && VEQ(data, pos, tmp, "*"))
3152 {
3153 len -= tmp; pos += tmp;
3154 if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
3155 return (1);
3156 }
3157 else
3158 {
3159 len -= tmp; pos += tmp;
3160 if (!http_parse_quality(data, &pos, &len, FALSE, &dummy))
3161 return (1);
3162 }
3163
3164 if (!len)
3165 break;
3166
3167 assert(VPREFIX(data, pos, len, ","));
3168 len -= 1; pos += 1;
3169 HTTP_SKIP_LWS(data, pos, len);
3170
3171 if (req->policy->max_AL_nodes && (num >= req->policy->max_AL_nodes))
3172 return (1);
3173 }
3174
3175 ASSERT(quality <= 1001);
3176 if (quality == 1001)
3177 return (0);
3178
3179 return (quality);
3180 }
3181
3182 /* Lookup content type for filename, If this lookup "fails" it still returns
3183 * the default content-type. So we just have to determine if we want to use
3184 * it or not. Can also return "content-types" like /404/ which returns a 404
3185 * error for the request */
http_req_content_type(Httpd_req_data * req)3186 int http_req_content_type(Httpd_req_data *req)
3187 {
3188 const Vstr_base *vs1 = NULL;
3189 size_t pos = 0;
3190 size_t len = 0;
3191
3192 if (req->content_type_vs1) /* manually set */
3193 return (TRUE);
3194
3195 mime_types_match(req->policy->mime_types,
3196 req->fname, 1, req->fname->len, &vs1, &pos, &len);
3197 if (!len)
3198 {
3199 req->parse_accept = FALSE;
3200 return (TRUE);
3201 }
3202
3203 if ((vstr_export_chr(vs1, pos) == '/') && (len > 2) &&
3204 (vstr_export_chr(vs1, vstr_sc_poslast(pos, len)) == '/'))
3205 {
3206 size_t num_len = 1;
3207
3208 len -= 2;
3209 ++pos;
3210 req->user_return_error_code = TRUE;
3211 req->direct_filename = FALSE;
3212 switch (vstr_parse_uint(vs1, pos, len, 0, &num_len, NULL))
3213 {
3214 case 400: if (num_len == len) HTTPD_ERR_RET(req, 400, FALSE);
3215 case 403: if (num_len == len) HTTPD_ERR_RET(req, 403, FALSE);
3216 case 404: if (num_len == len) HTTPD_ERR_RET(req, 404, FALSE);
3217 case 410: if (num_len == len) HTTPD_ERR_RET(req, 410, FALSE);
3218 case 500: if (num_len == len) HTTPD_ERR_RET(req, 500, FALSE);
3219 case 503: if (num_len == len) HTTPD_ERR_RET(req, 503, FALSE);
3220
3221 default: /* just ignore any other content */
3222 req->user_return_error_code = FALSE;
3223 return (TRUE);
3224 }
3225 }
3226
3227 req->content_type_vs1 = vs1;
3228 req->content_type_pos = pos;
3229 req->content_type_len = len;
3230
3231 return (TRUE);
3232 }
3233
http__policy_req(struct Con * con,Httpd_req_data * req)3234 static int http__policy_req(struct Con *con, Httpd_req_data *req)
3235 {
3236 if (!httpd_policy_request(con, req,
3237 httpd_opts->conf, httpd_opts->match_request))
3238 {
3239 Vstr_base *s1 = httpd_opts->conf->tmp;
3240 if (!req->user_return_error_code)
3241 vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]:"
3242 " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1);
3243 return (TRUE);
3244 }
3245
3246 if (con->evnt->flag_q_closed)
3247 {
3248 Vstr_base *s1 = req->policy->s->policy_name;
3249
3250 vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
3251 CON_CEVNT_SA(con), s1);
3252 return (FALSE);
3253 }
3254
3255 if (req->direct_uri)
3256 {
3257 Vstr_base *s1 = req->policy->s->policy_name;
3258 vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
3259 " Has URI.\n", CON_CEVNT_SA(con), s1);
3260 HTTPD_ERR_RET(req, 503, TRUE);
3261 }
3262
3263 if (req->policy->auth_token->len) /* they need rfc2617 auth */
3264 {
3265 Vstr_base *data = con->evnt->io_r;
3266 size_t pos = req->http_hdrs->hdr_authorization->pos;
3267 size_t len = req->http_hdrs->hdr_authorization->len;
3268 Vstr_base *auth_token = req->policy->auth_token;
3269 int auth_ok = TRUE;
3270
3271 if (!VIPREFIX(data, pos, len, "Basic"))
3272 auth_ok = FALSE;
3273 else
3274 {
3275 len -= CLEN("Basic"); pos += CLEN("Basic");
3276 HTTP_SKIP_LWS(data, pos, len);
3277 if (!vstr_cmp_eq(data, pos, len, auth_token, 1, auth_token->len))
3278 auth_ok = FALSE;
3279 }
3280
3281 if (!auth_ok)
3282 {
3283 req->user_return_error_code = FALSE;
3284 HTTPD_ERR(req, 401);
3285 }
3286 }
3287
3288 return (TRUE);
3289 }
3290
http_req_op_get(struct Con * con,Httpd_req_data * req)3291 int http_req_op_get(struct Con *con, Httpd_req_data *req)
3292 { /* GET or HEAD ops */
3293 Vstr_base *data = con->evnt->io_r;
3294 Vstr_base *out = con->evnt->io_w;
3295 Vstr_base *fname = req->fname;
3296 const char *fname_cstr = NULL;
3297 unsigned int http_ret_code = 200;
3298 const char * http_ret_line = "OK";
3299
3300 if (fname->conf->malloc_bad)
3301 goto malloc_err;
3302
3303 assert(VPREFIX(fname, 1, fname->len, "/"));
3304
3305 /* final act of vengance, before policy */
3306 if (vstr_srch_cstr_buf_fwd(data, req->path_pos, req->path_len, "//"))
3307 { /* in theory we can skip this if there is no policy */
3308 vstr_sub_vstr(fname, 1, fname->len, data, req->path_pos, req->path_len, 0);
3309 if (!httpd_canon_path(fname))
3310 goto malloc_err;
3311 httpd_req_absolute_uri(con, req, fname, 1, fname->len);
3312 HTTPD_ERR_301(req);
3313
3314 return (http_fin_err_req(con, req));
3315 }
3316
3317 if (!http__policy_req(con, req))
3318 return (FALSE);
3319 if (req->error_code)
3320 return (http_fin_err_req(con, req));
3321
3322 httpd_sc_add_default_filename(req, fname);
3323
3324 if (!http__conf_req(con, req))
3325 return (FALSE);
3326 if (req->error_code)
3327 return (http_fin_err_req(con, req));
3328
3329 if (!http_req_content_type(req))
3330 return (http_fin_err_req(con, req));
3331
3332 /* don't change vary_a/vary_al just because of 406 */
3333 if (req->parse_accept)
3334 {
3335 if (!http_parse_accept(req, HTTP__XTRA_HDR_PARAMS(req, content_type)))
3336 HTTPD_ERR_RET(req, 406, http_fin_err_req(con, req));
3337 }
3338 if (!req->content_language_vs1 || !req->content_language_len)
3339 req->parse_accept_language = FALSE;
3340 if (req->parse_accept_language)
3341 {
3342 if (!http_parse_accept_language(req,
3343 HTTP__XTRA_HDR_PARAMS(req,
3344 content_language)))
3345 HTTPD_ERR_RET(req, 406, http_fin_err_req(con, req));
3346 }
3347
3348 /* Add the document root now, this must be at least . so
3349 * "///foo" becomes ".///foo" ... this is done now
3350 * so nothing has to deal with document_root values */
3351 if (!req->skip_document_root)
3352 http_prepend_doc_root(fname, req);
3353
3354 fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
3355
3356 if (fname->conf->malloc_bad)
3357 goto malloc_err;
3358
3359 ASSERT(con->fs && !con->fs_num && !con->fs_off && (con->fs->fd == -1));
3360 if ((con->fs->fd = io_open_nonblock(fname_cstr)) == -1)
3361 {
3362 if (0) { }
3363 else if (req->direct_filename && (errno == EISDIR)) /* don't allow */
3364 HTTPD_ERR(req, 404);
3365 else if (errno == EISDIR)
3366 return (http_req_chk_dir(con, req));
3367 else if (((errno == ENOENT) && req->conf_friendly_dirs) ||
3368 (errno == ENOTDIR) || /* part of path was not a dir */
3369 (errno == ENAMETOOLONG) || /* 414 ? */
3370 FALSE)
3371 return (http_req_chk_file(con, req));
3372 else if (errno == EACCES)
3373 HTTPD_ERR(req, 403);
3374 else if ((errno == ENOENT) ||
3375 (errno == ENODEV) || /* device file, with no driver */
3376 (errno == ENXIO) || /* device file, with no driver */
3377 (errno == ELOOP) || /* symlinks */
3378 FALSE)
3379 HTTPD_ERR(req, 404);
3380 else
3381 HTTPD_ERR(req, 500);
3382
3383 return (http_fin_err_req(con, req));
3384 }
3385 if (fstat64(con->fs->fd, req->f_stat) == -1)
3386 HTTPD_ERR_RET(req, 500, http_fin_err_close_req(con, req));
3387 if (req->policy->use_public_only && !(req->f_stat->st_mode & S_IROTH))
3388 HTTPD_ERR_RET(req, 403, http_fin_err_close_req(con, req));
3389
3390 if (S_ISDIR(req->f_stat->st_mode))
3391 {
3392 if (req->direct_filename) /* don't allow */
3393 HTTPD_ERR_RET(req, 404, http_fin_err_close_req(con, req));
3394 httpd_fin_fd_close(con);
3395 return (http_req_chk_dir(con, req));
3396 }
3397 if (!S_ISREG(req->f_stat->st_mode))
3398 HTTPD_ERR_RET(req, 403, http_fin_err_close_req(con, req));
3399
3400 con->fs->len = req->f_stat->st_size;
3401
3402 if (req->ver_0_9)
3403 {
3404 httpd_serv_file_sects_none(con, req);
3405 httpd_serv_call_file_init(con, req, &http_ret_code, &http_ret_line);
3406 http_ret_line = "OK - HTTP/0.9";
3407 }
3408 else if (!http_req_1_x(con, req, &http_ret_code, &http_ret_line))
3409 return (http_fin_err_close_req(con, req));
3410
3411 if (out->conf->malloc_bad)
3412 goto malloc_close_err;
3413
3414 vlg_dbg3(vlg, "REPLY:\n$<vstr.all:%p>\n", out);
3415
3416 if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
3417 req->f_mmap, 1, req->f_mmap->len))
3418 goto malloc_close_err;
3419
3420 /* req->head_op is set for 304 returns */
3421 vlg_info(vlg, "REQ $<vstr.sect:%p%p%u> from[$<sa:%p>] ret[%03u %s]"
3422 " sz[${BKMG.ju:%ju}:%ju]", data, req->sects, 1U, CON_CEVNT_SA(con),
3423 http_ret_code, http_ret_line, con->fs->len, con->fs->len);
3424 http_vlg_def(con, req);
3425
3426 return (http_fin_fd_req(con, req));
3427
3428 malloc_close_err:
3429 httpd_fin_fd_close(con);
3430 malloc_err:
3431 VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_get(): %m\n"));
3432 }
3433
http_req_op_opts(struct Con * con,Httpd_req_data * req)3434 int http_req_op_opts(struct Con *con, Httpd_req_data *req)
3435 {
3436 Vstr_base *out = con->evnt->io_w;
3437 Vstr_base *fname = req->fname;
3438 VSTR_AUTOCONF_uintmax_t tmp = 0;
3439
3440 if (fname->conf->malloc_bad)
3441 goto malloc_err;
3442
3443 assert(VPREFIX(fname, 1, fname->len, "/") ||
3444 !req->policy->use_vhosts_name ||
3445 !req->policy->use_host_err_chk ||
3446 !req->policy->use_host_err_400 ||
3447 VEQ(con->evnt->io_r, req->path_pos, req->path_len, "*"));
3448
3449 /* apache doesn't test for 404's here ... which seems weird */
3450
3451 http_app_def_hdrs(con, req, 200, "OK", 0, NULL, TRUE, 0);
3452 HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
3453 http_app_end_hdrs(out);
3454 if (out->conf->malloc_bad)
3455 goto malloc_err;
3456
3457 vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
3458 "OPTIONS", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
3459 http_vlg_def(con, req);
3460
3461 return (http_fin_req(con, req));
3462
3463 malloc_err:
3464 VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_opts(): %m\n"));
3465 }
3466
http_req_op_trace(struct Con * con,Httpd_req_data * req)3467 int http_req_op_trace(struct Con *con, Httpd_req_data *req)
3468 {
3469 Vstr_base *data = con->evnt->io_r;
3470 Vstr_base *out = con->evnt->io_w;
3471 VSTR_AUTOCONF_uintmax_t tmp = 0;
3472
3473 http_app_def_hdrs(con, req, 200, "OK", req->now,
3474 "message/http", FALSE, req->len);
3475 http_app_end_hdrs(out);
3476 vstr_add_vstr(out, out->len, data, 1, req->len, VSTR_TYPE_ADD_DEF);
3477 if (out->conf->malloc_bad)
3478 VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_trace(): %m\n"));
3479
3480 tmp = req->len;
3481 vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
3482 "TRACE", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
3483 http_vlg_def(con, req);
3484
3485 return (http_fin_req(con, req));
3486 }
3487
3488 /* characters that are valid in a part of a URL _and_ in a file basename ...
3489 * without encoding */
3490 #define HTTPD__VALID_CSTR_CHRS_URL_FILENAME \
3491 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
3492 "abcdefghijklmnopqrstuvwxyz" \
3493 "0123456789" \
3494 ":.-_~"
httpd_valid_url_filename(Vstr_base * s1,size_t pos,size_t len)3495 int httpd_valid_url_filename(Vstr_base *s1, size_t pos, size_t len)
3496 {
3497 const char *const cstr = HTTPD__VALID_CSTR_CHRS_URL_FILENAME;
3498 return (vstr_spn_cstr_chrs_fwd(s1, pos, len, cstr) == s1->len);
3499 }
3500
httpd_init_default_hostname(Opt_serv_policy_opts * sopts)3501 int httpd_init_default_hostname(Opt_serv_policy_opts *sopts)
3502 {
3503 Httpd_policy_opts *popts = (Httpd_policy_opts *)sopts;
3504 Vstr_base *nhn = popts->default_hostname;
3505 Vstr_base *chn = NULL;
3506
3507 if (!httpd_valid_url_filename(nhn, 1, nhn->len))
3508 vstr_del(nhn, 1, nhn->len);
3509
3510 if (nhn->len)
3511 return (TRUE);
3512
3513 if (sopts != sopts->beg->def_policy)
3514 chn = ((Httpd_policy_opts *)sopts->beg->def_policy)->default_hostname;
3515
3516 if (chn)
3517 HTTPD_APP_REF_ALLVSTR(nhn, chn);
3518 else
3519 {
3520 opt_serv_sc_append_hostname(nhn, 0);
3521 vstr_conv_lowercase(nhn, 1, nhn->len);
3522 }
3523
3524 return (!nhn->conf->malloc_bad);
3525 }
3526
httpd__chk_vhost(const Httpd_policy_opts * popts,Vstr_base * lfn,size_t pos,size_t len)3527 static int httpd__chk_vhost(const Httpd_policy_opts *popts,
3528 Vstr_base *lfn, size_t pos, size_t len)
3529 {
3530 const char *vhost = NULL;
3531 struct stat64 v_stat[1];
3532 const Vstr_base *def_hname = popts->default_hostname;
3533 int ret = -1;
3534
3535 ASSERT(pos);
3536
3537 if (!popts->use_host_err_chk)
3538 return (TRUE);
3539
3540 if (vstr_cmp_eq(lfn, pos, len, def_hname, 1, def_hname->len))
3541 return (TRUE); /* don't do lots of work for nothing */
3542
3543 vstr_add_vstr(lfn, pos - 1,
3544 popts->document_root, 1, popts->document_root->len,
3545 VSTR_TYPE_ADD_BUF_PTR);
3546 len += popts->document_root->len;
3547
3548 if (lfn->conf->malloc_bad || !(vhost = vstr_export_cstr_ptr(lfn, pos, len)))
3549 return (TRUE); /* dealt with as errmem_req() later */
3550
3551 ret = stat64(vhost, v_stat);
3552 vstr_del(lfn, pos, popts->document_root->len);
3553
3554 if (ret == -1)
3555 return (FALSE);
3556
3557 if (!S_ISDIR(v_stat->st_mode))
3558 return (FALSE);
3559
3560 return (TRUE);
3561 }
3562
httpd_serv_add_vhost(struct Con * con,struct Httpd_req_data * req)3563 static int httpd_serv_add_vhost(struct Con *con, struct Httpd_req_data *req)
3564 {
3565 Vstr_base *data = con->evnt->io_r;
3566 Vstr_base *fname = req->fname;
3567 Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
3568 size_t h_h_pos = h_h->pos;
3569 size_t h_h_len = h_h->len;
3570 size_t orig_len = 0;
3571
3572 if (!req->policy->use_vhosts_name)
3573 return (TRUE);
3574
3575 if (h_h_len && req->policy->use_canonize_host)
3576 {
3577 size_t dots = 0;
3578
3579 if (VIPREFIX(data, h_h_pos, h_h_len, "www."))
3580 { h_h_len -= CLEN("www."); h_h_pos += CLEN("www."); }
3581
3582 dots = vstr_spn_cstr_chrs_rev(data, h_h_pos, h_h_len, ".");
3583 h_h_len -= dots;
3584 }
3585 h_h->pos = h_h_pos;
3586 h_h->len = h_h_len;
3587
3588 orig_len = fname->len;
3589 if (!h_h_len)
3590 httpd_sc_add_default_hostname(con, req, fname, 0);
3591 else if (vstr_add_vstr(fname, 0, data, /* add as buf's, for lowercase op */
3592 h_h_pos, h_h_len, VSTR_TYPE_ADD_DEF))
3593 {
3594 vstr_conv_lowercase(fname, 1, h_h_len);
3595
3596 if (!httpd__chk_vhost(req->policy, fname, 1, h_h_len))
3597 {
3598 if (req->policy->use_host_err_400)
3599 HTTPD_ERR_RET(req, 400, FALSE); /* rfc2616 5.2 */
3600 else
3601 { /* what everything else does ... *sigh* */
3602 if (fname->conf->malloc_bad)
3603 return (TRUE);
3604
3605 h_h->len = 0;
3606 vstr_del(fname, 1, h_h_len);
3607 httpd_sc_add_default_hostname(con, req, fname, 0);
3608 }
3609 }
3610 }
3611 vstr_add_cstr_ptr(fname, 0, "/");
3612
3613 req->vhost_prefix_len = (fname->len - orig_len);
3614
3615 return (TRUE);
3616 }
3617
3618 /* Decode url-path,
3619 check url-path for a bunch of problems,
3620 if vhosts is on add vhost prefix,
3621 Note we don't do dir_filename additions yet */
http_req_make_path(struct Con * con,Httpd_req_data * req)3622 static int http_req_make_path(struct Con *con, Httpd_req_data *req)
3623 {
3624 Vstr_base *data = con->evnt->io_r;
3625 Vstr_base *fname = req->fname;
3626
3627 ASSERT(!fname->len);
3628
3629 assert(VPREFIX(data, req->path_pos, req->path_len, "/") ||
3630 VEQ(data, req->path_pos, req->path_len, "*"));
3631
3632 if (req->chk_encoded_slash &&
3633 vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2f"))
3634 HTTPD_ERR_RET(req, 403, FALSE);
3635 if (req->chk_encoded_dot &&
3636 vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2e"))
3637 HTTPD_ERR_RET(req, 403, FALSE);
3638 req->chked_encoded_path = TRUE;
3639
3640 vstr_add_vstr(fname, 0,
3641 data, req->path_pos, req->path_len, VSTR_TYPE_ADD_BUF_PTR);
3642 vstr_conv_decode_uri(fname, 1, fname->len);
3643
3644 if (fname->conf->malloc_bad) /* dealt with as errmem_req() later */
3645 return (TRUE);
3646
3647 /* NOTE: need to split function here so we can more efficently alter the
3648 * path. */
3649 if (!httpd_serv_add_vhost(con, req))
3650 return (FALSE);
3651
3652 /* check posix path ... including hostname, for NIL and path escapes */
3653 if (vstr_srch_chr_fwd(fname, 1, fname->len, 0))
3654 HTTPD_ERR_RET(req, 403, FALSE);
3655
3656 /* web servers don't have relative paths, so /./ and /../ aren't "special" */
3657 if (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/../") ||
3658 VSUFFIX(req->fname, 1, req->fname->len, "/.."))
3659 HTTPD_ERR_RET(req, 400, FALSE);
3660 if (req->policy->chk_dot_dir &&
3661 (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/./") ||
3662 VSUFFIX(req->fname, 1, req->fname->len, "/.")))
3663 HTTPD_ERR_RET(req, 400, FALSE);
3664
3665 ASSERT(fname->len);
3666 assert(VPREFIX(fname, 1, fname->len, "/") ||
3667 VEQ(fname, 1, fname->len, "*") ||
3668 fname->conf->malloc_bad);
3669
3670 if (fname->conf->malloc_bad)
3671 return (TRUE);
3672
3673 return (TRUE);
3674 }
3675
http_parse_wait_io_r(struct Con * con)3676 static int http_parse_wait_io_r(struct Con *con)
3677 {
3678 if (con->evnt->io_r_shutdown)
3679 return (!!con->evnt->io_w->len);
3680
3681 evnt_wait_cntl_add(con->evnt, POLLIN);
3682 evnt_fd_set_cork(con->evnt, FALSE);
3683
3684 return (TRUE);
3685 }
3686
httpd_serv__parse_no_req(struct Con * con,struct Httpd_req_data * req)3687 static int httpd_serv__parse_no_req(struct Con *con, struct Httpd_req_data *req)
3688 {
3689 if (req->policy->max_header_sz &&
3690 (con->evnt->io_r->len > req->policy->max_header_sz))
3691 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3692
3693 http_req_free(req);
3694
3695 return (http_parse_wait_io_r(con));
3696 }
3697
3698 /* http spec says ignore leading LWS ... *sigh* */
http__parse_req_all(struct Con * con,struct Httpd_req_data * req,const char * eol,int * ern)3699 static int http__parse_req_all(struct Con *con, struct Httpd_req_data *req,
3700 const char *eol, int *ern)
3701 {
3702 Vstr_base *data = con->evnt->io_r;
3703
3704 ASSERT(eol && ern);
3705
3706 *ern = FALSE;
3707
3708 if (!(req->len = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
3709 goto no_req;
3710
3711 if (req->len == 1)
3712 { /* should use vstr_del(data, 1, vstr_spn_cstr_buf_fwd(..., HTTP_EOL)); */
3713 while (VPREFIX(data, 1, data->len, HTTP_EOL))
3714 vstr_del(data, 1, CLEN(HTTP_EOL));
3715
3716 if (!(req->len = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
3717 goto no_req;
3718
3719 ASSERT(req->len > 1);
3720 }
3721
3722 req->len += CLEN(eol) - 1; /* add rest of EOL */
3723
3724 return (TRUE);
3725
3726 no_req:
3727 *ern = httpd_serv__parse_no_req(con, req);
3728 return (FALSE);
3729 }
3730
http_parse_req(struct Con * con)3731 static int http_parse_req(struct Con *con)
3732 {
3733 Vstr_base *data = con->evnt->io_r;
3734 struct Httpd_req_data *req = NULL;
3735 int ern_req_all = FALSE;
3736
3737 ASSERT(con->fs && !con->fs_num);
3738
3739 if (!data->len)
3740 return (http_parse_wait_io_r(con));
3741
3742 if (!(req = http_req_make(con)))
3743 return (FALSE);
3744
3745 if (con->parsed_method_ver_1_0) /* wait for all the headers */
3746 {
3747 if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
3748 return (ern_req_all);
3749 }
3750 else
3751 {
3752 if (!http__parse_req_all(con, req, HTTP_EOL, &ern_req_all))
3753 return (ern_req_all);
3754 }
3755
3756 con->keep_alive = HTTP_NON_KEEP_ALIVE;
3757 http_req_split_method(con, req);
3758 if (req->sects->malloc_bad)
3759 VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "split: %m\n"));
3760 else if (req->sects->num < 2)
3761 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3762 else
3763 {
3764 size_t op_pos = 0;
3765 size_t op_len = 0;
3766
3767 if (req->ver_0_9)
3768 vlg_dbg1(vlg, "Method(0.9):"
3769 " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>\n",
3770 con->evnt->io_r, req->sects, 1U,
3771 con->evnt->io_r, req->sects, 2U);
3772 else
3773 { /* need to get all headers */
3774 if (!con->parsed_method_ver_1_0)
3775 {
3776 con->parsed_method_ver_1_0 = TRUE;
3777 if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
3778 return (ern_req_all);
3779 }
3780
3781 vlg_dbg1(vlg, "Method(1.x):"
3782 " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>"
3783 " $<http-esc.vstr.sect:%p%p%u>\n", data, req->sects, 1U,
3784 data, req->sects, 2U, data, req->sects, 3U);
3785 http_req_split_hdrs(con, req);
3786 }
3787 evnt_got_pkt(con->evnt);
3788
3789 if (HTTP_CONF_SAFE_PRINT_REQ)
3790 vlg_dbg3(vlg, "REQ:\n$<vstr.hexdump:%p%zu%zu>",
3791 data, (size_t)1, req->len);
3792 else
3793 vlg_dbg3(vlg, "REQ:\n$<vstr:%p%zu%zu>", data, (size_t)1, req->len);
3794
3795 assert(((req->sects->num >= 3) && !req->ver_0_9) || (req->sects->num == 2));
3796
3797 op_pos = VSTR_SECTS_NUM(req->sects, 1)->pos;
3798 op_len = VSTR_SECTS_NUM(req->sects, 1)->len;
3799 req->path_pos = VSTR_SECTS_NUM(req->sects, 2)->pos;
3800 req->path_len = VSTR_SECTS_NUM(req->sects, 2)->len;
3801
3802 if (!req->ver_0_9 && !http__parse_1_x(con, req))
3803 {
3804 if (req->error_code == 500)
3805 return (http_fin_errmem_req(con, req));
3806 return (http_fin_err_req(con, req));
3807 }
3808
3809 if (!http_parse_host(con, req))
3810 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3811
3812 if (0) { }
3813 else if (VEQ(data, op_pos, op_len, "GET"))
3814 {
3815 if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
3816 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3817
3818 if (!http_req_make_path(con, req))
3819 return (http_fin_err_req(con, req));
3820
3821 return (http_req_op_get(con, req));
3822 }
3823 else if (req->ver_0_9) /* 400 or 501? - apache does 400 */
3824 HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
3825 else if (VEQ(data, op_pos, op_len, "HEAD"))
3826 {
3827 req->head_op = TRUE; /* not sure where this should go here */
3828
3829 if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
3830 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3831
3832 if (!http_req_make_path(con, req))
3833 return (http_fin_err_req(con, req));
3834
3835 return (http_req_op_get(con, req));
3836 }
3837 else if (VEQ(data, op_pos, op_len, "OPTIONS"))
3838 {
3839 if (!VPREFIX(data, req->path_pos, req->path_len, "/") &&
3840 !VEQ(data, req->path_pos, req->path_len, "*"))
3841 HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
3842
3843 /* Speed hack: Don't even call make_path if it's "OPTIONS * ..."
3844 * and we don't need to check the Host header */
3845 if (req->policy->use_vhosts_name &&
3846 req->policy->use_host_err_chk && req->policy->use_host_err_400 &&
3847 !VEQ(data, req->path_pos, req->path_len, "*") &&
3848 !http_req_make_path(con, req))
3849 return (http_fin_err_req(con, req));
3850
3851 return (http_req_op_opts(con, req));
3852 }
3853 else if (req->policy->use_trace_op && VEQ(data, op_pos, op_len, "TRACE"))
3854 return (http_req_op_trace(con, req));
3855 else if (VEQ(data, op_pos, op_len, "TRACE") ||
3856 VEQ(data, op_pos, op_len, "POST") ||
3857 VEQ(data, op_pos, op_len, "PUT") ||
3858 VEQ(data, op_pos, op_len, "DELETE") ||
3859 VEQ(data, op_pos, op_len, "CONNECT") ||
3860 FALSE) /* we know about these ... but don't allow them */
3861 HTTPD_ERR_RET(req, 405, http_fin_err_req(con, req));
3862 else
3863 HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
3864 }
3865 ASSERT_NOT_REACHED();
3866 }
3867
httpd__serv_send_err(struct Con * con,const char * msg)3868 static int httpd__serv_send_err(struct Con *con, const char *msg)
3869 {
3870 if (errno != EPIPE)
3871 vlg_warn(vlg, "send(%s): %m\n", msg);
3872 else
3873 vlg_dbg2(vlg, "send(%s): SIGPIPE $<sa:%p>\n", msg, CON_CEVNT_SA(con));
3874
3875 return (FALSE);
3876 }
3877
httpd_serv_q_send(struct Con * con)3878 static int httpd_serv_q_send(struct Con *con)
3879 {
3880 vlg_dbg2(vlg, "http Q send $<sa:%p>\n", CON_CEVNT_SA(con));
3881 if (!evnt_send_add(con->evnt, TRUE, HTTPD_CONF_MAX_WAIT_SEND))
3882 return (httpd__serv_send_err(con, "Q"));
3883
3884 /* queued */
3885 return (TRUE);
3886 }
3887
httpd__serv_fin_send(struct Con * con)3888 static int httpd__serv_fin_send(struct Con *con)
3889 {
3890 if (con->keep_alive)
3891 { /* need to try immediately, as we might have already got the next req */
3892 if (!con->evnt->io_r_shutdown)
3893 evnt_wait_cntl_add(con->evnt, POLLIN);
3894 return (http_parse_req(con));
3895 }
3896
3897 vlg_dbg2(vlg, "shutdown_w = %p\n", con->evnt);
3898 return (evnt_shutdown_w(con->evnt));
3899 }
3900
3901 /* NOTE: lim is just a hint ... we can send more */
httpd__serv_send_lim(struct Con * con,const char * emsg,unsigned int lim,int * cont)3902 static int httpd__serv_send_lim(struct Con *con, const char *emsg,
3903 unsigned int lim, int *cont)
3904 {
3905 Vstr_base *out = con->evnt->io_w;
3906
3907 *cont = FALSE;
3908
3909 while (out->len >= lim)
3910 {
3911 if (!con->io_limit_num--) return (httpd_serv_q_send(con));
3912
3913 if (!evnt_send(con->evnt))
3914 return (httpd__serv_send_err(con, emsg));
3915 }
3916
3917 *cont = TRUE;
3918 return (TRUE);
3919 }
3920
httpd_serv_send(struct Con * con)3921 int httpd_serv_send(struct Con *con)
3922 {
3923 Vstr_base *out = con->evnt->io_w;
3924 int cont = FALSE;
3925 int ret = FALSE;
3926 struct File_sect *fs = NULL;
3927
3928 ASSERT(!out->conf->malloc_bad);
3929
3930 if (!con->fs_num)
3931 {
3932 ASSERT(!con->fs_off);
3933
3934 ret = httpd__serv_send_lim(con, "end", 1, &cont);
3935 if (!cont)
3936 return (ret);
3937
3938 return (httpd__serv_fin_send(con));
3939 }
3940
3941 ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
3942 fs = &con->fs[con->fs_off];
3943 ASSERT((fs->fd != -1) && fs->len);
3944
3945 if (con->use_sendfile)
3946 {
3947 unsigned int ern = 0;
3948
3949 ret = httpd__serv_send_lim(con, "sendfile", 1, &cont);
3950 if (!cont)
3951 return (ret);
3952
3953 while (fs->len)
3954 {
3955 if (!con->io_limit_num--) return (httpd_serv_q_send(con));
3956
3957 if (!evnt_sendfile(con->evnt, fs->fd, &fs->off, &fs->len, &ern))
3958 {
3959 if (ern == VSTR_TYPE_SC_READ_FD_ERR_EOF)
3960 goto file_eof_end;
3961
3962 if (errno == EPIPE)
3963 {
3964 vlg_dbg2(vlg, "sendfile: SIGPIPE $<sa:%p>\n", CON_CEVNT_SA(con));
3965 return (FALSE);
3966 }
3967
3968 if (errno == ENOSYS)
3969 httpd__disable_sendfile();
3970 vlg_warn(vlg, "sendfile: %m\n");
3971
3972 if (lseek64(fs->fd, fs->off, SEEK_SET) == -1)
3973 VLG_WARN_RET(FALSE,
3974 (vlg, "lseek(<sendfile>,off=%ju): %m\n", fs->off));
3975
3976 con->use_sendfile = FALSE;
3977 return (httpd_serv_send(con)); /* recurse */
3978 }
3979 }
3980
3981 goto file_end;
3982 }
3983
3984 while (fs->len)
3985 {
3986 ret = httpd__serv_send_lim(con, "max", EX_MAX_W_DATA_INCORE, &cont);
3987 if (!cont)
3988 return (ret);
3989
3990 switch (evnt_sc_read_send(con->evnt, fs->fd, &fs->len))
3991 {
3992 case EVNT_IO_OK:
3993 ASSERT_NO_SWITCH_DEF();
3994
3995 case EVNT_IO_READ_ERR:
3996 vlg_warn(vlg, "read: %m\n");
3997 out->conf->malloc_bad = FALSE;
3998
3999 case EVNT_IO_READ_FIN: goto file_end;
4000 case EVNT_IO_READ_EOF: goto file_eof_end;
4001
4002 case EVNT_IO_SEND_ERR:
4003 return (httpd__serv_send_err(con, "io_get"));
4004 }
4005 }
4006
4007 ASSERT_NOT_REACHED();
4008
4009 file_end:
4010 ASSERT(!fs->len);
4011 if (con->use_mpbr) /* multipart/byterange */
4012 {
4013 ASSERT(con->mpbr_ct);
4014 ASSERT(con->mpbr_fs_len);
4015
4016 if (!(fs = httpd__fd_next(con)))
4017 {
4018 vstr_add_cstr_ptr(out, out->len, "--SEP--" HTTP_EOL);
4019 return (httpd_serv_send(con)); /* restart with a read, or finish */
4020 }
4021 con->use_mmap = FALSE;
4022 if (!httpd__serv_call_seek(con, fs))
4023 VLG_WARN_RET(FALSE, (vlg, "lseek(<mpbr>,off=%ju): %m\n", fs->off));
4024
4025 http_app_hdrs_mpbr(con, fs);
4026 return (httpd_serv_send(con)); /* start outputting next file section */
4027 }
4028
4029 file_eof_end:
4030 if (fs->len) /* something bad happened, just kill the connection */
4031 con->keep_alive = HTTP_NON_KEEP_ALIVE;
4032
4033 httpd_fin_fd_close(con);
4034 return (httpd_serv_send(con)); /* restart with a read, or finish */
4035 }
4036
httpd_serv_recv(struct Con * con)4037 int httpd_serv_recv(struct Con *con)
4038 {
4039 unsigned int ern = 0;
4040 int ret = 0;
4041 Vstr_base *data = con->evnt->io_r;
4042
4043 ASSERT(!con->evnt->io_r_shutdown);
4044
4045 if (!con->io_limit_num--)
4046 return (TRUE);
4047
4048 if (!(ret = evnt_recv(con->evnt, &ern)))
4049 {
4050 if (ern != VSTR_TYPE_SC_READ_FD_ERR_EOF)
4051 {
4052 vlg_dbg2(vlg, "RECV ERR from[$<sa:%p>]: %u\n", CON_CEVNT_SA(con), ern);
4053 goto con_cleanup;
4054 }
4055 if (!evnt_shutdown_r(con->evnt, TRUE))
4056 goto con_cleanup;
4057 }
4058
4059 if (con->fs_num) /* may need to stop input, until we can deal with next req */
4060 {
4061 ASSERT(con->keep_alive || con->parsed_method_ver_1_0);
4062
4063 if (con->policy->max_header_sz && (data->len > con->policy->max_header_sz))
4064 evnt_wait_cntl_del(con->evnt, POLLIN);
4065
4066 return (TRUE);
4067 }
4068
4069 if (http_parse_req(con))
4070 return (TRUE);
4071
4072 con_cleanup:
4073 con->evnt->io_r->conf->malloc_bad = FALSE;
4074 con->evnt->io_w->conf->malloc_bad = FALSE;
4075
4076 return (FALSE);
4077 }
4078
httpd_con_init(struct Con * con,struct Acpt_listener * acpt_listener)4079 int httpd_con_init(struct Con *con, struct Acpt_listener *acpt_listener)
4080 {
4081 int ret = TRUE;
4082
4083 con->mpbr_ct = NULL;
4084
4085 con->fs = con->fs_store;
4086 con->fs->len = 0;
4087 con->fs->fd = -1;
4088 con->fs_off = 0;
4089 con->fs_num = 0;
4090 con->fs_sz = 1;
4091
4092 con->vary_star = FALSE;
4093 con->keep_alive = HTTP_NON_KEEP_ALIVE;
4094 con->acpt_sa_ref = vstr_ref_add(acpt_listener->ref);
4095 con->use_mpbr = FALSE;
4096 con->use_mmap = FALSE;
4097
4098 con->parsed_method_ver_1_0 = FALSE;
4099
4100 httpd_policy_change_con(con, (Httpd_policy_opts *)httpd_opts->s->def_policy);
4101
4102 if (!httpd_policy_connection(con,
4103 httpd_opts->conf, httpd_opts->match_connection))
4104 {
4105 Vstr_base *s1 = con->policy->s->policy_name;
4106 Vstr_base *s2 = httpd_opts->conf->tmp;
4107
4108 vlg_info(vlg, "CONF-MAIN-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
4109 " backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
4110 ret = FALSE;
4111 }
4112 else if (con->evnt->flag_q_closed)
4113 {
4114 Vstr_base *s1 = con->policy->s->policy_name;
4115 vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
4116 CON_CEVNT_SA(con), s1);
4117 ret = FALSE;
4118 }
4119
4120 return (ret);
4121 }
4122