[mod_ssi] basic recursive SSI include virtual (fixes #536)

EXPERIMENTAL: basic recursive SSI <!--#include virtual="..." -->
Marked experimental since behavior may change in future.

Prior behavior was simpler and treated them all as files included as-is.

New behavior treats all #include virtual="..." targets as SSI files.

In the future, this may change to be a full recursive subrequest and the
virtual path may be treated as a new subrequest and might be something
other than SSI (e.g. might be CGI).  This has not been implemented.

Current behavior processes <!--#include virtual="..." --> as static file
Enable new behavior by setting ssi.recursion-max to value other than 0.
ssi.recursion-max = X to set maximum recusion depth

x-ref:
  "add recursion to the SSI #include directive"
  https://redmine.lighttpd.net/issues/536
This commit is contained in:
Glenn Strauss 2016-12-11 01:49:11 -05:00
parent 879ce0b534
commit 185e262bf5
2 changed files with 91 additions and 25 deletions

View File

@ -116,6 +116,7 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
{ "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
{ "ssi.conditional-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ "ssi.exec", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ "ssi.recursion-max", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
@ -132,11 +133,13 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
s->content_type = buffer_init();
s->conditional_requests = 0;
s->ssi_exec = 1;
s->ssi_recursion_max = 0;
cv[0].destination = s->ssi_extension;
cv[1].destination = s->content_type;
cv[2].destination = &(s->conditional_requests);
cv[3].destination = &(s->ssi_exec);
cv[4].destination = &(s->ssi_recursion_max);
p->config_storage[i] = s;
@ -190,6 +193,8 @@ static int build_ssi_cgi_vars(server *srv, connection *con, handler_ctx *p) {
return 0;
}
static int mod_ssi_process_file(server *srv, connection *con, handler_ctx *p, struct stat *st);
static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const char **l, size_t n, struct stat *st) {
/**
@ -255,7 +260,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const
char buf[255];
buffer *b = NULL;
struct {
static const struct {
const char *var;
enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD,
SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF,
@ -291,7 +296,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const
/* int enc = 0; */
const char *var_val = NULL;
struct {
static const struct {
const char *var;
enum {
SSI_ECHO_UNSET,
@ -318,7 +323,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const
};
/*
struct {
static const struct {
const char *var;
enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type;
} encvars[] = {
@ -607,12 +612,56 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const
}
break;
case SSI_INCLUDE:
chunkqueue_append_file(con->write_queue, p->stat_fn, 0, stb.st_size);
/* Keep the newest mtime of included files */
if (stb.st_mtime > include_file_last_mtime)
include_file_last_mtime = stb.st_mtime;
if (file_path || 0 == p->conf.ssi_recursion_max) {
/* don't process if #include file="..." is used */
chunkqueue_append_file(con->write_queue, p->stat_fn, 0, stb.st_size);
} else {
buffer *upsave, *ppsave, *prpsave;
/* only allow predefined recursion depth */
if (p->ssi_recursion_depth >= p->conf.ssi_recursion_max) {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(error: include directives recurse deeper than pre-defined ssi.recursion-max)"));
break;
}
/* prevents simple infinite loop */
if (buffer_is_equal(con->physical.path, p->stat_fn)) {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(error: include directives create an infinite loop)"));
break;
}
/* save and restore con->physical.path, con->physical.rel_path, and con->uri.path around include
*
* srv->tmp_buf contains url-decoded, path-simplified, and lowercased (if con->conf.force_lowercase) uri path of target.
* con->uri.path and con->physical.rel_path are set to the same since we only operate on filenames here,
* not full re-run of all modules for subrequest */
upsave = con->uri.path;
ppsave = con->physical.path;
prpsave = con->physical.rel_path;
con->physical.path = p->stat_fn;
p->stat_fn = buffer_init();
con->uri.path = con->physical.rel_path = buffer_init_buffer(srv->tmp_buf);
/*(ignore return value; muddle along as best we can if error occurs)*/
++p->ssi_recursion_depth;
mod_ssi_process_file(srv, con, p, &stb);
--p->ssi_recursion_depth;
buffer_free(con->uri.path);
con->uri.path = upsave;
con->physical.rel_path = prpsave;
buffer_free(p->stat_fn);
p->stat_fn = con->physical.path;
con->physical.path = ppsave;
}
break;
}
} else {
@ -1094,14 +1143,16 @@ static int mod_ssi_stmt_len(const char *s, const int len) {
return 0; /* incomplete directive "<!--#...-->" */
}
static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, int fd, struct stat *st) {
static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, struct stat *st, int fd) {
ssize_t rd;
size_t offset, pretag;
char buf[8192];
size_t bufsz = 8192;
char *buf = malloc(bufsz); /* allocate to reduce chance of stack exhaustion upon deep recursion */
force_assert(buf);
offset = 0;
pretag = 0;
while (0 < (rd = read(fd, buf+offset, sizeof(buf)-offset))) {
while (0 < (rd = read(fd, buf+offset, bufsz-offset))) {
char *s;
size_t prelen = 0, len;
offset += (size_t)rd;
@ -1123,7 +1174,7 @@ static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, int fd
offset = pretag = 0;
break;
}
} else if (0 == prelen && offset == sizeof(buf)) { /*(full buf)*/
} else if (0 == prelen && offset == bufsz) { /*(full buf)*/
/* SSI statement is way too long
* NOTE: skipping this buf will expose *the rest* of this SSI statement */
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("<!-- [an error occurred: directive too long] "));
@ -1151,7 +1202,7 @@ static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, int fd
}
/* loop to look for next '<' */
}
if (offset == sizeof(buf)) {
if (offset == bufsz) {
if (!p->if_is_false) {
chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag);
}
@ -1169,6 +1220,8 @@ static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, int fd
chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag);
}
}
free(buf);
}
@ -1179,8 +1232,29 @@ static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, int fd
# define FIFO_NONBLOCK 0
#endif
static int mod_ssi_process_file(server *srv, connection *con, handler_ctx *p, struct stat *st) {
int fd = open(con->physical.path->ptr, O_RDONLY | FIFO_NONBLOCK);
if (-1 == fd) {
log_error_write(srv, __FILE__, __LINE__, "SsB", "open(): ",
strerror(errno), con->physical.path);
return -1;
}
if (0 != fstat(fd, st)) {
log_error_write(srv, __FILE__, __LINE__, "SsB", "fstat(): ",
strerror(errno), con->physical.path);
close(fd);
return -1;
}
mod_ssi_read_fd(srv, con, p, st, fd);
close(fd);
return 0;
}
static int mod_ssi_handle_request(server *srv, connection *con, handler_ctx *p) {
int fd;
struct stat st;
/* get a stream to the file */
@ -1193,21 +1267,8 @@ static int mod_ssi_handle_request(server *srv, connection *con, handler_ctx *p)
/* Reset the modified time of included files */
include_file_last_mtime = 0;
if (-1 == (fd = open(con->physical.path->ptr, O_RDONLY | FIFO_NONBLOCK))) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"open: ", con->physical.path);
return -1;
}
mod_ssi_process_file(srv, con, p, &st);
if (0 != fstat(fd, &st)) {
log_error_write(srv, __FILE__, __LINE__, "SB", "fstat failed: ", con->physical.path);
close(fd);
return -1;
}
mod_ssi_read_fd(srv, con, p, fd, &st);
close(fd);
con->file_started = 1;
con->file_finished = 1;
@ -1258,6 +1319,7 @@ static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p
PATCH(content_type);
PATCH(conditional_requests);
PATCH(ssi_exec);
PATCH(ssi_recursion_max);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
@ -1279,6 +1341,8 @@ static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p
PATCH(conditional_requests);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.exec"))) {
PATCH(ssi_exec);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.recursion-max"))) {
PATCH(ssi_recursion_max);
}
}
}

View File

@ -15,6 +15,7 @@ typedef struct {
buffer *content_type;
unsigned short conditional_requests;
unsigned short ssi_exec;
unsigned short ssi_recursion_max;
} plugin_config;
typedef struct {
@ -42,6 +43,7 @@ typedef struct {
array *ssi_cgi_env;
int if_level, if_is_false_level, if_is_false, if_is_false_endif;
unsigned short ssi_recursion_depth;
plugin_config conf;
} handler_ctx;