diff options
author | Jan Kneschke <jan@kneschke.de> | 2005-08-09 06:42:33 +0000 |
---|---|---|
committer | Jan Kneschke <jan@kneschke.de> | 2005-08-09 06:42:33 +0000 |
commit | 75c3a8393a06bcdbf7d9bbfc627728619ae93261 (patch) | |
tree | eabea4f43f18d4bbcc5de838cb9186a3dc906397 | |
parent | 360aba360f5bb6c8db7f98717d67c08448b7a111 (diff) | |
download | lighttpd-git-75c3a8393a06bcdbf7d9bbfc627728619ae93261.tar.gz |
added include_shell option to configfiles (merged the rest of the trunk changesets)
git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x@530 152afb58-edef-0310-8abb-c4023f1b3aa9
-rw-r--r-- | configure.in | 138 | ||||
-rw-r--r-- | doc/configuration.txt | 4 | ||||
-rw-r--r-- | doc/lighttpd.conf | 5 | ||||
-rw-r--r-- | src/.cvsignore | 2 | ||||
-rw-r--r-- | src/Makefile.am | 14 | ||||
-rw-r--r-- | src/array.h | 1 | ||||
-rw-r--r-- | src/base.h | 11 | ||||
-rw-r--r-- | src/configfile-glue.c | 94 | ||||
-rw-r--r-- | src/configfile.c | 135 | ||||
-rw-r--r-- | src/configfile.h | 1 | ||||
-rw-r--r-- | src/configparser.y | 43 | ||||
-rw-r--r-- | src/connections.c | 18 | ||||
-rw-r--r-- | src/data_config.c | 11 | ||||
-rw-r--r-- | src/keyvalue.c | 20 | ||||
-rw-r--r-- | src/keyvalue.h | 3 | ||||
-rw-r--r-- | src/mod_alias.c | 26 | ||||
-rw-r--r-- | src/mod_redirect.c | 28 | ||||
-rw-r--r-- | src/mod_rewrite.c | 13 | ||||
-rw-r--r-- | src/plugin.c | 2 | ||||
-rw-r--r-- | src/plugin.h | 1 | ||||
-rw-r--r-- | src/proc_open.c | 386 | ||||
-rw-r--r-- | src/proc_open.h | 25 | ||||
-rw-r--r-- | src/response.c | 5 | ||||
-rw-r--r-- | src/stream.c | 1 | ||||
-rwxr-xr-x | tests/LightyTest.pm | 8 | ||||
-rwxr-xr-x | tests/core-var-include.t | 33 | ||||
-rw-r--r-- | tests/docroot/www/pathinfo.php | 3 | ||||
-rw-r--r-- | tests/lighttpd.conf | 23 | ||||
-rwxr-xr-x | tests/mod-fastcgi.t | 10 | ||||
-rwxr-xr-x | tests/mod-redirect.t | 27 | ||||
-rw-r--r-- | tests/var-include-sub.conf | 6 |
31 files changed, 933 insertions, 164 deletions
diff --git a/configure.in b/configure.in index 59d059d1..d4e3d4ed 100644 --- a/configure.in +++ b/configure.in @@ -96,7 +96,7 @@ AC_ARG_WITH(mysql, if test \! -x $withval; then echo "--with-mysql=path-to-mysql_config" fi - if $withval | grep -- '--include' ; then + if $withval | grep -- '--include' > /dev/null ; then MYSQL_INCLUDE="`$withval --include | sed s/\'//g`" else MYSQL_INCLUDE="`$withval --cflags | sed s/\'//g`" @@ -364,6 +364,9 @@ AC_ARG_ENABLE(lfs, esac],[CPPFLAGS="${CPPFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES"]) AC_MSG_RESULT($enableval) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(off_t) + if test "x$ac_cv_func_sendfile" = xyes; then # check if sendfile works AC_MSG_CHECKING(if sendfile works) @@ -409,6 +412,7 @@ if test x$ipv6 = xtrue; then fi fi + AM_CONDITIONAL(CROSS_COMPILING, test "x$cross_compiling" = xyes) dnl check for fastcgi lib, for the tests only @@ -440,51 +444,121 @@ AC_CONFIG_FILES([Makefile debian/Makefile src/Makefile doc/Makefile tests/Makefi openwrt/Makefile openwrt/control openwrt/lighttpd.mk]) AC_OUTPUT -$ECHO -$ECHO "Plugins:" -$ECHO + +do_build="mod_cgi mod_fastcgi mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir" + +plugins="mod_rewrite mod_redirect mod_ssi" +features="regex-conditionals" if test ! "x$PCRE_LIB" = x; then - $ECHO "mod_rewrite : enabled" - $ECHO "mod_redirect : enabled" - $ECHO "mod_ssi : enabled" + do_build="$do_build $plugins" + enable_feature="$features" +else + no_build="$no_build $plugins" + disable_feature="$features" +fi + +plugins="mod_mysql_vhost" +if test ! "x$MYSQL_LIBS" = x; then + do_build="$do_build $plugins" else - $ECHO "mod_rewrite : disabled (libpcre missing)" - $ECHO "mod_redirect : disabled (libpcre missing)" - $ECHO "mod_ssi : disabled (libpcre missing)" + no_build="$no_build $plugins" fi -$ECHO "mod_cgi : enabled" -$ECHO "mod_fastcgi : enabled" -$ECHO "mod_proxy : enabled" -$ECHO "mod_evhost : enabled" -$ECHO "mod_simple_vhost: enabled" +plugins="mod_trigger_b4_dl" +if test ! "x$GDBM_LIB" = x; then + do_build="$do_build $plugins" +else + no_build="$no_build $plugins" +fi + + +features="compress-gzip compress-deflate" +if test ! "x$Z_LIB" = x; then + enable_feature="$enable_feature $features" +else + disable_feature="$disable_feature $features" +fi + +features="compress-bzip2" +if test ! "x$BZ_LIB" = x; then + enable_feature="$enable_feature $features" +else + disable_feature="$disable_feature $features" +fi -if test "x$MYSQL_LIBS" = x; then - $ECHO "mod_mysql_vhost : disabled (libmysqlclient missing or mysql support disabled)" +features="auth-ldap" +if test ! "x$LDAP_LIB" = x; then + enable_feature="$enable_feature $features" else - $ECHO "mod_mysql_vhost : enabled" + disable_feature="$disable_feature $features" fi -$ECHO "mod_access : enabled" -$ECHO "mod_alias : enabled" -$ECHO "mod_setenv : enabled" -$ECHO "mod_usertrack : enabled" -if test "x$Z_LIB" = x; then - $ECHO "mod_compress : disabled (libz missing)" +features="network-openssl" +if test ! "x$SSL_LIB" = x; then + enable_feature="$enable_feature $features" else - $ECHO "mod_compress : enabled" + disable_feature="$disable_feature $features" fi # no crypt call +features="auth-crypt" if test "$ac_cv_search_crypt" = no; then - $ECHO "mod_auth : enabled, crypt() support disabled" + disable_feature="$disable_feature $features" else - $ECHO "mod_auth : enabled" + enable_feature="$enable_feature $features" fi -$ECHO "mod_status : enabled" -$ECHO "mod_accesslog : enabled" -$ECHO "mod_rrdtool : enabled" -$ECHO "mod_secdownload : enabled" -$ECHO "mod_expire : enabled" + +features="network-ipv6" +if test "$ac_cv_ipv6_support" = yes; then + enable_feature="$enable_feature $features" +else + disable_feature="$disable_feature $features" +fi + +features="large-files" +if test "$enable_lfs" = yes; then + enable_feature="$enable_feature $features" +else + disable_feature="$disable_feature $features" +fi + +## post processing +do_build=`echo $do_build | sed 's/ /\n/g' | sort` +no_build=`echo $no_build | sed 's/ /\n/g' | sort` +enable_feature=`echo $enable_feature | sed 's/ /\n/g' | sort` +disable_feature=`echo $disable_feature | sed 's/ /\n/g' | sort` + +## output + +$ECHO +$ECHO "Plugins:" +$ECHO + +$ECHO "enabled: " +i=0 +for p in $do_build; do + $ECHO " $p" +done + +$ECHO "disabled: " +for p in $no_build; do + $ECHO " $p" +done + +$ECHO +$ECHO "Features:" +$ECHO + +$ECHO "enabled: " +i=0 +p="" +for p in $enable_feature; do + $ECHO " $p" +done + +$ECHO "disabled: " +for p in $disable_feature; do + $ECHO " $p" +done $ECHO diff --git a/doc/configuration.txt b/doc/configuration.txt index 66194973..ead31d0e 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -35,6 +35,7 @@ A BNF like notation: :: <boolean>: ( "enable" | "disable" ) <array> : "(" [ <string> "=>" ] <value> [, [ <string> "=>" ] <value> ]* ")" INCLUDE : "include" VALUE + INCLUDE_SHELL : "include_shell" STRING_VALUE Example ------- @@ -60,6 +61,9 @@ Example # include, relative to dirname of main config file include "mime.types.conf" + # read configuration from output of a command + include_shell "/usr/local/bin/confmimetype /etc/mime.types" + Conditional Configuration ========================= diff --git a/doc/lighttpd.conf b/doc/lighttpd.conf index 02464396..2e4b7cd9 100644 --- a/doc/lighttpd.conf +++ b/doc/lighttpd.conf @@ -301,3 +301,8 @@ $HTTP["url"] =~ "\.pdf$" { #include /etc/lighttpd/lighttpd-inc.conf ## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf" #include "lighttpd-inc.conf" + +#### include_shell +#include_shell "echo var.a=1" +## the above is same as: +#var.a=1 diff --git a/src/.cvsignore b/src/.cvsignore index 7e68d178..55061b0a 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -9,6 +9,8 @@ lighttpd .deps .libs array +proc_open +regex mod_ssi_exprparser.c mod_ssi_exprparser.h configparser.c diff --git a/src/Makefile.am b/src/Makefile.am index d8e58f5c..8236b82a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -noinst_PROGRAMS=array chunk lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license +noinst_PROGRAMS=proc_open lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license sbin_PROGRAMS=lighttpd bin_PROGRAMS=spawn-fcgi LEMON=$(top_builddir)/src/lemon @@ -47,8 +47,7 @@ src = server.c response.c connections.c network.c \ network_write.c network_linux_sendfile.c \ network_freebsd_sendfile.c network_writev.c \ network_solaris_sendfilev.c network_openssl.c \ - configfile.c request.c - + configfile.c request.c proc_open.c spawn_fcgi_SOURCES=spawn-fcgi.c @@ -208,7 +207,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \ mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ configparser.h mod_ssi_exprparser.h \ sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \ - splaytree.h + splaytree.h proc_open.h DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" @@ -217,11 +216,8 @@ lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_liba lighttpd_LDFLAGS = -export-dynamic lighttpd_CCPFLAGS = $(FAM_CFLAGS) -array_SOURCES = array.c buffer.c data_string.c data_count.c -array_CPPFLAGS= -DDEBUG_ARRAY - -chunk_SOURCES = buffer.c chunk.c -chunk_CPPFLAGS= -DDEBUG_CHUNK +proc_open_SOURCES = proc_open.c buffer.c +proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN #gen_license_SOURCES = license.c md5.c buffer.c gen_license.c diff --git a/src/array.h b/src/array.h index 12c18c61..7c79c321 100644 --- a/src/array.h +++ b/src/array.h @@ -64,7 +64,6 @@ typedef struct { data_array *data_array_init(void); typedef enum { CONFIG_COND_UNSET, CONFIG_COND_EQ, CONFIG_COND_MATCH, CONFIG_COND_NE, CONFIG_COND_NOMATCH } config_cond_t; -typedef enum { COND_RESULT_FALSE, COND_RESULT_TRUE, COND_RESULT_UNSET } cond_result_t; #define PATCHES NULL, "SERVERsocket", "HTTPurl", "HTTPhost", "HTTPreferer", "HTTPuseragent", "HTTPcookie", "HTTPremoteip" typedef enum { @@ -290,6 +290,14 @@ typedef struct { typedef enum { CON_STATE_CONNECT, CON_STATE_REQUEST_START, CON_STATE_READ, CON_STATE_REQUEST_END, CON_STATE_READ_POST, CON_STATE_HANDLE_REQUEST, CON_STATE_RESPONSE_START, CON_STATE_WRITE, CON_STATE_RESPONSE_END, CON_STATE_ERROR, CON_STATE_CLOSE } connection_state_t; +typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t; +typedef struct { + cond_result_t result; + int patterncount; + int matches[3 * 10]; + buffer *comp_value; /* just a pointer */ +} cond_cache_t; + typedef struct { connection_state_t state; @@ -336,6 +344,7 @@ typedef struct { int http_status; sock_addr dst_addr; + buffer *dst_addr_buf; /* request */ buffer *parse_request; @@ -361,7 +370,7 @@ typedef struct { void **plugin_ctx; /* plugin connection specific config */ specific_config conf; /* global connection specific config */ - cond_result_t *cond_results_cache; + cond_cache_t *cond_cache; buffer *server_name; diff --git a/src/configfile-glue.c b/src/configfile-glue.c index 00e28ea8..3f20f6e7 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -147,7 +147,7 @@ int config_insert_values_global(server *srv, array *ca, const config_values_t cv return config_insert_values_internal(srv, ca, cv); } -static int config_check_cond_cached(server *srv, connection *con, data_config *dc); +static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc); static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { buffer *l; @@ -155,21 +155,21 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat /* check parent first */ if (dc->parent && dc->parent->context_ndx) { if (con->conf.log_condition_handling) { - log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->string); + log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key); } - if (!config_check_cond_cached(srv, con, dc->parent)) { + if (config_check_cond_cached(srv, con, dc->parent) == COND_RESULT_FALSE) { return COND_RESULT_FALSE; } } if (dc->prev) { if (con->conf.log_condition_handling) { - log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->string); + log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key); } /* make sure prev is checked first */ config_check_cond_cached(srv, con, dc->prev); /* one of prev set me to FALSE */ - if (con->cond_results_cache[dc->context_ndx] == COND_RESULT_FALSE) { + if (COND_RESULT_FALSE == con->cond_cache[dc->context_ndx].result) { return COND_RESULT_FALSE; } } @@ -178,7 +178,8 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat l = srv->empty_string; - if (COMP_HTTP_HOST == dc->comp) { + switch (dc->comp) { + case COMP_HTTP_HOST: { l = con->uri.authority; #if 0 /* FIXME: get this working again */ @@ -208,7 +209,9 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat break; } } - } else if (COMP_HTTP_REMOTEIP == dc->comp) { + break; + } + case COMP_HTTP_REMOTEIP: { char *nm_slash; /* handle remoteip limitations * @@ -283,27 +286,40 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat buffer_copy_string(srv->cond_check_buf, s); } #endif - } else if (COMP_HTTP_URL == dc->comp) { + break; + } + case COMP_HTTP_URL: l = con->uri.path; - } else if (COMP_SERVER_SOCKET == dc->comp) { + break; + + case COMP_SERVER_SOCKET: l = srv_sock->srv_token; - } else if (COMP_HTTP_REFERER == dc->comp) { + break; + + case COMP_HTTP_REFERER: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) { l = ds->value; } - } else if (COMP_HTTP_COOKIE == dc->comp) { + break; + } + case COMP_HTTP_COOKIE: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) { l = ds->value; } - } else if (COMP_HTTP_USERAGENT == dc->comp) { + break; + } + case COMP_HTTP_USERAGENT: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) { l = ds->value; } - } else { + break; + } + + default: return COND_RESULT_FALSE; } @@ -322,15 +338,21 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat #ifdef HAVE_PCRE_H case CONFIG_COND_NOMATCH: case CONFIG_COND_MATCH: { -#define N 10 - int ovec[N * 3]; + cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; int n; - n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, ovec, N * 3); +#ifndef elementsof +#define elementsof(x) (sizeof(x) / sizeof(x[0])) +#endif + n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, + cache->matches, elementsof(cache->matches)); if (n > 0) { + cache->patterncount = n; + cache->comp_value = l; return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { + /* cache is already cleared */ return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; @@ -344,36 +366,56 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat return COND_RESULT_FALSE; } -static int config_check_cond_cached(server *srv, connection *con, data_config *dc) { - cond_result_t *cache = con->cond_results_cache; +static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) { + cond_cache_t *caches = con->cond_cache; - if (cache[dc->context_ndx] == COND_RESULT_UNSET) { - if (COND_RESULT_TRUE == (cache[dc->context_ndx] = config_check_cond_nocache(srv, con, dc))) { + if (COND_RESULT_UNSET == caches[dc->context_ndx].result) { + if (COND_RESULT_TRUE == (caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc))) { if (dc->next) { data_config *c; if (con->conf.log_condition_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "setting remains of chaining to FALSE"); + log_error_write(srv, __FILE__, __LINE__, "s", + "setting remains of chaining to false"); } for (c = dc->next; c; c = c->next) { - cache[c->context_ndx] = COND_RESULT_FALSE; + caches[c->context_ndx].result = COND_RESULT_FALSE; } } } if (con->conf.log_condition_handling) { - log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(uncached) result:", cache[dc->context_ndx]); + log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx, + "(uncached) result:", + caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"); } } else { if (con->conf.log_condition_handling) { - log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(cached) result:", cache[dc->context_ndx]); + log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx, + "(cached) result:", + caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"); } } - return cache[dc->context_ndx]; + return caches[dc->context_ndx].result; } int config_check_cond(server *srv, connection *con, data_config *dc) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ==="); } - return config_check_cond_cached(srv, con, dc); + return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE); } + +int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n) +{ + cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; + if (n > cache->patterncount) { + return 0; + } + + n <<= 1; /* n *= 2 */ + buffer_append_string_len(buf, + cache->comp_value->ptr + cache->matches[n], + cache->matches[n + 1] - cache->matches[n]); + return 1; +} + diff --git a/src/configfile.c b/src/configfile.c index 0597eb3a..a58de1a1 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -19,6 +19,7 @@ #include "configparser.h" #include "configfile.h" +#include "proc_open.h" static int config_insert(server *srv) { @@ -225,11 +226,6 @@ static int config_insert(server *srv) { #define PATCH(x) con->conf.x = s->x int config_setup_connection(server *srv, connection *con) { specific_config *s = srv->config_storage[0]; - int i; - - for (i = srv->config_context->used - 1; i >= 0; i --) { - con->cond_results_cache[i] = COND_RESULT_UNSET; - } PATCH(allow_http11); PATCH(mimetypes); @@ -351,12 +347,12 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { return 0; } #undef PATCH + typedef struct { int foo; int bar; - buffer *file; - stream s; + const buffer *source; const char *input; size_t offset; size_t size; @@ -369,6 +365,7 @@ typedef struct { int in_cond; } tokenizer_t; +#if 0 static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) { if (buffer_is_empty(basedir) && (fn[0] == '/' || fn[0] == '\\') && @@ -404,7 +401,7 @@ static int tokenizer_close(server *srv, tokenizer_t *t) { buffer_free(t->file); return stream_close(&(t->s)); } - +#endif static int config_skip_newline(tokenizer_t *t) { int skipped = 1; assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n'); @@ -445,7 +442,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * tid = TK_ARRAY_ASSIGN; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "use => for assignments in arrays"); return -1; @@ -465,7 +462,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * tid = TK_MATCH; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "only =~ and == are allow in the condition"); return -1; @@ -481,7 +478,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * t->line_pos++; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "unexpected equal-sign: ="); return -1; @@ -504,7 +501,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * tid = TK_NOMATCH; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "only !~ and != are allow in the condition"); return -1; @@ -513,7 +510,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * t->in_cond = 0; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "unexpected exclamation-marks: !"); return -1; @@ -603,7 +600,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "missing closing quote"); @@ -646,8 +643,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * t->offset += 2; buffer_copy_string(token, "+="); tid = TK_APPEND; - } - else { + } else { t->offset++; tid = TK_PLUS; buffer_copy_string(token, "+"); @@ -708,7 +704,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "invalid character in condition"); return -1; @@ -728,7 +724,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "unexpected EOF"); @@ -748,6 +744,8 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * if (strcmp(token->ptr, "include") == 0) { tid = TK_INCLUDE; + } else if (strcmp(token->ptr, "include_shell") == 0) { + tid = TK_INCLUDE_SHELL; } else if (strcmp(token->ptr, "else") == 0) { tid = TK_ELSE; } else { @@ -759,7 +757,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, "invalid character in variable name"); return -1; @@ -773,7 +771,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * *token_id = tid; #if 0 log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd", - "file:", t->file, + "source:", t->source, "line:", t->line, "pos:", t->line_pos, token, token->used - 1, tid); #endif @@ -787,21 +785,16 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * return 0; } -int config_parse_file(server *srv, config_t *context, const char *fn) { - tokenizer_t t; +static int config_parse(server *srv, config_t *context, tokenizer_t *t) { void *pParser; int token_id; buffer *token, *lasttoken; int ret; - if (tokenizer_open(srv, &t, context->basedir, fn) == -1) { - return -1; - } - pParser = configparserAlloc( malloc ); lasttoken = buffer_init(); token = buffer_init(); - while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context->ok) { + while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) { buffer_copy_string_buffer(lasttoken, token); configparser(pParser, token_id, token, context); @@ -821,20 +814,98 @@ int config_parse_file(server *srv, config_t *context, const char *fn) { if (ret == -1) { log_error_write(srv, __FILE__, __LINE__, "sb", "configfile parser failed:", lasttoken); - } - else if (context->ok == 0) { + } else if (context->ok == 0) { log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb", - "file:", t.file, - "line:", t.line, "pos:", t.line_pos, + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, "parser failed somehow near here:", lasttoken); ret = -1; } buffer_free(lasttoken); - tokenizer_close(srv, &t); return ret == -1 ? -1 : 0; } +static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) { + + t->source = source; + t->input = input; + t->size = size; + t->offset = 0; + t->line = 1; + t->line_pos = 1; + + t->in_key = 1; + t->in_brace = 0; + t->in_cond = 0; + return 0; +} + +int config_parse_file(server *srv, config_t *context, const char *fn) { + tokenizer_t t; + stream s; + int ret; + buffer *filename; + + if (buffer_is_empty(context->basedir) && + (fn[0] == '/' || fn[0] == '\\') && + (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) { + filename = buffer_init_string(fn); + } else { + filename = buffer_init_buffer(context->basedir); + buffer_append_string(filename, fn); + } + + if (0 != stream_open(&s, filename)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening configfile ", filename, "failed:", strerror(errno)); + ret = -1; + } else { + tokenizer_init(&t, filename, s.start, s.size); + ret = config_parse(srv, context, &t); + } + + stream_close(&s); + buffer_free(filename); + return ret; +} + +int config_parse_cmd(server *srv, config_t *context, const char *cmd) { + proc_handler_t proc; + tokenizer_t t; + int ret; + buffer *source; + buffer *out; + char oldpwd[PATH_MAX]; + + if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "cannot get cwd", strerror(errno)); + return -1; + } + + source = buffer_init_string(cmd); + out = buffer_init(); + + if (!buffer_is_empty(context->basedir)) { + chdir(context->basedir->ptr); + } + + if (0 != proc_open_buffer(&proc, cmd, NULL, out, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening", source, "failed:", strerror(errno)); + ret = -1; + } else { + tokenizer_init(&t, source, out->ptr, out->used); + ret = config_parse(srv, context, &t); + } + + buffer_free(source); + buffer_free(out); + chdir(oldpwd); + return ret; +} + static void context_init(server *srv, config_t *context) { context->srv = srv; context->ok = 1; diff --git a/src/configfile.h b/src/configfile.h index f8dc9bb2..a58a257d 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -18,5 +18,6 @@ void *configparserAlloc(void *(*mallocProc)(size_t)); void configparserFree(void *p, void (*freeProc)(void*)); void configparser(void *yyp, int yymajor, buffer *yyminor, config_t *ctx); int config_parse_file(server *srv, config_t *context, const char *fn); +int config_parse_cmd(server *srv, config_t *context, const char *cmd); #endif diff --git a/src/configparser.y b/src/configparser.y index 23e19140..06fa6955 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -131,6 +131,7 @@ metalines ::= . metaline ::= varline. metaline ::= condlines(A) EOL. { A = NULL; } metaline ::= include. +metaline ::= include_shell. metaline ::= EOL. %type value {data_unset *} @@ -141,17 +142,17 @@ metaline ::= EOL. %type aelements {array *} %type array {array *} %type key {buffer *} +%type stringop {buffer *} %type cond {config_cond_t } %destructor value { $$->free($$); } %destructor expression { $$->free($$); } %destructor aelement { $$->free($$); } -%destructor condline { $$->free((data_unset *)$$); } -%destructor condlines { $$->free((data_unset *)$$); } %destructor aelements { array_free($$); } %destructor array { array_free($$); } %destructor key { buffer_free($$); } +%destructor stringop { buffer_free($$); } %token_type {buffer *} %token_destructor { buffer_free($$); } @@ -347,7 +348,7 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) expression( break; default: assert(0); - break; + return; } b = buffer_init(); @@ -470,18 +471,36 @@ cond(A) ::= NOMATCH. { A = CONFIG_COND_NOMATCH; } -include ::= INCLUDE expression(A). { +stringop(A) ::= expression(B). { + A = NULL; if (ctx->ok) { - if (A->type != TYPE_STRING) { - fprintf(stderr, "file must be string"); + if (B->type != TYPE_STRING) { + fprintf(stderr, "operand must be string"); ctx->ok = 0; } else { - buffer *file = ((data_string*)A)->value; - if (0 != config_parse_file(ctx->srv, ctx, file->ptr)) { - ctx->ok = 0; - } + A = buffer_init_buffer(((data_string*)B)->value); } } - A->free(A); - A = NULL; + B->free(B); + B = NULL; +} + +include ::= INCLUDE stringop(A). { + if (ctx->ok) { + if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) { + ctx->ok = 0; + } + buffer_free(A); + A = NULL; + } +} + +include_shell ::= INCLUDE_SHELL stringop(A). { + if (ctx->ok) { + if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) { + ctx->ok = 0; + } + buffer_free(A); + A = NULL; + } } diff --git a/src/connections.c b/src/connections.c index 1842f3c6..f525a5d5 100644 --- a/src/connections.c +++ b/src/connections.c @@ -23,6 +23,8 @@ #include "plugin.h" +#include "inet_ntop_cache.h" + #ifdef USE_OPENSSL # include <openssl/ssl.h> # include <openssl/err.h> @@ -479,6 +481,7 @@ static int connection_handle_write_prepare(server *srv, connection *con) { break; } + case 207: break; default: if (con->request.http_method == HTTP_METHOD_HEAD || @@ -603,7 +606,8 @@ connection *connection_init(server *srv) { con->plugin_ctx = calloc(srv->plugins.used + 1, sizeof(void *)); - con->cond_results_cache = calloc(srv->config_context->used, sizeof(cond_result_t)); + con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); + con->dst_addr_buf = buffer_init(); config_setup_connection(srv, con); return con; @@ -653,7 +657,7 @@ void connections_free(server *srv) { CLEAN(error_handler); #undef CLEAN free(con->plugin_ctx); - free(con->cond_results_cache); + free(con->cond_cache); free(con); } @@ -753,6 +757,15 @@ int connection_reset(server *srv, connection *con) { con->plugin_ctx[i] = NULL; } +#if COND_RESULT_UNSET + for (i = srv->config_context->used - 1; i >= 0; i --) { + con->cond_cache[i].result = COND_RESULT_UNSET; + con->cond_cache[i].patterncount = 0; + } +#else + memset(con->cond_cache, 0, sizeof(cond_cache_t) * srv->config_context->used); +#endif + con->header_len = 0; con->in_error_handler = 0; @@ -1132,6 +1145,7 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { con->connection_start = srv->cur_ts; con->dst_addr = cnt_addr; + buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); con->srv_socket = srv_socket; if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { diff --git a/src/data_config.c b/src/data_config.c index 90cdd367..529df359 100644 --- a/src/data_config.c +++ b/src/data_config.c @@ -73,10 +73,13 @@ static void data_config_print(const data_unset *d, int depth) { for (i = 0; i < ds->childs->used; i ++) { data_unset *du = ds->childs->data[i]; - fprintf(stderr, "\n"); - array_print_indent(depth + 1); - du->print(du, depth + 1); - fprintf(stderr, "\n"); + /* only the 1st block of chaining */ + if (NULL == ((data_config *)du)->prev) { + fprintf(stderr, "\n"); + array_print_indent(depth + 1); + du->print(du, depth + 1); + fprintf(stderr, "\n"); + } } } diff --git a/src/keyvalue.c b/src/keyvalue.c index 9af6d510..74886985 100644 --- a/src/keyvalue.c +++ b/src/keyvalue.c @@ -292,6 +292,7 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons size_t i; const char *errptr; int erroff; + pcre_keyvalue *kv; #endif if (!key) return -1; @@ -316,14 +317,20 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons } } - if (NULL == (kvb->kv[kvb->used]->key = pcre_compile(key, + kv = kvb->kv[kvb->used]; + if (NULL == (kv->key = pcre_compile(key, 0, &errptr, &erroff, NULL))) { fprintf(stderr, "%s.%d: rexexp compilation error at %s\n", __FILE__, __LINE__, errptr); return -1; } + + if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && + errptr != NULL) { + return -1; + } - kvb->kv[kvb->used]->value = strdup(value); + kv->value = buffer_init_string(value); kvb->used++; @@ -339,11 +346,14 @@ int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, cons void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) { #ifdef HAVE_PCRE_H size_t i; + pcre_keyvalue *kv; for (i = 0; i < kvb->size; i++) { - if (kvb->kv[i]->key) pcre_free(kvb->kv[i]->key); - if (kvb->kv[i]->value) free(kvb->kv[i]->value); - free(kvb->kv[i]); + kv = kvb->kv[i]; + if (kv->key) pcre_free(kv->key); + if (kv->key_extra) pcre_free(kv->key_extra); + if (kv->value) buffer_free(kv->value); + free(kv); } if (kvb->kv) free(kvb->kv); diff --git a/src/keyvalue.h b/src/keyvalue.h index 9d31fab9..de7056ff 100644 --- a/src/keyvalue.h +++ b/src/keyvalue.h @@ -25,9 +25,10 @@ typedef struct { typedef struct { #ifdef HAVE_PCRE_H pcre *key; + pcre_extra *key_extra; #endif - char *value; + buffer *value; } pcre_keyvalue; typedef enum { HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST } httpauth_type; diff --git a/src/mod_alias.c b/src/mod_alias.c index e4869493..4d898922 100644 --- a/src/mod_alias.c +++ b/src/mod_alias.c @@ -1,6 +1,7 @@ #include <ctype.h> #include <stdlib.h> #include <string.h> +#include <stdio.h> #include "base.h" #include "log.h" @@ -86,6 +87,31 @@ SETDEFAULTS_FUNC(mod_alias_set_defaults) { if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } + if (s->alias->used >= 2) { + const array *a = s->alias; + size_t j, k; + + for (j = 0; j < a->used; j ++) { + const buffer *prefix = a->data[a->sorted[j]]->key; + for (k = j + 1; k < a->used; k ++) { + const buffer *key = a->data[a->sorted[k]]->key; + + if (key->used < prefix->used) { + break; + } + if (memcmp(key->ptr, prefix->ptr, prefix->used - 1) != 0) { + break; + } + /* ok, they have same prefix. check position */ + if (a->sorted[j] < a->sorted[k]) { + fprintf(stderr, "url.alias: `%s' will never match as `%s' matched first\n", + key->ptr, + prefix->ptr); + return HANDLER_ERROR; + } + } + } + } } return HANDLER_GO_ON; diff --git a/src/mod_redirect.c b/src/mod_redirect.c index e6578ffa..aa0c8207 100644 --- a/src/mod_redirect.c +++ b/src/mod_redirect.c @@ -15,6 +15,7 @@ typedef struct { pcre_keyvalue_buffer *redirect; + data_config *context; /* to which apply me */ } plugin_config; typedef struct { @@ -165,6 +166,7 @@ static int mod_redirect_patch_connection(server *srv, connection *con, plugin_da if (0 == strcmp(du->key->ptr, "url.redirect")) { p->conf.redirect = s->redirect; + p->conf.context = dc; } } } @@ -190,19 +192,22 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ for (i = 0; i < p->conf.redirect->used; i++) { pcre *match; + pcre_extra *extra; const char *pattern; size_t pattern_len; int n; + pcre_keyvalue *kv = p->conf.redirect->kv[i]; # define N 10 int ovec[N * 3]; - match = p->conf.redirect->kv[i]->key; - pattern = p->conf.redirect->kv[i]->value; - pattern_len = strlen(pattern); + match = kv->key; + extra = kv->key_extra; + pattern = kv->value->ptr; + pattern_len = kv->value->used - 1; - if ((n = pcre_exec(match, NULL, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { + if ((n = pcre_exec(match, extra, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { - log_error_write(srv, __FILE__, __LINE__, "sd" + log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching: ", n); return HANDLER_ERROR; } @@ -210,6 +215,7 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ const char **list; size_t start, end; size_t k; + /* it matched */ pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list); @@ -219,7 +225,7 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ start = 0; end = pattern_len; for (k = 0; k < pattern_len; k++) { - if (pattern[k] == '$' && + if ((pattern[k] == '$' || pattern[k] == '%') && isdigit((unsigned char)pattern[k + 1])) { /* got one */ @@ -229,9 +235,13 @@ static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_ buffer_append_string_len(p->location, pattern + start, end - start); - /* n is always > 0 */ - if (num < (size_t)n) { - buffer_append_string(p->location, list[num]); + if (pattern[k] == '$') { + /* n is always > 0 */ + if (num < (size_t)n) { + buffer_append_string(p->location, list[num]); + } + } else { + config_append_cond_match_buffer(con, p->conf.context, p->location, num); } k++; diff --git a/src/mod_rewrite.c b/src/mod_rewrite.c index 345f6f6c..8674c59a 100644 --- a/src/mod_rewrite.c +++ b/src/mod_rewrite.c @@ -31,6 +31,7 @@ typedef struct { typedef struct { rewrite_rule_buffer *rewrite; + data_config *context; /* to which apply me */ } plugin_config; typedef struct { @@ -377,7 +378,7 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) { start = 0; end = pattern_len; for (k = 0; k < pattern_len; k++) { - if (pattern[k] == '$' && + if ((pattern[k] == '$' || pattern[k] == '%') && isdigit((unsigned char)pattern[k + 1])) { /* got one */ @@ -387,9 +388,13 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) { buffer_append_string_len(con->request.uri, pattern + start, end - start); - /* n is always larger than 0 */ - if (num < (size_t)n) { - buffer_append_string(con->request.uri, list[num]); + if (pattern[k] == '$') { + /* n is always > 0 */ + if (num < (size_t)n) { + buffer_append_string(con->request.uri, list[num]); + } + } else { + config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num); } k++; diff --git a/src/plugin.c b/src/plugin.c index 4e53a26e..d861b918 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -62,7 +62,7 @@ static void plugin_free(plugin *p) { int use_dlclose = 1; if (p->name) buffer_free(p->name); #ifdef HAVE_VALGRIND_VALGRIND_H - if (RUNNING_ON_VALGRIND) use_dlclose = 0; + /*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/ #endif if (use_dlclose && p->lib) { diff --git a/src/plugin.h b/src/plugin.h index dad01424..dd2b59a9 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -89,5 +89,6 @@ int config_insert_values_internal(server *srv, array *ca, const config_values_t int config_setup_connection(server *srv, connection *con); int config_patch_connection(server *srv, connection *con, comp_key_t comp); int config_check_cond(server *srv, connection *con, data_config *dc); +int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n); #endif diff --git a/src/proc_open.c b/src/proc_open.c new file mode 100644 index 00000000..ecea815a --- /dev/null +++ b/src/proc_open.c @@ -0,0 +1,386 @@ +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include "proc_open.h" + +#ifdef WIN32 +#include <io.h> +#include <fcntl.h> +#else +#include <sys/wait.h> +#include <unistd.h> +#endif + + +#ifdef WIN32 +/* {{{ win32 stuff */ +# define SHELLENV "ComSpec" +# define SECURITY_DC , SECURITY_ATTRIBUTES *security +# define SECURITY_CC , security +# define pipe(pair) (CreatePipe(&pair[0], &pair[1], security, 2048L) ? 0 : -1) +static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig) +{ + HANDLE copy, self = GetCurrentProcess(); + + if (!DuplicateHandle(self, src, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS | + (closeorig ? DUPLICATE_CLOSE_SOURCE : 0))) + return NULL; + return copy; +} +# define close_descriptor(fd) CloseHandle(fd) +static void pipe_close_parent(pipe_t *p) { + /* don't let the child inherit the parent side of the pipe */ + p->parent = dup_handle(p->parent, FALSE, TRUE); +} +static void pipe_close_child(pipe_t *p) { + close_descriptor(p->child); + p->fd = _open_osfhandle((long)p->parent, + (p->fd == 0 ? O_RDONLY : O_WRONLY)|O_BINARY); +} +/* }}} */ +#else /* WIN32 */ +/* {{{ unix way */ +# define SHELLENV "SHELL" +# define SECURITY_DC +# define SECURITY_CC +# define close_descriptor(fd) close(fd) +static void pipe_close_parent(pipe_t *p) { + /* don't close stdin */ + close_descriptor(p->parent); + if (dup2(p->child, p->fd) != p->fd) { + perror("pipe_child dup2"); + } else { + close_descriptor(p->child); + p->child = p->fd; + } +} +static void pipe_close_child(pipe_t *p) { + close_descriptor(p->child); + p->fd = p->parent; +} +/* }}} */ +#endif /* WIN32 */ + +/* {{{ pipe_close */ +static void pipe_close(pipe_t *p) { + close_descriptor(p->parent); + close_descriptor(p->child); +#ifdef WIN32 + close(p->fd); +#endif +} +/* }}} */ +/* {{{ pipe_open */ +static int pipe_open(pipe_t *p, int fd SECURITY_DC) { + descriptor_t newpipe[2]; + + if (0 != pipe(newpipe)) { + fprintf(stderr, "can't open pipe"); + return -1; + } + if (0 == fd) { + p->parent = newpipe[1]; /* write */ + p->child = newpipe[0]; /* read */ + } else { + p->parent = newpipe[0]; /* read */ + p->child = newpipe[1]; /* write */ + } + p->fd = fd; + + return 0; +} +/* }}} */ + +/* {{{ proc_open_pipes */ +static int proc_open_pipes(proc_handler_t *proc SECURITY_DC) { + if (pipe_open(&(proc->in), 0 SECURITY_CC) != 0) { + return -1; + } + if (pipe_open(&(proc->out), 1 SECURITY_CC) != 0) { + return -1; + } + if (pipe_open(&(proc->err), 2 SECURITY_CC) != 0) { + return -1; + } + return 0; +} +/* }}} */ +/* {{{ proc_close_pipes */ +static void proc_close_pipes(proc_handler_t *proc) { + pipe_close(&proc->in); + pipe_close(&proc->out); + pipe_close(&proc->err); +} +/* }}} */ +/* {{{ proc_close_parents */ +static void proc_close_parents(proc_handler_t *proc) { + pipe_close_parent(&proc->in); + pipe_close_parent(&proc->out); + pipe_close_parent(&proc->err); +} +/* }}} */ +/* {{{ proc_close_childs */ +static void proc_close_childs(proc_handler_t *proc) { + pipe_close_child(&proc->in); + pipe_close_child(&proc->out); + pipe_close_child(&proc->err); +} +/* }}} */ + +#ifdef WIN32 +/* {{{ proc_close */ +int proc_close(proc_handler_t *proc) { + proc_pid_t child = proc->child; + DWORD wstatus; + + proc_close_pipes(proc); + WaitForSingleObject(child, INFINITE); + GetExitCodeProcess(child, &wstatus); + CloseHandle(child); + + return wstatus; +} +/* }}} */ +/* {{{ proc_open */ +int proc_open(proc_handler_t *proc, const char *command) { + PROCESS_INFORMATION pi; + STARTUPINFO si; + BOOL procok; + SECURITY_ATTRIBUTES security; + const char *shell; + buffer *cmdline; + + if (NULL == (shell = getenv(SHELLENV))) { + fprintf(stderr, "env %s is required", SHELLENV); + return -1; + } + + /* we use this to allow the child to inherit handles */ + memset(&security, 0, sizeof(security)); + security.nLength = sizeof(security); + security.bInheritHandle = TRUE; + security.lpSecurityDescriptor = NULL; + + if (proc_open_pipes(proc, &security) != 0) { + return -1; + } + proc_close_parents(proc); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = proc->in.child; + si.hStdOutput = proc->out.child; + si.hStdError = proc->err.child; + + memset(&pi, 0, sizeof(pi)); + + cmdline = buffer_init(); + buffer_append_string(cmdline, shell); + buffer_append_string_len(cmdline, CONST_STR_LEN(" /c ")); + buffer_append_string(cmdline, command); + procok = CreateProcess(NULL, cmdline->ptr, &security, &security, TRUE, + NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + buffer_free(cmdline); + + if (FALSE == procok) { + fprintf(stderr, "failed to CreateProcess"); + return -1; + } + + proc->child = pi.hProcess; + CloseHandle(pi.hThread); + + proc_close_childs(proc); + + return 0; +} +/* }}} */ +#else /* WIN32 */ +/* {{{ proc_close */ +int proc_close(proc_handler_t *proc) { + pid_t child = proc->child; + int wstatus; + pid_t wait_pid; + + proc_close_pipes(proc); + + do { + wait_pid = waitpid(child, &wstatus, 0); + } while (wait_pid == -1 && errno == EINTR); + + if (wait_pid == -1) { + return -1; + } else { + if (WIFEXITED(wstatus)) + wstatus = WEXITSTATUS(wstatus); + } + + return wstatus; +} +/* }}} */ +/* {{{ proc_open */ +int proc_open(proc_handler_t *proc, const char *command) { + pid_t child; + const char *shell; + + if (NULL == (shell = getenv(SHELLENV))) { + fprintf(stderr, "env %s is required", SHELLENV); + return -1; + } + + if (proc_open_pipes(proc) != 0) { + return -1; + } + + /* the unix way */ + + child = fork(); + + if (child == 0) { + /* this is the child process */ + + /* close those descriptors that we just opened for the parent stuff, + * dup new descriptors into required descriptors and close the original + * cruft + */ + proc_close_parents(proc); + + execl(shell, shell, "-c", command, NULL); + _exit(127); + + } else if (child < 0) { + fprintf(stderr, "failed to forking"); + proc_close(proc); + return -1; + + } else { + proc->child = child; + proc_close_childs(proc); + return 0; + } +} +/* }}} */ +#endif /* WIN32 */ + +/* {{{ proc_read_fd_to_buffer */ +static void proc_read_fd_to_buffer(int fd, buffer *b) { + ssize_t s; + + for (;;) { + buffer_prepare_append(b, 512); + if ((s = read(fd, (void *)(b->ptr + b->used), 512 - 1)) <= 0) { + break; + } + b->used += s; + } + b->ptr[b->used] = '\0'; +} +/* }}} */ +/* {{{ proc_open_buffer */ +int proc_open_buffer(proc_handler_t *proc, const char *command, buffer *in, buffer *out, buffer *err) { + + UNUSED(err); + + if (proc_open(proc, command) != 0) { + return -1; + } + + if (in) { + if (write(proc->in.fd, (void *)in->ptr, in->used) < 0) { + perror("error writing pipe"); + return -1; + } + } + pipe_close(&proc->in); + + if (out) { + proc_read_fd_to_buffer(proc->out.fd, out); + } + pipe_close(&proc->out); + + if (err) { + proc_read_fd_to_buffer(proc->err.fd, err); + } + pipe_close(&proc->err); + + return 0; +} +/* }}} */ + +/* {{{ test */ +#ifdef DEBUG_PROC_OPEN +int main() { + proc_handler_t proc; + buffer *in = buffer_init(), *out = buffer_init(), *err = buffer_init(); + int wstatus; + +#define FREE() do { \ + buffer_free(in); \ + buffer_free(out); \ + buffer_free(err); \ +} while (0) + +#define RESET() do { \ + buffer_reset(in); \ + buffer_reset(out); \ + buffer_reset(err); \ + wstatus = proc_close(&proc); \ + if (0&&wstatus != 0) { \ + fprintf(stdout, "exitstatus %d\n", wstatus); \ + return __LINE__ - 200; \ + } \ +} while (0) + +#define ERROR_OUT() do { \ + fprintf(stdout, "failed opening proc\n"); \ + wstatus = proc_close(&proc); \ + fprintf(stdout, "exitstatus %d\n", wstatus); \ + FREE(); \ + return __LINE__ - 300; \ +} while (0) + +#ifdef WIN32 +#define CMD_CAT "pause" +#else +#define CMD_CAT "cat" +#endif + + do { + fprintf(stdout, "test: echo 123 without read\n"); + if (proc_open(&proc, "echo 321") != 0) { + ERROR_OUT(); + } + close_descriptor(proc.in.parent); + close_descriptor(proc.out.parent); + close_descriptor(proc.err.parent); + RESET(); + + fprintf(stdout, "test: echo 321 with read\n"); fflush(stdout); + if (proc_open_buffer(&proc, "echo 321", NULL, out, err) != 0) { + ERROR_OUT(); + } + fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); + RESET(); + + fprintf(stdout, "test: echo 123 | " CMD_CAT "\n"); fflush(stdout); + buffer_copy_string_len(in, CONST_STR_LEN("123\n")); + if (proc_open_buffer(&proc, CMD_CAT, in, out, err) != 0) { + ERROR_OUT(); + } + fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); + RESET(); + } while (0); + +#undef RESET +#undef ERROR_OUT + + fprintf(stdout, "ok\n"); + + FREE(); + return 0; +} +#endif /* DEBUG_PROC_OPEN */ +/* }}} */ + diff --git a/src/proc_open.h b/src/proc_open.h new file mode 100644 index 00000000..e07421a0 --- /dev/null +++ b/src/proc_open.h @@ -0,0 +1,25 @@ + +#include "buffer.h" + +#ifdef WIN32 +#include <windows.h> +typedef HANDLE descriptor_t; +typedef HANDLE proc_pid_t; +#else +typedef int descriptor_t; +typedef pid_t proc_pid_t; +#endif + +typedef struct { + descriptor_t parent, child; + int fd; +} pipe_t; + +typedef struct { + pipe_t in, out, err; + proc_pid_t child; +} proc_handler_t; + +int proc_close(proc_handler_t *ht); +int proc_open(proc_handler_t *ht, const char *command); +int proc_open_buffer(proc_handler_t *ht, const char *command, buffer *in, buffer *out, buffer *err); diff --git a/src/response.c b/src/response.c index f15bf769..0e07396e 100644 --- a/src/response.c +++ b/src/response.c @@ -974,8 +974,10 @@ handler_t http_response_prepare(server *srv, connection *con) { if (con->mode == DIRECT && con->physical.path->used == 0) { char *qstr; + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); + } config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */ - config_patch_connection(srv, con, COMP_HTTP_REMOTEIP); /* Client-IP */ /** * prepare strings @@ -1003,6 +1005,7 @@ handler_t http_response_prepare(server *srv, connection *con) { buffer_copy_string_buffer(con->uri.authority, con->request.http_host); config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */ + config_patch_connection(srv, con, COMP_HTTP_REMOTEIP); /* Client-IP */ config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */ config_patch_connection(srv, con, COMP_HTTP_USERAGENT); /* User-Agent: */ config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ diff --git a/src/stream.c b/src/stream.c index d82a60db..fb6d0b7f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -22,6 +22,7 @@ int stream_open(stream *f, buffer *fn) { void *p; #endif + f->start = NULL; if (-1 == stat(fn->ptr, &st)) { return -1; diff --git a/tests/LightyTest.pm b/tests/LightyTest.pm index a95ca06d..a4797245 100755 --- a/tests/LightyTest.pm +++ b/tests/LightyTest.pm @@ -110,8 +110,12 @@ sub handle_http { my $remote = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $host, - PeerPort => $self->{PORT}) - or return -1; + PeerPort => $self->{PORT}); + + if (not defined $remote) { + diag("connect failed: $!"); + return -1; + } $remote->autoflush(1); diff --git a/tests/core-var-include.t b/tests/core-var-include.t index 538338fd..32830e07 100755 --- a/tests/core-var-include.t +++ b/tests/core-var-include.t @@ -8,7 +8,7 @@ BEGIN { use strict; use IO::Socket; -use Test::More tests => 16; +use Test::More tests => 17; use LightyTest; my $tf = LightyTest->new(); @@ -29,20 +29,22 @@ my $server_name = "test.example.org"; my $mystr = "string"; $mystr .= "_append"; my $tests = { - "include" => "/good_include", - "concat" => "/good_" . "concat", - "servername1" => "/good_" . $server_name, - "servername2" => $server_name . "/good_", - "servername3" => "/good_" . $server_name . "/", - "var.myvar" => "/good_var_myvar" . $myvar, - "myvar" => "/good_myvar" . $myvar, - "env" => "/" . $ENV{"env_test"}, - - "number1" => "/good_number" . "1", - "number2" => "1" . "/good_number", - "array_append" => "/good_array_append", - "string_append" => "/good_" . $mystr, - "number_append" => "/good_" . "2" + "include" => "/good_include", + "concat" => "/good_" . "concat", + "servername1" => "/good_" . $server_name, + "servername2" => $server_name . "/good_", + "servername3" => "/good_" . $server_name . "/", + "var.myvar" => "/good_var_myvar" . $myvar, + "myvar" => "/good_myvar" . $myvar, + "env" => "/" . $ENV{"env_test"}, + + "number1" => "/good_number" . "1", + "number2" => "1" . "/good_number", + "array_append" => "/good_array_append", + "string_append" => "/good_" . $mystr, + "number_append" => "/good_" . "2", + + "include_shell" => "/good_include_shell_" . "456" }; foreach my $test (keys %{ $tests }) { @@ -57,4 +59,3 @@ EOF } ok($tf->stop_proc == 0, "Stopping lighttpd"); - diff --git a/tests/docroot/www/pathinfo.php b/tests/docroot/www/pathinfo.php new file mode 100644 index 00000000..39cfce92 --- /dev/null +++ b/tests/docroot/www/pathinfo.php @@ -0,0 +1,3 @@ +<?php + print $_SERVER["PATH_INFO"]; +?> diff --git a/tests/lighttpd.conf b/tests/lighttpd.conf index 7863f543..4e92433c 100644 --- a/tests/lighttpd.conf +++ b/tests/lighttpd.conf @@ -67,7 +67,7 @@ setenv.add-request-header = ( "FOO" => "foo") setenv.add-response-header = ( "BAR" => "foo") fastcgi.debug = 0 -fastcgi.server = ( ".php" => ( ( "host" => "127.0.0.1", "port" => 1026 ) ), +fastcgi.server = ( ".php" => ( ( "host" => "127.0.0.1", "port" => 1026, "broken-scriptfilename" => "enable" ) ), "/prefix.fcgi" => ( ( "host" => "127.0.0.1", "port" => 1026, "check-local" => "disable", "broken-scriptfilename" => "enable" ) ) ) @@ -115,8 +115,6 @@ auth.require = ( "/server-status" => url.access-deny = ( "~", ".inc") -url.redirect = ( "^/redirect/$" => "http://localhost:2048/" ) - url.rewrite = ( "^/rewrite/foo($|\?.+)" => "/indexfile/rewrite.php$1", "^/rewrite/bar(?:$|\?(.+))" => "/indexfile/rewrite.php?bar&$1" ) @@ -149,3 +147,22 @@ $HTTP["host"] !~ "(no-simple\.example\.org)" { simple-vhost.default-host = "www.example.org" } +$HTTP["host"] =~ "(vvv).example.org" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/" ) +} + +$HTTP["host"] =~ "(zzz).example.org" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/%1" ) +} + +$HTTP["host"] =~ "(remoteip).example.org" { + $HTTP["remoteip"] =~ "(127.0.0.1)" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/%1" ) + } +} + +$HTTP["remoteip"] =~ "(127.0.0.1)" { + $HTTP["host"] =~ "(remoteip2).example.org" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/%1" ) + } +} diff --git a/tests/mod-fastcgi.t b/tests/mod-fastcgi.t index 8ead034d..515486fd 100755 --- a/tests/mod-fastcgi.t +++ b/tests/mod-fastcgi.t @@ -7,7 +7,7 @@ BEGIN { } use strict; -use Test::More tests => 43; +use Test::More tests => 44; use LightyTest; my $tf = LightyTest->new(); @@ -68,6 +68,14 @@ EOF ok($tf->handle_http($t) == 0, '$_SERVER["PHP_SELF"]'); $t->{REQUEST} = ( <<EOF +GET /pathinfo.php/foo HTTP/1.0 +Host: www.example.org +EOF + ); + $t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/foo' } ); + ok($tf->handle_http($t) == 0, '$_SERVER["PATH_INFO"]'); + + $t->{REQUEST} = ( <<EOF GET /phphost.php HTTP/1.0 Host: www.example.org EOF diff --git a/tests/mod-redirect.t b/tests/mod-redirect.t index 6b6104c8..690acdd0 100755 --- a/tests/mod-redirect.t +++ b/tests/mod-redirect.t @@ -8,7 +8,7 @@ BEGIN { use strict; use IO::Socket; -use Test::More tests => 3; +use Test::More tests => 6; use LightyTest; my $tf = LightyTest->new(); @@ -18,11 +18,34 @@ ok($tf->start_proc == 0, "Starting lighttpd") or die(); $t->{REQUEST} = ( <<EOF GET /redirect/ HTTP/1.0 +Host: vvv.example.org EOF ); $t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:2048/' } ); ok($tf->handle_http($t) == 0, 'external redirect'); +$t->{REQUEST} = ( <<EOF +GET /redirect/ HTTP/1.0 +Host: zzz.example.org +EOF + ); +$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:2048/zzz' } ); +ok($tf->handle_http($t) == 0, 'external redirect with cond regsub'); -ok($tf->stop_proc == 0, "Stopping lighttpd"); +$t->{REQUEST} = ( <<EOF +GET /redirect/ HTTP/1.0 +Host: remoteip.example.org +EOF + ); +$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:2048/127.0.0.1' } ); +ok($tf->handle_http($t) == 0, 'external redirect with cond regsub on remoteip'); + +$t->{REQUEST} = ( <<EOF +GET /redirect/ HTTP/1.0 +Host: remoteip2.example.org +EOF + ); +$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:2048/remoteip2' } ); +ok($tf->handle_http($t) == 0, 'external redirect with cond regsub on remoteip2'); +ok($tf->stop_proc == 0, "Stopping lighttpd"); diff --git a/tests/var-include-sub.conf b/tests/var-include-sub.conf index 152e6de1..3e0c3b9c 100644 --- a/tests/var-include-sub.conf +++ b/tests/var-include-sub.conf @@ -25,4 +25,10 @@ $HTTP["host"] =~ "^" + server.name + "$" { "^/string_append$" => "/good_" + mystr, "^/number_append$" => "/good_" + one, ) + + cmd = "echo cmd_ok=456" + include_shell cmd + url.redirect += ( + "^/include_shell$" => "/good_include_shell_" + cmd_ok, + ) } |