httpd_req.c
#define EX_UTILS_NO_FUNCS 1
#include "ex_utils.h"
#include "mk.h"
#include "vlg.h"
#define HTTPD_HAVE_GLOBAL_OPTS 1
#include "httpd.h"
#include "httpd_policy.h"
#include <syslog.h>
#if ! HTTPD_CONF_USE_MIME_XATTR
# define getxattr(w, x, y, z) (errno = ENOSYS, -1)
#else
# include <sys/xattr.h>
#endif
#if ! COMPILE_DEBUG
# define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024)
# define HTTP_CONF_SAFE_PRINT_REQ TRUE
#else
# define HTTP_CONF_MMAP_LIMIT_MIN 8
# define HTTP_CONF_SAFE_PRINT_REQ FALSE
#endif
#define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
#define HTTPD_CONF_ZIP_LIMIT_MIN 8
#define CLEN COMPILE_STRLEN
#define VPREFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
#define VSUFFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
#define VIPREFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
#define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
#define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
#define HTTP__HDR_SET(req, h, p, l) do { \
(req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
(req)-> http_hdrs -> hdr_ ## h ->len = (l); \
} while (FALSE)
#define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
(req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
(req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
} while (FALSE)
#define HTTP__XTRA_HDR_INIT(x) do { \
req-> x ## _vs1 = NULL; \
req-> x ## _pos = 0; \
req-> x ## _len = 0; \
} while (FALSE)
static Vlg *vlg = NULL;
void httpd_req_init(Vlg *passed_vlg)
{
ASSERT(passed_vlg && !vlg);
vlg = passed_vlg;
}
void httpd_req_exit(void)
{
ASSERT(vlg);
vlg = NULL;
}
Httpd_req_data *http_req_make(struct Con *con)
{
static Httpd_req_data real_req[1];
Httpd_req_data *req = real_req;
const Httpd_policy_opts *policy = NULL;
ASSERT(!req->using_req);
if (!req->done_once)
{
Vstr_conf *conf = NULL;
if (con)
conf = con->evnt->io_w->conf;
if (!(req->fname = vstr_make_base(conf)) ||
!(req->http_hdrs->multi->combiner_store = vstr_make_base(conf)) ||
!(req->sects = vstr_sects_make(8)) ||
!(req->tag = vstr_make_base(conf)))
return (NULL);
req->f_mmap = NULL;
req->xtra_content = NULL;
}
http_parse_clear_hdrs(req);
req->http_hdrs->multi->comb = con ? con->evnt->io_r : NULL;
vstr_del(req->fname, 1, req->fname->len);
req->now = evnt_sc_time();
req->len = 0;
req->path_pos = 0;
req->path_len = 0;
req->error_code = 0;
req->error_line = "";
req->error_msg = "";
req->error_xmsg = "";
req->error_len = 0;
req->fs_len = 0;
req->f_stat_st_size = 0;
req->sects->num = 0;
if (con)
req->orig_io_w_len = con->evnt->io_w->len;
ASSERT(!req->f_mmap || !req->f_mmap->len);
if (req->f_mmap)
vstr_del(req->f_mmap, 1, req->f_mmap->len);
ASSERT(!req->xtra_content || !req->xtra_content->len);
if (req->xtra_content)
vstr_del(req->xtra_content, 1, req->xtra_content->len);
req->vhost_prefix_len = 0;
req->sects->malloc_bad = FALSE;
req->parsed_content_encoding = FALSE;
req->content_encoding_identity = TRUE;
req->content_encoding_gzip = FALSE;
req->content_encoding_bzip2 = FALSE;
req->content_encoding_xgzip = FALSE;
req->user_return_error_code = FALSE;
req->vary_star = con ? con->vary_star : FALSE;
req->vary_a = FALSE;
req->vary_ac = FALSE;
req->vary_ae = FALSE;
req->vary_al = FALSE;
req->vary_rf = FALSE;
req->vary_ua = FALSE;
req->vary_ims = FALSE;
req->vary_ius = FALSE;
req->vary_ir = FALSE;
req->vary_im = FALSE;
req->vary_inm = FALSE;
req->vary_xm = FALSE;
req->direct_uri = FALSE;
req->direct_filename = FALSE;
req->skip_document_root = FALSE;
req->ver_0_9 = FALSE;
req->ver_1_1 = FALSE;
req->ver_1_x = FALSE;
req->head_op = FALSE;
req->chked_encoded_path = FALSE;
req->neg_content_type_done = FALSE;
req->neg_content_lang_done = FALSE;
req->conf_secure_dirs = FALSE;
req->conf_friendly_file = FALSE;
req->conf_friendly_dirs = FALSE;
req->done_once = TRUE;
req->using_req = TRUE;
req->malloc_bad = FALSE;
if (con && !vstr_sub_vstr(req->tag, 1, req->tag->len,
con->tag, 1, con->tag->len, VSTR_TYPE_SUB_BUF_REF))
return (NULL);
if (!con)
req->policy = NULL;
else
{
policy = con->policy;
httpd_policy_change_req(con, req, policy);
}
return (req);
}
void http_req_free(Httpd_req_data *req)
{
if (!req)
return;
ASSERT(req->done_once && req->using_req);
vstr_del(req->fname, 1, req->fname->len);
ASSERT(!req->http_hdrs->multi->combiner_store->len);
if (req->f_mmap)
vstr_del(req->f_mmap, 1, req->f_mmap->len);
req->http_hdrs->multi->comb = NULL;
req->using_req = FALSE;
}
void http_req_exit(void)
{
Httpd_req_data *req = http_req_make(NULL);
struct Http_hdrs__multi *tmp = NULL;
if (!req)
return;
tmp = req->http_hdrs->multi;
vstr_free_base(req->fname); req->fname = NULL;
vstr_free_base(tmp->combiner_store); tmp->combiner_store = NULL;
vstr_free_base(req->f_mmap); req->f_mmap = NULL;
vstr_free_base(req->xtra_content); req->xtra_content = NULL;
vstr_sects_free(req->sects); req->sects = NULL;
vstr_free_base(req->tag); req->tag = NULL;
req->done_once = FALSE;
req->using_req = FALSE;
}
void httpd_req_absolute_uri(struct Con *con, Httpd_req_data *req,
Vstr_base *lfn, size_t pos, size_t len)
{
Vstr_base *data = con->evnt->io_r;
size_t apos = pos - 1;
size_t alen = lfn->len;
int has_schema = TRUE;
int has_abs_path = TRUE;
int has_data = TRUE;
unsigned int prev_num = 0;
if (!VPREFIX(lfn, pos, len, "http://"))
{
has_schema = FALSE;
if (!VPREFIX(lfn, pos, len, "/"))
{
if (VPREFIX(lfn, pos, len, "./"))
{
has_data = TRUE;
vstr_del(lfn, pos, CLEN("./")); len -= CLEN("./");
alen = lfn->len;
}
else
{
while (VPREFIX(lfn, pos, len, "../"))
{
++prev_num;
vstr_del(lfn, pos, CLEN("../")); len -= CLEN("../");
}
if (prev_num)
alen = lfn->len;
else
has_data = !!lfn->len;
}
has_abs_path = FALSE;
}
}
if (!has_schema)
{
vstr_add_cstr_buf(lfn, apos, "http://");
apos += lfn->len - alen;
alen = lfn->len;
httpd_sc_add_hostname(con, req, lfn, apos);
apos += lfn->len - alen;
}
if (!has_abs_path)
{
size_t path_len = req->path_len;
if (has_data || prev_num)
{
path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
while (path_len && prev_num--)
{
path_len -= vstr_spn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
}
if (!path_len) path_len = 1;
}
vstr_add_vstr(lfn, apos, data, req->path_pos, path_len, VSTR_TYPE_ADD_DEF);
}
}
int http_req_chk_dir(struct Con *con, Httpd_req_data *req, const char *xmsg)
{
Vstr_base *fname = req->fname;
ASSERT(fname->len);
if (req->policy->use_secure_dirs && !req->conf_secure_dirs)
{
const char *fname_cstr = NULL;
struct stat64 d_stat[1];
vstr_add_cstr_buf(fname, fname->len, "/");
HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
if ((stat64(fname_cstr, d_stat) == -1) || !S_ISREG(d_stat->st_mode))
HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
}
vstr_del(fname, 1, fname->len);
httpd_req_absolute_uri(con, req, fname, 1, fname->len);
if (fname->len && (vstr_export_chr(fname, fname->len) == '/'))
HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
vstr_add_cstr_buf(fname, fname->len, "/");
HTTPD_REDIR_MSG(req, 301, "dir -> filename");
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
return (http_fin_err_req(con, req));
}
int http_req_chk_file(struct Con *con, Httpd_req_data *req, const char *xmsg)
{
Vstr_base *fname = req->fname;
size_t len = 0;
ASSERT(fname->len);
if (!req->policy->use_friendly_dirs)
HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
else if (!req->conf_friendly_dirs)
{
const char *fname_cstr = NULL;
struct stat64 d_stat[1];
len = vstr_cspn_cstr_chrs_rev(fname, 1, fname->len, "/") + 1;
if ((len <= 1) || (len >= (fname->len - req->vhost_prefix_len)))
HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
vstr_sc_reduce(fname, 1, fname->len, len);
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
if ((stat64(fname_cstr, d_stat) == -1) && !S_ISREG(d_stat->st_mode))
HTTPD_ERR_MSG_RET(req, 404, xmsg, http_fin_err_req(con, req));
}
vstr_sub_cstr_ptr(fname, 1, fname->len, "./");
httpd_req_absolute_uri(con, req, fname, 1, fname->len);
assert(VSUFFIX(fname, 1, fname->len, "/"));
vstr_sc_reduce(fname, 1, fname->len, strlen("/"));
HTTPD_REDIR_MSG(req, 301, "filename -> dir");
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
return (http_fin_err_req(con, req));
}
void http_req_split_method(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *s1 = con->evnt->io_r;
size_t pos = 1;
size_t len = req->len;
size_t el = 0;
size_t skip_len = 0;
unsigned int orig_num = req->sects->num;
el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL);
ASSERT(el >= pos);
len = el - pos;
if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
return;
vstr_sects_add(req->sects, pos, el - pos);
len -= (el - pos); pos += (el - pos);
if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{ len -= skip_len; pos += skip_len; }
if (!len)
goto req_line_parse_err;
if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{
vstr_sects_add(req->sects, pos, len);
len = 0;
}
else
{
vstr_sects_add(req->sects, pos, el - pos);
len -= (el - pos); pos += (el - pos);
if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{ len -= skip_len; pos += skip_len; }
}
if (len)
vstr_sects_add(req->sects, pos, len);
else if (!req->policy->allow_http_0_9)
return;
else
req->ver_0_9 = TRUE;
if (req->sects->malloc_bad)
goto req_line_parse_err;
return;
req_line_parse_err:
req->sects->num = orig_num;
}
void http_req_split_hdrs(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *s1 = con->evnt->io_r;
size_t pos = 1;
size_t len = req->len;
size_t el = 0;
size_t hpos = 0;
ASSERT(req->sects->num >= 3);
el = (VSTR_SECTS_NUM(req->sects, req->sects->num)->pos +
VSTR_SECTS_NUM(req->sects, req->sects->num)->len);
assert(VEQ(s1, el, CLEN(HTTP_EOL), HTTP_EOL));
len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
if (VPREFIX(s1, pos, len, HTTP_EOL))
return;
ASSERT(vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_END_OF_REQUEST));
hpos = pos;
while ((el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL)) != pos)
{
char chr = 0;
len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
chr = vstr_export_chr(s1, pos);
if (chr == ' ' || chr == '\t')
continue;
vstr_sects_add(req->sects, hpos, el - hpos);
hpos = pos;
}
}
int http_req_content_type(Httpd_req_data *req)
{
const Vstr_base *vs1 = NULL;
size_t pos = 0;
size_t len = 0;
if (req->content_type_vs1)
return (TRUE);
if (req->policy->use_mime_xattr)
{
static const char key[] = "user.mime_type";
char buf[1024];
ssize_t ret = -1;
const char *fname_cstr = NULL;
if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL)))
return (FALSE);
fname_cstr = vstr_export_cstr_ptr(req->fname, 1, req->fname->len);
ASSERT(fname_cstr);
if ((ret = getxattr(fname_cstr, key, buf, sizeof(buf))) != -1)
{
pos = req->xtra_content->len + 1;
len = ret;
if (!vstr_add_buf(req->xtra_content, req->xtra_content->len, buf, len))
return (FALSE);
req->content_type_vs1 = req->xtra_content;
req->content_type_pos = pos;
req->content_type_len = len;
HTTP_REQ__X_CONTENT_HDR_CHK(content_type);
return (TRUE);
}
else if (errno == ENOSYS)
httpd_disable_getxattr();
else if ((errno == EOPNOTSUPP) || (errno == EPERM))
{
static time_t last = -1;
time_t now = evnt_sc_time();
if ((last == -1) || (difftime(last, now) > (10 * 60)))
{
vlg_warn(vlg, "getxattr($<vstr.all:%p>, %s): %m\n", req->fname, key);
last = now;
}
}
}
mime_types_match(req->policy->mime_types,
req->fname, 1, req->fname->len, &vs1, &pos, &len);
if (!len)
{
req->parse_accept = FALSE;
return (TRUE);
}
if ((vstr_export_chr(vs1, pos) == '/') && (len > 2) &&
(vstr_export_chr(vs1, vstr_sc_poslast(pos, len)) == '/'))
{
size_t num_len = 1;
static const char xmsg[] = "Mime/Type";
len -= 2;
++pos;
req->user_return_error_code = TRUE;
req->direct_filename = FALSE;
switch (vstr_parse_uint(vs1, pos, len, 0, &num_len, NULL))
{
case 400: if (num_len == len) HTTPD_ERR_MSG_RET(req, 400, xmsg, FALSE);
case 403: if (num_len == len) HTTPD_ERR_MSG_RET(req, 403, xmsg, FALSE);
case 404: if (num_len == len) HTTPD_ERR_MSG_RET(req, 404, xmsg, FALSE);
case 410: if (num_len == len) HTTPD_ERR_MSG_RET(req, 410, xmsg, FALSE);
case 500: if (num_len == len) HTTPD_ERR_MSG_RET(req, 500, xmsg, FALSE);
case 503: if (num_len == len) HTTPD_ERR_MSG_RET(req, 503, xmsg, FALSE);
default:
req->user_return_error_code = FALSE;
return (TRUE);
}
}
req->content_type_vs1 = vs1;
req->content_type_pos = pos;
req->content_type_len = len;
return (TRUE);
}
static void httpd__req_etag_hex_num(Vstr_base *vs1, uintmax_t val, int more)
{
char buf[(sizeof(uintmax_t) * CHAR_BIT) + 1];
size_t len = 0;
len = vstr_sc_conv_num_uintmax(buf, sizeof(buf), val, "0123456789abcdef", 16);
vstr_add_buf(vs1, vs1->len, buf, len);
if (more)
vstr_add_cstr_buf(vs1, vs1->len, "-");
}
static int httpd__req_etag_auto(struct Httpd_req_data *req)
{
size_t xpos = 0;
Vstr_base *vs1 = NULL;
ASSERT(!req->etag_vs1 && req->policy->etag_auto_type);
if (!(xpos = http_req_xtra_content(req, NULL, 0, &req->etag_len)))
return (FALSE);
vs1 = req->xtra_content;
if (difftime(req->now, req->f_stat->st_mtime) <= 1)
vstr_add_cstr_buf(vs1, vs1->len, "W/");
vstr_add_cstr_buf(vs1, vs1->len, "\"");
switch (req->policy->etag_auto_type)
{
case HTTPD_ETAG_TYPE_AUTO_DISM:
httpd__req_etag_hex_num(vs1, req->f_stat->st_dev, TRUE);
case HTTPD_ETAG_TYPE_AUTO_ISM:
httpd__req_etag_hex_num(vs1, req->f_stat->st_ino, TRUE);
case HTTPD_ETAG_TYPE_AUTO_SM:
httpd__req_etag_hex_num(vs1, req->f_stat->st_size, TRUE);
httpd__req_etag_hex_num(vs1, req->f_stat->st_mtime, FALSE);
ASSERT_NO_SWITCH_DEF();
}
vstr_add_cstr_buf(vs1, vs1->len, "\"");
req->etag_vs1 = vs1;
req->etag_pos = xpos;
req->etag_len = (req->xtra_content->len - xpos) + 1;
return (!vs1->conf->malloc_bad);
}
#define HTTPD__HD_EQ(x) \
VEQ(hdrs, h_ ## x ->pos, h_ ## x ->len, date)
static int http_response_ok(struct Con *con, struct Httpd_req_data *req,
unsigned int *http_ret_code,
const char ** http_ret_line)
{
const Vstr_base *hdrs = con->evnt->io_r;
time_t mtime = req->f_stat->st_mtime;
Vstr_sect_node *h_ims = req->http_hdrs->hdr_if_modified_since;
Vstr_sect_node *h_ir = req->http_hdrs->hdr_if_range;
Vstr_sect_node *h_iums = req->http_hdrs->hdr_if_unmodified_since;
Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
Vstr_base *comb = req->http_hdrs->multi->comb;
Vstr_sect_node *h_im = req->http_hdrs->multi->hdr_if_match;
Vstr_sect_node *h_inm = req->http_hdrs->multi->hdr_if_none_match;
int h_ir_tst = FALSE;
int h_iums_tst = FALSE;
int req_if_range = FALSE;
int cached_output = FALSE;
const char *date = NULL;
if (HTTPD_VER_GE_1_1(req) && h_iums->pos)
h_iums_tst = TRUE;
if (HTTPD_VER_GE_1_1(req) && h_ir->pos && h_r->pos)
h_ir_tst = TRUE;
if (difftime(req->now, mtime) > 0)
{
Date_store *ds = httpd_opts->date;
date = date_rfc1123(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
date = date_rfc850(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
date = date_asctime(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
}
if (HTTPD_VER_GE_1_1(req))
{
const Vstr_base *vs1 = NULL;
size_t pos = 0;
size_t len = 0;
if (req->policy->etag_auto_type && !req->etag_vs1)
{
if (!httpd__req_etag_auto(req))
{
con->evnt->io_w->conf->malloc_bad = TRUE;
return (TRUE);
}
req->etag_time = mtime;
}
if (req->etag_vs1 && (req->etag_time >= mtime))
{
vs1 = req->etag_vs1;
pos = req->etag_pos;
len = req->etag_len;
}
if (h_ir_tst && !req_if_range &&
httpd_match_etags(req,
hdrs, h_ir->pos, h_ir->len, vs1, pos, len, FALSE))
req_if_range = TRUE;
if (h_ir_tst && !req_if_range)
h_r->pos = 0;
if (h_inm->pos && (VEQ(hdrs, h_inm->pos, h_inm->len, "*") ||
httpd_match_etags(req, comb, h_inm->pos, h_inm->len,
vs1, pos, len, !h_r->pos)))
cached_output = TRUE;
if (!cached_output &&
h_im->pos && !(VEQ(hdrs, h_im->pos, h_im->len, "*") ||
httpd_match_etags(req, comb, h_im->pos, h_im->len,
vs1, pos, len, FALSE)))
return (FALSE);
}
else if (h_ir_tst && !req_if_range)
h_r->pos = 0;
if (cached_output)
{
req->head_op = TRUE;
*http_ret_code = 304;
*http_ret_line = "Not Modified";
}
else if (h_r->pos)
{
*http_ret_code = 206;
*http_ret_line = "Partial content";
}
return (TRUE);
}
int http_req_1_x(struct Con *con, Httpd_req_data *req,
unsigned int *http_ret_code,
const char **http_ret_line)
{
Vstr_base *out = con->evnt->io_w;
Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
time_t mtime = -1;
if (HTTPD_VER_GE_1_1(req) && req->http_hdrs->hdr_expect->len)
HTTPD_ERR_RET(req, 417, FALSE);
httpd_parse_sc_try_fd_encoding(con, req, req->f_stat, &req->f_stat_st_size,
req->fname);
if (req->policy->use_err_406 &&
!req->content_encoding_identity &&
!req->content_encoding_bzip2 && !req->content_encoding_gzip)
HTTPD_ERR_MSG_RET(req, 406, "Encoding", FALSE);
if (h_r->pos)
{
int ret_code = 0;
if (!(req->policy->use_range &&
(HTTPD_VER_GE_1_1(req) || req->policy->use_range_1_0)))
h_r->pos = 0;
else if (!(ret_code = http_parse_range(con, req)))
h_r->pos = 0;
ASSERT(!ret_code || (ret_code == 200) || (ret_code == 416));
if (ret_code == 416)
{
if (!req->http_hdrs->hdr_if_range->pos)
HTTPD_ERR_RET(req, 416, FALSE);
h_r->pos = 0;
}
}
if (!http_response_ok(con, req, http_ret_code, http_ret_line))
HTTPD_ERR_RET(req, 412, FALSE);
if (!h_r->pos)
httpd_serv_file_sects_none(con, req, req->f_stat_st_size);
httpd_serv_call_file_init(con, req, http_ret_code, http_ret_line);
ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
ASSERT(!con->fs_off);
mtime = req->f_stat->st_mtime;
http_app_def_hdrs(con, req, *http_ret_code, *http_ret_line,
mtime, NULL, TRUE, http_serv_file_len(con, req));
if (h_r->pos && !con->use_mpbr)
http_app_hdr_fmt(out, "Content-Range", "%s %ju-%ju/%ju", "bytes",
con->fs->off, con->fs->off + (con->fs->len - 1),
(uintmax_t)req->f_stat_st_size);
if (req->content_location_vs1)
http_app_hdr_vstr_def(out, "Content-Location",
HTTP__XTRA_HDR_PARAMS(req, content_location));
http_app_hdrs_url(con, req);
http_app_hdrs_file(con, req);
http_app_end_hdrs(out);
if (!req->head_op && h_r->pos && con->use_mpbr)
{
con->mpbr_fs_len = req->f_stat_st_size;
http_app_hdrs_mpbr(con, con->fs);
}
return (TRUE);
}
static int httpd_req_add_vhost(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *fname = req->fname;
Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
size_t h_h_pos = h_h->pos;
size_t h_h_len = h_h->len;
size_t orig_len = 0;
size_t dots = 0;
if (h_h_len)
{
dots = vstr_spn_cstr_chrs_rev(data, h_h_pos, h_h_len, ".");
if (dots == h_h_len)
h_h_len = 1;
else
{
ASSERT(dots < h_h_len);
h_h_len -= dots;
if (req->policy->use_canonize_host)
{
if (VIPREFIX(data, h_h_pos, h_h_len, "www."))
{ h_h_len -= CLEN("www."); h_h_pos += CLEN("www."); }
}
}
h_h->pos = h_h_pos;
h_h->len = h_h_len;
}
if (!req->policy->use_vhosts_name)
return (TRUE);
orig_len = fname->len;
if (!http_serv_add_vhost(con, req, fname, 0, TRUE))
return (FALSE);
vstr_add_cstr_ptr(fname, 0, "/");
req->vhost_prefix_len = (fname->len - orig_len);
return (TRUE);
}
int http_req_make_path(struct Con *con, Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *fname = req->fname;
ASSERT(!fname->len);
assert(VPREFIX(data, req->path_pos, req->path_len, "/") ||
VEQ(data, req->path_pos, req->path_len, "*"));
if (req->chk_encoded_slash &&
vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2f"))
HTTPD_ERR_MSG_RET(req, 403, "Path has encoded /", FALSE);
if (req->chk_encoded_dot &&
vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2e"))
HTTPD_ERR_MSG_RET(req, 403, "Path has encoded .", FALSE);
req->chked_encoded_path = TRUE;
vstr_add_vstr(fname, 0,
data, req->path_pos, req->path_len, VSTR_TYPE_ADD_BUF_PTR);
vstr_conv_decode_uri(fname, 1, fname->len);
if (fname->conf->malloc_bad)
return (TRUE);
if (!httpd_req_add_vhost(con, req))
return (FALSE);
if (vstr_srch_chr_fwd(fname, 1, fname->len, 0))
HTTPD_ERR_MSG_RET(req, 403, "Path has NIL", FALSE);
if (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/../") ||
VSUFFIX(req->fname, 1, req->fname->len, "/.."))
HTTPD_ERR_MSG_RET(req, 403, "Path has /../", FALSE);
if (req->policy->chk_dot_dir &&
(vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/./") ||
VSUFFIX(req->fname, 1, req->fname->len, "/.")))
HTTPD_ERR_MSG_RET(req, 403, "Path has /./", FALSE);
ASSERT(fname->len);
assert(VPREFIX(fname, 1, fname->len, "/") ||
VEQ(fname, 1, fname->len, "*") ||
fname->conf->malloc_bad);
if (fname->conf->malloc_bad)
return (TRUE);
return (TRUE);
}
size_t http_req_xtra_content(Httpd_req_data *req, const Vstr_base *s1,
size_t pos, size_t *len)
{
Vstr_base *xs1 = req->xtra_content;
size_t ret = 0;
ASSERT(len);
ASSERT((s1 == xs1) || !s1);
ASSERT(s1 || !*len);
if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL)))
return (0);
xs1 = req->xtra_content;
if (!s1 || !*len)
return (xs1->len + 1);
if (vstr_sc_poslast(pos, *len) == xs1->len)
return (pos);
ret = xs1->len + 1;
if (!vstr_add_vstr(xs1, xs1->len, s1, pos, *len, VSTR_TYPE_ADD_BUF_REF))
return (0);
return (ret);
}