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