commit 8f8fa606cad6a67d91210c61c1925826621e4952 Author: Glenn Strauss Date: Sat Jul 16 07:58:40 2016 -0400 - next is 1.4.41 diff --git a/NEWS b/NEWS index 8b48e2e..b62c9b9 100644 --- a/NEWS +++ b/NEWS @@ -3,7 +3,9 @@ NEWS ==== -- 1.4.40 +- 1.4.41 + +- 1.4.40 - 2016-07-16 * [mod_ssi] enhance support for ssi vars (thx fbrosson) * add handling for lua 5.2 and 5.3 (fixes #2674) * use libmemcached instead of deprecated libmemcache commit e9c9f425646e7426bb9642ba961bd485a05c5868 Author: Glenn Strauss Date: Sat Jul 16 16:11:51 2016 -0400 remove long-deprecated, non-functional config opts diff --git a/src/configfile.c b/src/configfile.c index d845adc..4a260f8 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -123,34 +123,6 @@ static int config_insert(server *srv) { { "server.stream-request-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 76 */ { "server.stream-response-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 77 */ - { "server.host", - "use server.bind instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.docroot", - "use server.document-root instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-root", - "load mod_simple_vhost and use simple-vhost.server-root instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-default-host", - "load mod_simple_vhost and use simple-vhost.default-host instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-docroot", - "load mod_simple_vhost and use simple-vhost.document-root instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.userid", - "use server.username instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.groupid", - "use server.groupname instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.use-keep-alive", - "use server.max-keep-alive-requests = 0 instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.force-lower-case-files", - "use server.force-lowercase-filenames instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; diff --git a/src/mod_usertrack.c b/src/mod_usertrack.c index 07e552b..05ffe2a 100644 --- a/src/mod_usertrack.c +++ b/src/mod_usertrack.c @@ -76,7 +76,6 @@ SETDEFAULTS_FUNC(mod_usertrack_set_defaults) { { "usertrack.cookie-max-age", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "usertrack.cookiename", NULL, T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; commit 2cdc017fb9d5296cfd7cd2a103fb948caa791a2b Author: Glenn Strauss Date: Sat Jul 16 16:15:19 2016 -0400 [config] inherit server.use-ipv6 and server.set-v6only (fixes #678) inherit server.use-ipv6 and server.set-v6only from global scope into $SERVER["socket"] blocks (This potential behavior change was announced with lighttpd 1.4.40) x-ref: "$SERVER["socket"] to bind to IPv6 by default" https://redmine.lighttpd.net/issues/678 diff --git a/src/configfile.c b/src/configfile.c index 4a260f8..fbcbcf3 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -200,8 +200,8 @@ static int config_insert(server *srv) { s->ssl_empty_fragments = 0; s->ssl_use_sslv2 = 0; s->ssl_use_sslv3 = 0; - s->use_ipv6 = 0; - s->set_v6only = 1; + s->use_ipv6 = (i == 0) ? 0 : srv->config_storage[0]->use_ipv6; + s->set_v6only = (i == 0) ? 1 : srv->config_storage[0]->set_v6only; s->defer_accept = (i == 0) ? 0 : srv->config_storage[0]->defer_accept; #ifdef HAVE_LSTAT s->follow_symlink = 1; commit 052a049f29ca7478d5e86924add77bce481d68bf Author: Glenn Strauss Date: Sat Jul 16 23:23:24 2016 -0400 [build] allow AUTHOR, KEYID overrides to packdist commit 00cc4d7c0ecd9be2c5f1cd6a5397b78f75830905 Author: Glenn Strauss Date: Sat Jul 16 23:25:53 2016 -0400 [mod_auth] fix Digest auth to be better than Basic (fixes #1844) Make Digest authentication more compliant with RFC. Excerpt from https://www.rfc-editor.org/rfc/rfc7616.txt Section 5.13: The bottom line is that any compliant implementation will be relatively weak by cryptographic standards, but any compliant implementation will be far superior to Basic Authentication. x-ref: "Serious security problem in Digest Authentication" https://redmine.lighttpd.net/issues/1844 diff --git a/src/http_auth.c b/src/http_auth.c index 8dfe9a6..7031fa2 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -887,6 +887,7 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p *(dkv[i].ptr) = c + dkv[i].key_len; c += strlen(c) - 1; } + break; } } } @@ -981,6 +982,22 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p buffer_free(password); + /* detect if attacker is attempting to reuse valid digest for one uri + * on a different request uri. Might also happen if intermediate proxy + * altered client request line. (Altered request would not result in + * the same digest as that calculated by the client.) */ + { + const size_t ulen = strlen(uri); + const size_t rlen = buffer_string_length(con->request.uri); + if (!buffer_is_equal_string(con->request.uri, uri, ulen) + && !(rlen < ulen && 0 == memcmp(con->request.uri->ptr, uri, rlen) && uri[rlen] == '?')) { + log_error_write(srv, __FILE__, __LINE__, "sbssss", + "digest: auth failed: uri mismatch (", con->request.uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + buffer_free(b); + return -1; + } + } + if (algorithm && strcasecmp(algorithm, "md5-sess") == 0) { li_MD5_Init(&Md5Ctx); @@ -1054,6 +1071,28 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p return 0; } + /* check age of nonce. Note that rand() is used in nonce generation + * in http_auth_digest_generate_nonce(). If that were replaced + * with nanosecond time, then nonce secret would remain unique enough + * for the purposes of Digest auth, and would be reproducible (and + * verifiable) if nanoseconds were inclued with seconds as part of the + * nonce "timestamp:secret". Since that is not done, timestamp in + * nonce could theoretically be modified and still produce same md5sum, + * but that is highly unlikely within a 10 min (moving) window of valid + * time relative to current time (now) */ + { + time_t ts = 0; + const unsigned char * const nonce_uns = (unsigned char *)nonce; + for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { + ts = (ts << 4) + hex2int(nonce_uns[i]); + } + if (i != 8 || nonce[8] != ':' + || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ + buffer_free(b); + return -2; /* nonce is stale; have client regenerate digest */ + } /*(future: might send nextnonce when expiration is imminent)*/ + } + /* remember the username */ buffer_copy_string(p->auth_user, username); diff --git a/src/mod_auth.c b/src/mod_auth.c index cfadba4..d4520ad 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -287,7 +287,7 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { } } - if (!auth_satisfied) { + if (1 != auth_satisfied) { /*(0 or -2)*/ data_string *method, *realm; method = (data_string *)array_get_element(req, "method"); realm = (data_string *)array_get_element(req, "realm"); @@ -311,8 +311,13 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); buffer_append_string_buffer(p->tmp_buf, realm->value); buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\"")); + buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts); + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":")); buffer_append_string(p->tmp_buf, hh); buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); + if (-2 == auth_satisfied) { + buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true")); + } response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); } else { diff --git a/tests/mod-auth.t b/tests/mod-auth.t index cc03aa8..ba76b04 100755 --- a/tests/mod-auth.t +++ b/tests/mod-auth.t @@ -8,7 +8,7 @@ BEGIN { use strict; use IO::Socket; -use Test::More tests => 19; +use Test::More tests => 20; use LightyTest; my $tf = LightyTest->new(); @@ -133,6 +133,9 @@ EOF $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ]; ok($tf->handle_http($t) == 0, 'Digest-Auth: missing qop, no crash'); +# (Note: test case is invalid; mismatch between request line and uri="..." +# is not what is intended to be tested here, but that is what is invalid) +# https://redmine.lighttpd.net/issues/477 ## this should not crash $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ]; ok($tf->handle_http($t) == 0, 'Basic-Auth: Invalid Base64'); - $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ]; +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; ok($tf->handle_http($t) == 0, 'Digest-Auth: md5-sess + missing cnonce'); -$t->{REQUEST} = ( <{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ]; -ok($tf->handle_http($t) == 0, 'Digest-Auth: trailing WS'); + ); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401, 'WWW-Authenticate' => '/, stale=true$/' } ]; +ok($tf->handle_http($t) == 0, 'Digest-Auth: stale nonce'); + +$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401, 'WWW-Authenticate' => '/, stale=true$/' } ]; +ok($tf->handle_http($t) == 0, 'Digest-Auth: trailing WS, stale nonce'); commit adf91591fc8492a7a5d5de1fa1526fd079781840 Author: Glenn Strauss Date: Sun Jul 17 00:30:24 2016 -0400 [doc] update memcache references to memcached diff --git a/doc/outdated/cml.txt b/doc/outdated/cml.txt index 10fac70..32ae1d5 100644 --- a/doc/outdated/cml.txt +++ b/doc/outdated/cml.txt @@ -206,9 +206,9 @@ of sending the big image: :: Installation ============ -You need `lua `_ and should install `libmemcache-1.3.x `_ and have to configure lighttpd with: :: +You need `lua `_ and should install `memcached `_ and have to configure lighttpd with: :: - ./configure ... --with-lua --with-memcache + ./configure ... --with-lua --with-memcached To use the plugin you have to load it: :: diff --git a/doc/outdated/trigger_b4_dl.txt b/doc/outdated/trigger_b4_dl.txt index f5c9d29..0bfd099 100644 --- a/doc/outdated/trigger_b4_dl.txt +++ b/doc/outdated/trigger_b4_dl.txt @@ -33,7 +33,7 @@ Requirements ------------ * libpcre - * libgdbm or libmemcache + * libgdbm or libmemcached Options ======= commit 393dfd8cb9b010564a51a819627aa77cf52c030f Author: Glenn Strauss Date: Sun Jul 17 14:54:03 2016 -0400 [mod_ssi] fix #config sizefmt="bytes" diff --git a/src/mod_ssi.c b/src/mod_ssi.c index 94b5aeb..c9b59a6 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -758,7 +758,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const } else if (0 == strcmp(l[i], "sizefmt")) { if (0 == strcmp(l[i+1], "abbrev")) { p->sizefmt = 1; - } else if (0 == strcmp(l[i+1], "abbrev")) { + } else if (0 == strcmp(l[i+1], "bytes")) { p->sizefmt = 0; } else { log_error_write(srv, __FILE__, __LINE__, "sssss", commit acad2c903af14c19c640f927860ce6ef6071aed0 Author: Glenn Strauss Date: Sun Jul 17 16:13:31 2016 -0400 fix some warnings reported by cppcheck fix some warnings reported by cppcheck and change mod_skeleton.c to use buffer_string_length() diff --git a/src/array.c b/src/array.c index 21c3ba4..802f60d 100644 --- a/src/array.c +++ b/src/array.c @@ -353,7 +353,7 @@ int array_print(array *a, int depth) { int j; if (i && (i % 5) == 0) { - fprintf(stdout, "# %zd\n", i); + fprintf(stdout, "# %zu\n", i); array_print_indent(depth + 1); } fprintf(stdout, "\"%s\"", du->key->ptr); @@ -367,7 +367,7 @@ int array_print(array *a, int depth) { } if (!(i && (i - 1 % 5) == 0)) { array_print_indent(depth + 1); - fprintf(stdout, "# %zd\n", i); + fprintf(stdout, "# %zu\n", i); } array_print_indent(depth); fprintf(stdout, ")"); diff --git a/src/buffer.c b/src/buffer.c index b79ea99..32390fa 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1094,7 +1094,7 @@ void print_backtrace(FILE *file) { void log_failed_assert(const char *filename, unsigned int line, const char *msg) { /* can't use buffer here; could lead to recursive assertions */ - fprintf(stderr, "%s.%d: %s\n", filename, line, msg); + fprintf(stderr, "%s.%u: %s\n", filename, line, msg); print_backtrace(stderr); fflush(stderr); abort(); diff --git a/src/connections-glue.c b/src/connections-glue.c index a733044..eee1907 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -242,7 +242,7 @@ int connection_handle_read(server *srv, connection *con) { len = recv(con->fd, mem, mem_len, 0); #else /* __WIN32 */ - if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { + if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) { toread = 4096; } else if (toread > MAX_READ_LIMIT) { diff --git a/src/mod_cgi.c b/src/mod_cgi.c index d43fe6d..38eb169 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -387,7 +387,7 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) { #if defined(__WIN32) buffer_string_prepare_copy(hctx->response, 4 * 1024); #else - if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { + if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) { buffer_string_prepare_copy(hctx->response, 4 * 1024); } else { if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 01e8662..1566299 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -437,8 +437,8 @@ static void http_dirls_sort(dirls_entry_t **ent, int num) { * conversion is simple but not perfect */ static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { - const char unit[] = "KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */ - const char *u = unit - 1; /* u will always increment at least once */ + const char unit[] = " KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */ + const char *u = unit; /* u will always increment at least once */ int remain; size_t buflen; diff --git a/src/mod_rewrite.c b/src/mod_rewrite.c index 721c5f1..904b889 100644 --- a/src/mod_rewrite.c +++ b/src/mod_rewrite.c @@ -495,8 +495,6 @@ URIHANDLER_FUNC(mod_rewrite_uri_handler) { if (!p->conf.rewrite) return HANDLER_GO_ON; return process_rewrite_rules(srv, con, p, p->conf.rewrite); - - return HANDLER_GO_ON; } #endif diff --git a/src/mod_skeleton.c b/src/mod_skeleton.c index 77fd53c..c00bc85 100644 --- a/src/mod_skeleton.c +++ b/src/mod_skeleton.c @@ -163,25 +163,24 @@ static int mod_skeleton_patch_connection(server *srv, connection *con, plugin_da URIHANDLER_FUNC(mod_skeleton_uri_handler) { plugin_data *p = p_d; - int s_len; - size_t k, i; + size_t s_len; + size_t k; UNUSED(srv); if (con->mode != DIRECT) return HANDLER_GO_ON; - if (con->uri.path->used == 0) return HANDLER_GO_ON; + s_len = buffer_string_length(con->uri.path); + if (0 == s_len) return HANDLER_GO_ON; mod_skeleton_patch_connection(srv, con, p); - s_len = con->uri.path->used - 1; - for (k = 0; k < p->conf.match->used; k++) { data_string *ds = (data_string *)p->conf.match->data[k]; - int ct_len = ds->value->used - 1; + size_t ct_len = buffer_string_length(ds->value); if (ct_len > s_len) continue; - if (ds->value->used == 0) continue; + if (ct_len == 0) continue; if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { con->http_status = 403; commit 9c49dc9a5c9cb67d3637ee70839704849bd90e8a Author: Glenn Strauss Date: Sun Jul 17 23:21:50 2016 -0400 workaround clang compiler warning diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c index 043defb..5e82ef5 100644 --- a/src/mod_accesslog.c +++ b/src/mod_accesslog.c @@ -580,15 +580,17 @@ SETDEFAULTS_FUNC(log_access_open) { f->opt |= FORMAT_FLAG_TIME_USEC; srv->srvconf.high_precision_timestamps = 1; } else if (FORMAT_TIME_USED == f->field) { - const char * const ptr = f->string->ptr; - if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1; if (buffer_string_is_empty(f->string) - || 0 == strcmp(ptr, "s") || 0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC; - else if (0 == strcmp(ptr, "ms") || 0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC; - else if (0 == strcmp(ptr, "us") || 0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC; - else if (0 == strcmp(ptr, "ns") || 0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC; + || buffer_is_equal_string(f->string, CONST_STR_LEN("s")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("sec"))) f->opt |= FORMAT_FLAG_TIME_SEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ms")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("us")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ns")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC; else { log_error_write(srv, __FILE__, __LINE__, "sb", "invalid time unit in %{UNIT}T:", s->format); commit 72abc87b40ccbdd07e53ad6ba08d307ce4f4c9d5 Author: fbrosson Date: Mon Jul 18 01:03:18 2016 +0000 [autobuild] move inet_pton detection later HAVE_INET_PTON was probably not being defined on Solaris. While at it, also add detection for accept() in libnetwork for Haiku. github: closes #68 commit a3ec906ef97fae05291f335210d66ae4b7260d4a Author: Glenn Strauss Date: Mon Jul 18 04:40:57 2016 -0400 [core] #include for FIONREAD (fixes #2726) illumos (OpenIndiana) gets FIONREAD from x-ref: "lighttpd 1.4.40 compilation fails on illumos (OpenIndiana)" https://redmine.lighttpd.net/issues/2735 diff --git a/src/sys-socket.h b/src/sys-socket.h index 04c9d94..e3ca029 100644 --- a/src/sys-socket.h +++ b/src/sys-socket.h @@ -21,6 +21,11 @@ #include #include + +#ifdef HAVE_SYS_FILIO_H +#include /* FIONREAD (for illumos (OpenIndiana)) */ +#endif + #endif #endif commit 4d920466f7a8163adb3de2862b48c53b24a9a915 Author: Glenn Strauss Date: Mon Jul 18 14:24:39 2016 -0400 [autobuild] clock_gettime() -lrt with glibc < 2.17 clock_gettime() needs -lrt with glibc < 2.17, and possibly other platforms On systems without clock_gettime (-cough- Mac OSX -cough-), use gettimeofday() (deprecated in POSIX.1-2008) which is slightly lower precision, but reasonably fast in execution. References: http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x http://stackoverflow.com/questions/11680461/monotonic-clock-on-osx https://discussions.apple.com/thread/6023936?tstart=0 diff --git a/src/connections.c b/src/connections.c index e73a667..507838c 100644 --- a/src/connections.c +++ b/src/connections.c @@ -692,7 +692,7 @@ static int connection_handle_read_state(server *srv, connection *con) { if (con->request_count > 1 && is_request_start) { con->request_start = srv->cur_ts; if (con->conf.high_precision_timestamps) - clock_gettime(CLOCK_REALTIME, &con->request_start_hp); + log_clock_gettime_realtime(&con->request_start_hp); } /* if there is a \r\n\r\n in the chunkqueue @@ -1000,7 +1000,7 @@ int connection_state_machine(server *srv, connection *con) { con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) - clock_gettime(CLOCK_REALTIME, &con->request_start_hp); + log_clock_gettime_realtime(&con->request_start_hp); con->request_count++; con->loops_per_request = 0; diff --git a/src/log.c b/src/log.c index b10bebe..21a2916 100644 --- a/src/log.c +++ b/src/log.c @@ -28,6 +28,26 @@ # define O_LARGEFILE 0 #endif +#ifndef HAVE_CLOCK_GETTIME +#ifdef HAVE_SYS_TIME_H +# include /* gettimeofday() */ +#endif +#endif + +int log_clock_gettime_realtime (struct timespec *ts) { + #ifdef HAVE_CLOCK_GETTIME + return clock_gettime(CLOCK_REALTIME, ts); + #else + /* Mac OSX does not provide clock_gettime() + * e.g. defined(__APPLE__) && defined(__MACH__) */ + struct timeval tv; + gettimeofday(&tv, NULL); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return 0; + #endif +} + /* retry write on EINTR or when not all data was written */ ssize_t write_all(int fd, const void* buf, size_t count) { ssize_t written = 0; diff --git a/src/log.h b/src/log.h index 0570f0b..32ce8d4 100644 --- a/src/log.h +++ b/src/log.h @@ -4,6 +4,9 @@ #include "server.h" +struct timespec; /* declaration */ +int log_clock_gettime_realtime (struct timespec *ts); + ssize_t write_all(int fd, const void* buf, size_t count); /* Close fd and _try_ to get a /dev/null for it instead. diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c index 5e82ef5..fba1cb4 100644 --- a/src/mod_accesslog.c +++ b/src/mod_accesslog.c @@ -765,7 +765,7 @@ REQUESTDONE_FUNC(log_access_write) { off_t t; /*(expected to be 64-bit since large file support enabled)*/ long ns; if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { - if (0 == ts.tv_sec) clock_gettime(CLOCK_REALTIME, &ts); + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); t = (off_t)ts.tv_sec; ns = ts.tv_nsec; } else { @@ -787,7 +787,7 @@ REQUESTDONE_FUNC(log_access_write) { long ns; char *ptr; if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { - if (0 == ts.tv_sec) clock_gettime(CLOCK_REALTIME, &ts); + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); ns = ts.tv_nsec; } else { ns = con->request_start_hp.tv_nsec; @@ -880,9 +880,13 @@ REQUESTDONE_FUNC(log_access_write) { } else { const struct timespec * const bs = &con->request_start_hp; off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/ - if (0 == ts.tv_sec) clock_gettime(CLOCK_REALTIME, &ts); + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec); - if (f->opt & FORMAT_FLAG_TIME_MSEC) { + if (tdiff <= 0) { + /* sanity check for time moving backwards + * (daylight savings adjustment or leap seconds or ?) */ + tdiff = -1; + } else if (f->opt & FORMAT_FLAG_TIME_MSEC) { tdiff += 999999; /* ceil */ tdiff /= 1000000; } else if (f->opt & FORMAT_FLAG_TIME_USEC) { commit d506f4a569e9d5a7b3c215333bc5916f17f3a4cc Author: Glenn Strauss Date: Mon Jul 18 18:01:45 2016 -0400 minor: spelling changes in some comments/messages diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 1566299..919497b 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -437,7 +437,7 @@ static void http_dirls_sort(dirls_entry_t **ent, int num) { * conversion is simple but not perfect */ static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { - const char unit[] = " KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */ + const char unit[] = " KMGTPE"; /* Kilo, Mega, Giga, Tera, Peta, Exa */ const char *u = unit; /* u will always increment at least once */ int remain; size_t buflen; diff --git a/src/mod_skeleton.c b/src/mod_skeleton.c index c00bc85..9ad6c63 100644 --- a/src/mod_skeleton.c +++ b/src/mod_skeleton.c @@ -13,7 +13,7 @@ /** * this is a skeleton for a lighttpd plugin * - * just replaces every occurance of 'skeleton' by your plugin name + * just replaces every occurrence of 'skeleton' by your plugin name * * e.g. in vim: * diff --git a/src/mod_trigger_b4_dl.c b/src/mod_trigger_b4_dl.c index fc99976..6330b49 100644 --- a/src/mod_trigger_b4_dl.c +++ b/src/mod_trigger_b4_dl.c @@ -253,7 +253,7 @@ SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { #if (!defined(HAVE_GDBM_H) && !defined(USE_MEMCACHED)) || !defined(HAVE_PCRE_H) log_error_write(srv, __FILE__, __LINE__, "s", - "(either gdbm or libmemcache) and pcre are require, but were not found, aborting"); + "(either gdbm or libmemcached) and pcre are require, but were not found, aborting"); return HANDLER_ERROR; #endif } @@ -608,7 +608,7 @@ int mod_trigger_b4_dl_plugin_init(plugin *p) { #else -#pragma message("(either gdbm or libmemcache) and pcre are required, but were not found") +#pragma message("(either gdbm or libmemcached) and pcre are required, but were not found") int mod_trigger_b4_dl_plugin_init(plugin *p); int mod_trigger_b4_dl_plugin_init(plugin *p) { commit 779c133c16f9af168b004dce7a2a64f16c1cb3a4 Author: Glenn Strauss Date: Mon Jul 18 22:59:33 2016 -0400 [security] do not emit HTTP_PROXY to CGI env Strip bogus "Proxy" header before creating subprocess environment. (mod_cgi, mod_fastcgi, mod_scgi, mod_ssi, mod_proxy) Do not emit HTTP_PROXY to subprocess environment. Some executables use HTTP_PROXY to configure outgoing proxy. This is not a lighttpd security issue per se, but this change to lighttpd adds a layer of defense to protect backend processes which might be vulnerable due to blindly using this untrusted environment variable. The HTTP_PROXY environment variable should not be trusted by a program running in a CGI-like environment. Mitigation in lighttpd <= 1.4.40 is to reject requests w/ Proxy header: * Create "/path/to/deny-proxy.lua", read-only to lighttpd, with content: if (lighty.request["Proxy"] == nil) then return 0 else return 403 end * Modify lighttpd.conf to load mod_magnet and run lua code server.modules += ( "mod_magnet" ) magnet.attract-raw-url-to = ( "/path/to/deny-proxy.lua" ) References: https://www.kb.cert.org/vuls/id/797896 CGI web servers assign Proxy header values from client requests to internal HTTP_PROXY environment variables https://httpoxy.org/ httpoxy: A CGI application vulnerability diff --git a/src/mod_cgi.c b/src/mod_cgi.c index 38eb169..e6212fe 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -1289,6 +1289,13 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ ds = (data_string *)con->request.headers->data[n]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { + continue; + } + buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 1); cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 31c2e62..52d707d 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -1888,6 +1888,13 @@ static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_dat ds = (data_string *)con->request.headers->data[i]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { + continue; + } + buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1); FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)),con); diff --git a/src/mod_proxy.c b/src/mod_proxy.c index 3b87785..b101baf 100644 --- a/src/mod_proxy.c +++ b/src/mod_proxy.c @@ -494,6 +494,10 @@ static int proxy_create_env(server *srv, handler_ctx *hctx) { if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Connection"))) continue; if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) continue; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); diff --git a/src/mod_scgi.c b/src/mod_scgi.c index 584c20a..f3ec978 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -1536,6 +1536,13 @@ static int scgi_env_add_request_headers(server *srv, connection *con, plugin_dat ds = (data_string *)con->request.headers->data[i]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { + continue; + } + buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1); scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)); diff --git a/src/mod_ssi.c b/src/mod_ssi.c index c9b59a6..8dda845 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -165,7 +165,14 @@ static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { /* don't forward the Authorization: Header */ - if (0 == strcasecmp(ds->key->ptr, "AUTHORIZATION")) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Authorization"))) { + continue; + } + + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { continue; } commit 1ebc83f11f8f816c825bd405a8f2d8aba4b58d7d Author: Glenn Strauss Date: Tue Jul 19 04:03:14 2016 -0400 [build_cmake] clock_gettime() -lrt w/ glibc < 2.17 (fixes #2737) clock_gettime() needs -lrt with glibc < 2.17, and possibly other platforms This commit contains fixes for CMake and SCONS See also commit:4d920466 which updated configure.ac for same x-ref: "1.4.40 compiling issuses on Debian Wheezy" https://redmine.lighttpd.net/issues/2737 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 31f57d5..1456f54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ include(CheckCSourceCompiles) include(CheckIncludeFiles) include(CheckFunctionExists) +include(CheckSymbolExists) include(CheckVariableExists) include(CheckTypeSize) include(CheckLibraryExists) @@ -157,6 +158,10 @@ check_function_exists(issetugid HAVE_ISSETUGID) check_function_exists(inet_pton HAVE_INET_PTON) check_function_exists(memset_s HAVE_MEMSET_S) check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) +check_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME) +if (NOT HAVE_CLOCK_GETTIME) + check_library_exists(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) +endif() check_c_source_compiles(" #include #include commit 78c79ead4a1d64f9c950c3e8cb15cf3dd976b39a Author: Glenn Strauss Date: Tue Jul 19 17:23:19 2016 -0400 [core] avoid spurious trace and error abort HANDLER_COMEBACK and HANDLER_ERROR are valid return values from dynamic fdevent handlers. Do not abort if HANDLER_ERROR is returned. diff --git a/src/server.c b/src/server.c index 3bd860f..e74ccd0 100644 --- a/src/server.c +++ b/src/server.c @@ -1770,18 +1770,11 @@ int main (int argc, char **argv) { /* n is the number of events */ int revents; int fd_ndx; -#if 0 - if (n > 0) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "polls:", n); - } -#endif last_active_ts = srv->cur_ts; fd_ndx = -1; do { fdevent_handler handler; void *context; - handler_t r; fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ @@ -1790,25 +1783,7 @@ int main (int argc, char **argv) { fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); - -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sdd", - "event for", fd, revents); -#endif - switch (r = (*handler)(srv, context, revents)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - case HANDLER_WAIT_FOR_EVENT: - case HANDLER_WAIT_FOR_FD: - break; - case HANDLER_ERROR: - /* should never happen */ - SEGFAULT(); - break; - default: - log_error_write(srv, __FILE__, __LINE__, "d", r); - break; - } + (*handler)(srv, context, revents); } while (--n > 0); } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", commit cb468d333cd029b7b7463a17409e16ebf01f5711 Author: Glenn Strauss Date: Wed Jul 20 05:43:39 2016 -0400 [core] stay in CON_STATE_CLOSE until done with req Do not switch to CON_STATE_ERROR upon idle timeout if already in CON_STATE_CLOSE. Changing to CON_STATE_ERROR might keep resetting con->close_timeout_ts if repeated calls to shutdown() succeed. diff --git a/src/server.c b/src/server.c index e74ccd0..35f9a6e 100644 --- a/src/server.c +++ b/src/server.c @@ -1590,7 +1590,11 @@ int main (int argc, char **argv) { int changed = 0; int t_diff; - if (waitevents & FDEVENT_IN) { + if (con->state == CON_STATE_CLOSE) { + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { + changed = 1; + } + } else if (waitevents & FDEVENT_IN) { if (con->request_count == 1 || con->state != CON_STATE_READ) { /* e.g. CON_STATE_READ_POST || CON_STATE_WRITE */ if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ @@ -1649,10 +1653,6 @@ int main (int argc, char **argv) { } } - if (con->state == CON_STATE_CLOSE && (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT)) { - changed = 1; - } - /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; commit cd33554b74fd39ecd2e7c367070534da178d5147 Author: Glenn Strauss Date: Thu Jul 21 01:42:35 2016 -0400 [core] $HTTP["remoteip"] must handle IPv6 w/o [] [core] $HTTP["remoteip"] must handle IPv6 w/o [] (existing behavior) This was inadvertently broken in lighttpd 1.4.40 when IP address normalization was added. In $HTTP["remoteip"], IPv6 is now accepted with or without '[]'. http_request_host_normalize() expects IPv6 with '[]', and config processing at runtime expects COMP_HTTP_REMOTE_IP compared without '[]', so '[]' is stripped (internally) after normalization diff --git a/src/configparser.y b/src/configparser.y index 349fbfe..f5ba032 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -114,6 +114,34 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { return op1; } +static int configparser_remoteip_normalize_compat(buffer *rvalue) { + /* $HTTP["remoteip"] IPv6 accepted with or without '[]' for config compat + * http_request_host_normalize() expects IPv6 with '[]', + * and config processing at runtime expects COMP_HTTP_REMOTE_IP + * compared without '[]', so strip '[]' after normalization */ + buffer *b = buffer_init(); + int rc; + + if (rvalue->ptr[0] != '[') { + buffer_append_string_len(b, CONST_STR_LEN("[")); + buffer_append_string_buffer(b, rvalue); + buffer_append_string_len(b, CONST_STR_LEN("]")); + } else { + buffer_append_string_buffer(b, rvalue); + } + + rc = http_request_host_normalize(b); + + if (0 == rc) { + /* remove surrounding '[]' */ + size_t blen = buffer_string_length(b); + if (blen > 1) buffer_copy_string_len(rvalue, b->ptr+1, blen-2); + } + + buffer_free(b); + return rc; +} + } %parse_failure { @@ -483,12 +511,14 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio } else if (COMP_HTTP_REMOTE_IP == dc->comp && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) { - char * const slash = strchr(rvalue->ptr, '/'); - if (NULL != slash && slash != rvalue->ptr){/*(skip AF_UNIX /path/file)*/ + char * const slash = strchr(rvalue->ptr, '/'); /* CIDR mask */ + char * const colon = strchr(rvalue->ptr, ':'); /* IPv6 */ + if (NULL != slash && slash == rvalue->ptr){/*(skip AF_UNIX /path/file)*/ + } + else if (NULL != slash) { char *nptr; const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10); - if (*nptr || 0 == nm_bits - || nm_bits > (rvalue->ptr[0] == '[' ? 128 : 32)) { + if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) { /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/ fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr); ctx->ok = 0; @@ -496,7 +526,9 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio else { int rc; buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/ - rc = http_request_host_normalize(rvalue); + rc = (NULL == colon) + ? http_request_host_normalize(rvalue) + : configparser_remoteip_normalize_compat(rvalue); buffer_append_string_len(rvalue, CONST_STR_LEN("/")); buffer_append_int(rvalue, (int)nm_bits); if (0 != rc) { @@ -506,7 +538,10 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio } } else { - if (http_request_host_normalize(rvalue)) { + int rc = (NULL == colon) + ? http_request_host_normalize(rvalue) + : configparser_remoteip_normalize_compat(rvalue); + if (0 != rc) { fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); ctx->ok = 0; } commit b43fc006be10b7c73c2e99896ec9e82c1eaa0dd4 Author: Glenn Strauss Date: Thu Jul 21 11:19:06 2016 -0400 [mod_status] show keep-alive status w/ text output (fixes #2740) x-ref: "mod_status with "?auto" modifier not showing keep-alive (k) status on Scoreboard" https://redmine.lighttpd.net/issues/2740 "server-status - additional stats - keepalive" https://redmine.lighttpd.net/issues/1202 diff --git a/src/mod_status.c b/src/mod_status.c index a37b6cd..647f403 100644 --- a/src/mod_status.c +++ b/src/mod_status.c @@ -638,7 +638,10 @@ static handler_t mod_status_handle_server_status_text(server *srv, connection *c buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: ")); for (k = 0; k < srv->conns->used; k++) { connection *c = srv->conns->ptr[k]; - const char *state = connection_get_short_state(c->state); + const char *state = + (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) + ? "k" + : connection_get_short_state(c->state); buffer_append_string_len(b, state, 1); } for (l = 0; l < srv->conns->size - srv->conns->used; l++) { commit ed340897a2ca7abce52edca6d5f8e097b41c43e4 Author: Glenn Strauss Date: Sat Jul 23 01:21:33 2016 -0400 do not set REDIRECT_URI in mod_magnet, mod_rewrite (#2738) reverts commit:b473220d x-ref: "mediawiki redirect loop if REQUEST_URI not orig req in 1.4.40" https://redmine.lighttpd.net/issues/2738 diff --git a/src/mod_magnet.c b/src/mod_magnet.c index 6698afd..db7b7a0 100644 --- a/src/mod_magnet.c +++ b/src/mod_magnet.c @@ -1015,12 +1015,6 @@ static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, bu result = HANDLER_FINISHED; } else if (MAGNET_RESTART_REQUEST == lua_return_value) { - if (!buffer_is_equal(con->request.uri, con->request.orig_uri) - && !array_get_element(con->environment, "REDIRECT_URI")) { - array_set_key_value(con->environment, - CONST_STR_LEN("REDIRECT_URI"), - CONST_BUF_LEN(con->request.orig_uri)); - } result = HANDLER_COMEBACK; } diff --git a/src/mod_rewrite.c b/src/mod_rewrite.c index 904b889..c3c71b8 100644 --- a/src/mod_rewrite.c +++ b/src/mod_rewrite.c @@ -444,13 +444,6 @@ static handler_t process_rewrite_rules(server *srv, connection *con, plugin_data if (rule->once) hctx->state = REWRITE_STATE_FINISHED; - if (!buffer_is_equal(con->request.uri, con->request.orig_uri) - && !array_get_element(con->environment, "REDIRECT_URI")) { - array_set_key_value(con->environment, - CONST_STR_LEN("REDIRECT_URI"), - CONST_BUF_LEN(con->request.orig_uri)); - } - return HANDLER_COMEBACK; } #undef N commit 9af58a9716b120209c4011b265657b32a414dff9 Author: Glenn Strauss Date: Sat Jul 23 01:24:25 2016 -0400 revert 1.4.40 swap of REQUEST_URI, REDIRECT_URI (fixes #2738) reverts part of commit:dbdab5db which swapped REQUEST_URI, REDIRECT_URI x-ref: "mediawiki redirect loop if REQUEST_URI not orig req in 1.4.40" https://redmine.lighttpd.net/issues/2738 Explanation: REQUEST_URI and REDIRECT_URI are not part of CGI standard environment. The reason for their existence is that PATH_INFO in CGI environment may be different from the path in the current request. The main reason for this potential difference is that the URI path is normalized to a path in the filesystem and tested against the filesystem to determine which part is SCRIPT_NAME and which part is PATH_INFO. In case-insensitive filesystems, the URI might be lowercased before testing against the filesystem, leading to loss of case-sensitive submission in any resulting PATH_INFO. Also, duplicated slashes "///" and directory references "/." and "/.." are removed, including prior path component in the case of "/..". This might be undesirable when the information after the SCRIPT_NAME is virtual information and there target script needs the virtual path preserved as-is. In that case, the target script can re-parse REQUEST_URI (or REDIRECT_URI, as appropriate) to obtain the unmodified information from the URI. con->request.uri is equivalent to con->request.orig_uri unless the request has been internally rewritten (e.g. by mod_rewrite, mod_magnet, others), in which case con->request.orig_uri is the request made by the client, and con->request.uri is the current URI being processed. Historical REQUEST_URI (environment variable) lighttpd inconsistencies - mod_cml set REQUEST_URI to con->request.orig_uri - mod_cgi set REQUEST_URI to con->request.orig_uri - mod_fastcgi set REQUEST_URI to con->request.orig_uri - mod_scgi set REQUEST_URI to con->request.orig_uri - mod_ssi set REQUEST_URI to current con->request.uri - mod_magnet set MAGNET_ENV_REQUEST_URI to current con->request.uri and MAGNET_ENV_REQUEST_ORIG_URI to con->request.orig_uri Historical REDIRECT_URI (environment variable) previously set only in mod_fastcgi and mod_scgi, and set to con->request.uri Since lighttpd 1.4.40 provides REDIRECT_URI with con->request.orig_uri, changes were made to REQUEST_URI for consistency, with the hope that there would be little impact to existing configurations since the request uri and original request uri are the same unless there has been an internal redirect. It turns out that various PHP frameworks use REQUEST_URI and require that it be the original URI requested by client. Therefore, this change is being reverted, and lighttpd will set REQUEST_URI to con->request.orig_uri in mod_cgi, mod_fastcgi, mod_scgi as was done in lighttpd 1.4.39 and earlier. Similarly, REDIRECT_URI also has the prior behavior in mod_fastcgi and mod_scgi, and added to mod_cgi. A future release of lighttpd might change mod_ssi to be consistent with the other modules in setting REQUEST_URI to con->request.orig_uri and to add REDIRECT_URI, when an internal redirect has occurred. diff --git a/src/connections.c b/src/connections.c index 507838c..670000a 100644 --- a/src/connections.c +++ b/src/connections.c @@ -1110,13 +1110,11 @@ int connection_state_machine(server *srv, connection *con) { con->response.content_length = -1; con->response.transfer_encoding = 0; - array_set_key_value(con->environment, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.orig_uri)); con->error_handler_saved_status = con->http_status; con->error_handler_saved_method = con->request.http_method; con->request.http_method = HTTP_METHOD_GET; } else { /*(preserve behavior for server.error-handler-404)*/ - array_set_key_value(con->environment, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(error_handler)); con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ } diff --git a/src/mod_cgi.c b/src/mod_cgi.c index e6212fe..52e7f5d 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -528,13 +528,6 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) { return FDEVENT_HANDLED_FINISHED; } - if (!buffer_is_equal(con->request.uri, con->request.orig_uri) - && !array_get_element(con->environment, "REDIRECT_URI")) { - array_set_key_value(con->environment, - CONST_STR_LEN("REDIRECT_URI"), - CONST_BUF_LEN(con->request.orig_uri)); - } - buffer_copy_buffer(con->request.uri, ds->value); if (con->request.content_length) { @@ -1211,10 +1204,9 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ } else { cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); } - if (con->error_handler_saved_status >= 0) { - cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.uri)); - } else { - cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { + cgi_env_add(&env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)); } /* set REDIRECT_STATUS for php compiled with --force-redirect * (if REDIRECT_STATUS has not already been set by error handler) */ diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 52d707d..4b0f8ba 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -1965,7 +1965,7 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, int request_id) { fcgi_extension_host *host= hctx->host; connection *con = hctx->remote_conn; - buffer * const req_uri = (con->error_handler_saved_status >= 0) ? con->request.uri : con->request.orig_uri; + buffer * const req_uri = con->request.orig_uri; server_socket *srv_sock = con->srv_socket; sock_addr our_addr; @@ -2142,6 +2142,9 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, int request_id) { } else { FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(req_uri)),con) } + if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)),con); + } if (!buffer_string_is_empty(con->uri.query)) { FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)),con) } else { diff --git a/src/mod_scgi.c b/src/mod_scgi.c index f3ec978..3d05769 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -1704,10 +1704,9 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) { scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); } - if (con->error_handler_saved_status >= 0) { - scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.uri)); - } else { - scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { + scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)); } if (!buffer_string_is_empty(con->uri.query)) { scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); diff --git a/tests/docroot/www/404.pl b/tests/docroot/www/404.pl index d4eb2ae..5b5672c 100755 --- a/tests/docroot/www/404.pl +++ b/tests/docroot/www/404.pl @@ -1,7 +1,6 @@ #!/usr/bin/env perl -my $request_uri = $ENV{'REQUEST_URI'}; # server.error-handler-404 -my $redirect_uri= $ENV{'REDIRECT_URI'}; # server.error-handler +my $request_uri = $ENV{'REQUEST_URI'}; if ($request_uri =~ m/^\/dynamic\/200\// ) { print "Status: 200\n", @@ -29,7 +28,7 @@ elsif ($request_uri =~ m/^\/send404\.pl/ ) { elsif ($request_uri =~ m/^\/dynamic\/nostatus\// ) { print ("found here\n"); } -elsif ($redirect_uri =~ m/^\/dynamic\/redirect_status\// ) { +elsif ($request_uri =~ m/^\/dynamic\/redirect_status\// ) { print "Status: $ENV{'REDIRECT_STATUS'}\n", "Content-Type: text/plain\n", "\n", commit 38139fa1a91a3bd1b76827aa4b388cd14569d56e Author: Glenn Strauss Date: Mon Jul 25 00:43:05 2016 -0400 [core] permit IPv6 address scope identifier getaddrinfo() on permits a scope identifier to be part of the IPv6 address string, so permit this syntax in $SERVER["socket"] validation. x-ref: https://tools.ietf.org/html/rfc4007#section-11 https://en.wikipedia.org/wiki/IPv6_address#Link-local_addresses_and_zone_indices diff --git a/src/request.c b/src/request.c index 8c09fe3..a2de944 100644 --- a/src/request.c +++ b/src/request.c @@ -276,8 +276,10 @@ int http_request_host_normalize(buffer *b) { struct in6_addr addr; char *bracket = b->ptr+blen-1; + char *percent = strchr(b->ptr+1, '%'); + size_t len; int rc; - char buf[INET6_ADDRSTRLEN]; + char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ if (blen <= 2) return -1; /*(invalid "[]")*/ if (*bracket != ']') { bracket = (char *)memchr(b->ptr+1, ']', blen-1); @@ -296,13 +298,22 @@ int http_request_host_normalize(buffer *b) { } *bracket = '\0';/*(terminate IPv6 string)*/ + if (percent) *percent = '\0'; /*(remove %interface from address)*/ rc = inet_pton(AF_INET6, b->ptr+1, &addr); + if (percent) *percent = '%'; /*(restore %interface)*/ *bracket = ']'; /*(restore bracket)*/ if (1 != rc) return -1; inet_ntop(AF_INET6,(const void *)&addr, buf, sizeof(buf)); + len = strlen(buf); + if (percent) { + if (percent > bracket) return -1; + if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; + memcpy(buf+len, percent, (size_t)(bracket - percent)); + len += (size_t)(bracket - percent); + } buffer_string_set_length(b, 1); /* truncate after '[' */ - buffer_append_string(b, buf); + buffer_append_string_len(b, buf, len); buffer_append_string_len(b, CONST_STR_LEN("]")); #else commit 565dec2ff1639119130c1013a26586b9561b80cb Author: Glenn Strauss Date: Tue Jul 26 15:55:45 2016 -0400 [core] consolidate duplicated response_end code diff --git a/src/connections.c b/src/connections.c index 670000a..790241a 100644 --- a/src/connections.c +++ b/src/connections.c @@ -121,9 +121,6 @@ static int connection_del(server *srv, connection *con) { static int connection_close(server *srv, connection *con) { #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; -#endif - -#ifdef USE_OPENSSL if (srv_sock->is_ssl) { if (con->ssl) SSL_free(con->ssl); con->ssl = NULL; @@ -157,6 +154,151 @@ static int connection_close(server *srv, connection *con) { return 0; } +static void connection_handle_close_state(server *srv, connection *con) { + /* we have to do the linger_on_close stuff regardless + * of con->keep_alive; even non-keepalive sockets may + * still have unread data, and closing before reading + * it will make the client not see all our output. + */ + int len; + char buf[1024]; + + len = read(con->fd, buf, sizeof(buf)); + if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); + } + + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { + connection_close(srv, con); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed for fd", con->fd); + } + } +} + +static void connection_handle_shutdown(server *srv, connection *con) { + int r; + +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; + if (srv_sock->is_ssl) { + int ret, ssl_r; + unsigned long err; + ERR_clear_error(); + switch ((ret = SSL_shutdown(con->ssl))) { + case 1: + /* ok */ + break; + case 0: + /* wait for fd-event + * + * FIXME: wait for fdevent and call SSL_shutdown again + * + */ + ERR_clear_error(); + if (-1 != (ret = SSL_shutdown(con->ssl))) break; + + /* fall through */ + default: + + switch ((ssl_r = SSL_get_error(con->ssl, ret))) { + case SSL_ERROR_ZERO_RETURN: + break; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + break; + case SSL_ERROR_SYSCALL: + /* perhaps we have error waiting in our error-queue */ + if (0 != (err = ERR_get_error())) { + do { + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + ssl_r, ret, + ERR_error_string(err, NULL)); + } while((err = ERR_get_error())); + } else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */ + switch(errno) { + case EPIPE: + case ECONNRESET: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", + ssl_r, ret, errno, + strerror(errno)); + break; + } + } + + break; + default: + while((err = ERR_get_error())) { + log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", + ssl_r, ret, + ERR_error_string(err, NULL)); + } + + break; + } + } + ERR_clear_error(); + } +#endif + + switch(r = plugins_call_handle_connection_close(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); + break; + } + + srv->con_closed++; + connection_reset(srv, con); + + /* close the connection */ + if ((0 == shutdown(con->fd, SHUT_WR))) { + con->close_timeout_ts = srv->cur_ts; + connection_set_state(srv, con, CON_STATE_CLOSE); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "shutdown for fd", con->fd); + } + } else { + connection_close(srv, con); + } +} + +static void connection_handle_response_end_state(server *srv, connection *con) { + /* log the request */ + /* (even if error, connection dropped, still write to access log if http_status) */ + if (con->http_status) { + plugins_call_handle_request_done(srv, con); + } + + if (con->state != CON_STATE_ERROR) srv->con_written++; + + if ((con->request.content_length + && (off_t)con->request.content_length > con->request_content_queue->bytes_in) + || con->state == CON_STATE_ERROR) { + /* request body is present and has not been read completely */ + con->keep_alive = 0; + } + + if (con->keep_alive) { + connection_reset(srv, con); +#if 0 + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; +#endif + connection_set_state(srv, con, CON_STATE_REQUEST_START); + } else { + connection_handle_shutdown(srv, con); + } +} + static void connection_handle_errdoc_init(server *srv, connection *con) { /* modules that produce headers required with error response should * typically also produce an error document. Make an exception for @@ -976,9 +1118,6 @@ connection *connection_accepted(server *srv, server_socket *srv_socket, sock_add int connection_state_machine(server *srv, connection *con) { int done = 0, r; -#ifdef USE_OPENSSL - server_socket *srv_sock = con->srv_socket; -#endif if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", @@ -990,13 +1129,13 @@ int connection_state_machine(server *srv, connection *con) { while (done == 0) { size_t ostate = con->state; + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) @@ -1009,11 +1148,6 @@ int connection_state_machine(server *srv, connection *con) { break; case CON_STATE_REQUEST_END: /* transient */ - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); @@ -1041,11 +1175,6 @@ int connection_state_machine(server *srv, connection *con) { * */ - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - switch (r = http_response_prepare(srv, con)) { case HANDLER_WAIT_FOR_EVENT: if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { @@ -1158,11 +1287,6 @@ int connection_state_machine(server *srv, connection *con) { * */ - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); @@ -1172,129 +1296,22 @@ int connection_state_machine(server *srv, connection *con) { connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ - /* log the request */ - - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - - if (con->request.content_length - && (off_t)con->request.content_length > con->request_content_queue->bytes_in) { - /* request body is present and has not been read completely */ - con->keep_alive = 0; - } - - plugins_call_handle_request_done(srv, con); - - srv->con_written++; - - if (con->keep_alive) { - connection_set_state(srv, con, CON_STATE_REQUEST_START); - -#if 0 - con->request_start = srv->cur_ts; - con->read_idle_ts = srv->cur_ts; -#endif - } else { - switch(r = plugins_call_handle_connection_close(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); - break; - } - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - switch (SSL_shutdown(con->ssl)) { - case 1: - /* done */ - break; - case 0: - /* wait for fd-event - * - * FIXME: wait for fdevent and call SSL_shutdown again - * - */ - - break; - default: - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - } - } -#endif - if ((0 == shutdown(con->fd, SHUT_WR))) { - con->close_timeout_ts = srv->cur_ts; - connection_set_state(srv, con, CON_STATE_CLOSE); - } else { - connection_close(srv, con); - } - - srv->con_closed++; - } - - connection_reset(srv, con); - + case CON_STATE_ERROR: /* transient */ + connection_handle_response_end_state(srv, con); break; case CON_STATE_CONNECT: - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - - /* we have to do the linger_on_close stuff regardless - * of con->keep_alive; even non-keepalive sockets may - * still have unread data, and closing before reading - * it will make the client not see all our output. - */ - { - int len; - char buf[1024]; - - len = read(con->fd, buf, sizeof(buf)); - if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { - con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); - } - } - - if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { - connection_close(srv, con); - - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "connection closed for fd", con->fd); - } - } - + connection_handle_close_state(srv, con); break; case CON_STATE_READ: - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "state for fd", con->fd, connection_get_state(con->state)); - } - do { /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { @@ -1335,108 +1352,6 @@ int connection_state_machine(server *srv, connection *con) { } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); break; - case CON_STATE_ERROR: /* transient */ - - /* even if the connection was drop we still have to write it to the access log */ - if (con->http_status) { - plugins_call_handle_request_done(srv, con); - } -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - int ret, ssl_r; - unsigned long err; - ERR_clear_error(); - switch ((ret = SSL_shutdown(con->ssl))) { - case 1: - /* ok */ - break; - case 0: - ERR_clear_error(); - if (-1 != (ret = SSL_shutdown(con->ssl))) break; - - /* fall through */ - default: - - switch ((ssl_r = SSL_get_error(con->ssl, ret))) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - break; - case SSL_ERROR_SYSCALL: - /* perhaps we have error waiting in our error-queue */ - if (0 != (err = ERR_get_error())) { - do { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", - ssl_r, ret, - ERR_error_string(err, NULL)); - } while((err = ERR_get_error())); - } else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */ - switch(errno) { - case EPIPE: - case ECONNRESET: - break; - default: - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", - ssl_r, ret, errno, - strerror(errno)); - break; - } - } - - break; - default: - while((err = ERR_get_error())) { - log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", - ssl_r, ret, - ERR_error_string(err, NULL)); - } - - break; - } - } - ERR_clear_error(); - } -#endif - - switch(con->mode) { - case DIRECT: -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", - "emergency exit: direct", - con->fd); -#endif - break; - default: - switch(r = plugins_call_handle_connection_close(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); - break; - } - break; - } - - connection_reset(srv, con); - - /* close the connection */ - if ((0 == shutdown(con->fd, SHUT_WR))) { - con->close_timeout_ts = srv->cur_ts; - connection_set_state(srv, con, CON_STATE_CLOSE); - - if (srv->srvconf.log_state_handling) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "shutdown for fd", con->fd); - } - } else { - connection_close(srv, con); - } - - con->keep_alive = 0; - - srv->con_closed++; - - break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); commit bce293e4a7b2a3d9d6518260c74dc3cb7233e342 Author: Glenn Strauss Date: Wed Jul 27 02:24:53 2016 -0400 [TLS] better handling of SSL_ERROR_WANT_READ/WRITE better handling of SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE diff --git a/src/connections-glue.c b/src/connections-glue.c index eee1907..2d60ec2 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -133,8 +133,9 @@ static int connection_handle_read_ssl(server *srv, connection *con) { if (len < 0) { int oerrno = errno; switch ((r = SSL_get_error(con->ssl, len))) { - case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: + con->is_writable = -1; + case SSL_ERROR_WANT_READ: con->is_readable = 0; /* the manual says we have to call SSL_read with the same arguments next time. diff --git a/src/connections.c b/src/connections.c index 790241a..ac4a682 100644 --- a/src/connections.c +++ b/src/connections.c @@ -207,6 +207,7 @@ static void connection_handle_shutdown(server *srv, connection *con) { case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_WRITE: + /*con->is_writable = -1;*//*(no effect; shutdown() called below)*/ case SSL_ERROR_WANT_READ: break; case SSL_ERROR_SYSCALL: @@ -1400,6 +1401,14 @@ int connection_state_machine(server *srv, connection *con) { } if (-1 != con->fd) { const int events = fdevent_event_get_interest(srv->ev, con->fd); + if (con->is_readable < 0) { + con->is_readable = 0; + r |= FDEVENT_IN; + } + if (con->is_writable < 0) { + con->is_writable = 0; + r |= FDEVENT_OUT; + } if (r != events) { /* update timestamps when enabling interest in events */ if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { diff --git a/src/network_openssl.c b/src/network_openssl.c index 4cf2cc4..9a9138e 100644 --- a/src/network_openssl.c +++ b/src/network_openssl.c @@ -125,7 +125,11 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu unsigned long err; switch ((ssl_r = SSL_get_error(ssl, r))) { + case SSL_ERROR_WANT_READ: + con->is_readable = -1; + return 0; /* try again later */ case SSL_ERROR_WANT_WRITE: + con->is_writable = -1; return 0; /* try again later */ case SSL_ERROR_SYSCALL: /* perhaps we have error waiting in our error-queue */ commit a95aaa9de984dc004dd1d4302147e8a0c23efb10 Author: Glenn Strauss Date: Wed Jul 27 05:54:25 2016 -0400 [TLS] read all available records from SSL_read() read all available records from SSL_read(), even if larger than MAX_READ_LIMIT, since the data is already in memory. openssl is configured with SSL_MODE_RELEASE_BUFFERS and will release openssl buffers once records have been read. Without reading available data, there was a chance that the connection would hang waiting for a read event on the fd, even though all the data had already been read from kernel socket buffers and was in openssl memory waiting to be read with SSL_read(). (thx glen and avij) diff --git a/src/connections-glue.c b/src/connections-glue.c index 2d60ec2..383c723 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -100,7 +100,7 @@ static void dump_packet(const unsigned char *data, size_t len) { static int connection_handle_read_ssl(server *srv, connection *con) { #ifdef USE_OPENSSL - int r, ssl_err, len, count = 0; + int r, ssl_err, len; char *mem = NULL; size_t mem_len = 0; @@ -115,20 +115,19 @@ static int connection_handle_read_ssl(server *srv, connection *con) { #endif len = SSL_read(con->ssl, mem, mem_len); - chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); + if (len > 0) { + chunkqueue_use_memory(con->read_queue, len); + con->bytes_read += len; + } else { + chunkqueue_use_memory(con->read_queue, 0); + } if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); connection_set_state(srv, con, CON_STATE_ERROR); return -1; } - - if (len > 0) { - con->bytes_read += len; - count += len; - } - } while (len == (ssize_t) mem_len && count < MAX_READ_LIMIT); - + } while (len > 0); if (len < 0) { int oerrno = errno; commit a69a803e35500c2ec0ba900420452cd4db750502 Author: Glenn Strauss Date: Wed Jul 27 15:37:46 2016 -0400 [core] try AF_INET after AF_INET6 if use-ipv6 try AF_INET after AF_INET6 if server.use-ipv6 = "enable" and getaddrinfo() fails EAI_ADDRFAMILY when hints.ai_family is AF_INET6. (Prefer IPv6 instead of setting hinst.ai_family to AF_UNSPEC since lighttpd only uses the first address returned) diff --git a/src/network.c b/src/network.c index f559929..61aeb39 100644 --- a/src/network.c +++ b/src/network.c @@ -281,6 +281,16 @@ static int network_server_init(server *srv, buffer *host_token, specific_config hints.ai_protocol = IPPROTO_TCP; if (0 != (r = getaddrinfo(host, NULL, &hints, &res))) { + hints.ai_family = AF_INET; + if (EAI_ADDRFAMILY == r && 0 == getaddrinfo(host, NULL, &hints, &res)) { + srv_socket->addr.ipv4.sin_family = AF_INET; + srv_socket->addr.ipv4.sin_port = htons(port); + memcpy(&(srv_socket->addr.ipv4.sin_addr.s_addr), res->ai_addr, res->ai_addrlen); + addr_len = sizeof(struct sockaddr_in); + freeaddrinfo(res); + break; + } + log_error_write(srv, __FILE__, __LINE__, "sssss", "getaddrinfo failed: ", gai_strerror(r), "'", host, "'"); commit a62bff986657f8143a34a8f11478c6e054f2a0ed Author: Glenn Strauss Date: Wed Jul 27 22:26:32 2016 -0400 [core] fix result copy from getaddrinfo() (thx avij) diff --git a/src/network.c b/src/network.c index 61aeb39..4693b91 100644 --- a/src/network.c +++ b/src/network.c @@ -283,10 +283,11 @@ static int network_server_init(server *srv, buffer *host_token, specific_config if (0 != (r = getaddrinfo(host, NULL, &hints, &res))) { hints.ai_family = AF_INET; if (EAI_ADDRFAMILY == r && 0 == getaddrinfo(host, NULL, &hints, &res)) { + memcpy(&srv_socket->addr.ipv4, res->ai_addr, res->ai_addrlen); srv_socket->addr.ipv4.sin_family = AF_INET; srv_socket->addr.ipv4.sin_port = htons(port); - memcpy(&(srv_socket->addr.ipv4.sin_addr.s_addr), res->ai_addr, res->ai_addrlen); addr_len = sizeof(struct sockaddr_in); + /*assert(addr_len == res->ai_addrlen);*/ freeaddrinfo(res); break; } commit c8e647ad3199de63d05489a89ef4e27aadc1e280 Author: Glenn Strauss Date: Thu Jul 28 03:57:52 2016 -0400 [core] set chunkqueue tempdirs at startup If server.upload-dirs is not configured, then attempt to use TMPDIR from the environment, if set, or else use /tmp. Warn at startup if tempdirs are not present. diff --git a/src/chunk.c b/src/chunk.c index 2bb57c3..a6b1f1f 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -22,6 +22,13 @@ #include #include +/* default 1MB, upper limit 128MB */ +#define DEFAULT_TEMPFILE_SIZE (1 * 1024 * 1024) +#define MAX_TEMPFILE_SIZE (128 * 1024 * 1024) + +static array *chunkqueue_default_tempdirs = NULL; +static unsigned int chunkqueue_default_tempfile_size = DEFAULT_TEMPFILE_SIZE; + chunkqueue *chunkqueue_init(void) { chunkqueue *cq; @@ -33,6 +40,9 @@ chunkqueue *chunkqueue_init(void) { cq->unused = NULL; + cq->tempdirs = chunkqueue_default_tempdirs; + cq->upload_temp_file_size = chunkqueue_default_tempfile_size; + return cq; } @@ -377,10 +387,15 @@ void chunkqueue_use_memory(chunkqueue *cq, size_t len) { } } -/* default 1MB, upper limit 128MB */ -#define DEFAULT_TEMPFILE_SIZE (1 * 1024 * 1024) -#define MAX_TEMPFILE_SIZE (128 * 1024 * 1024) +void chunkqueue_set_tempdirs_default (array *tempdirs, unsigned int upload_temp_file_size) { + chunkqueue_default_tempdirs = tempdirs; + chunkqueue_default_tempfile_size + = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE + : (upload_temp_file_size > MAX_TEMPFILE_SIZE) ? MAX_TEMPFILE_SIZE + : upload_temp_file_size; +} +#if 0 void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int upload_temp_file_size) { force_assert(NULL != cq); cq->tempdirs = tempdirs; @@ -390,6 +405,7 @@ void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int uploa : upload_temp_file_size; cq->tempdir_idx = 0; } +#endif void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len) { while (len > 0) { diff --git a/src/chunk.h b/src/chunk.h index 6acf2cf..651da56 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -50,7 +50,7 @@ typedef struct { } chunkqueue; chunkqueue *chunkqueue_init(void); -void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int upload_temp_file_size); +void chunkqueue_set_tempdirs_default (array *tempdirs, unsigned int upload_temp_file_size); void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len); /* copies "fn" */ void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len); /* copies "fn" */ void chunkqueue_append_mem(chunkqueue *cq, const char *mem, size_t len); /* copies memory */ diff --git a/src/configfile.c b/src/configfile.c index fbcbcf3..36c94ab 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1363,6 +1363,14 @@ int config_set_defaults(server *srv) { } } + if (!srv->srvconf.upload_tempdirs->used) { + data_string *ds = data_string_init(); + const char *tmpdir = getenv("TMPDIR"); + if (NULL == tmpdir) tmpdir = "/tmp"; + buffer_copy_string(ds->value, tmpdir); + array_insert_unique(srv->srvconf.upload_tempdirs, (data_unset *)ds); + } + if (srv->srvconf.upload_tempdirs->used) { buffer * const b = srv->tmp_buf; size_t len; @@ -1388,6 +1396,10 @@ int config_set_defaults(server *srv) { } } + chunkqueue_set_tempdirs_default( + srv->srvconf.upload_tempdirs, + srv->srvconf.upload_temp_file_size); + if (buffer_string_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); diff --git a/src/connections.c b/src/connections.c index ac4a682..ccdf360 100644 --- a/src/connections.c +++ b/src/connections.c @@ -612,10 +612,6 @@ connection *connection_init(server *srv) { con->write_queue = chunkqueue_init(); con->read_queue = chunkqueue_init(); con->request_content_queue = chunkqueue_init(); - chunkqueue_set_tempdirs( - con->request_content_queue, - srv->srvconf.upload_tempdirs, - srv->srvconf.upload_temp_file_size); con->request.headers = array_init(); con->response.headers = array_init(); commit ad6d41896efec57577850206737acb079cf026d0 Author: Glenn Strauss Date: Fri Jul 29 12:48:00 2016 -0400 [core] check if EAI_ADDRFAMILY is defined (EAI_ADDRFAMILY is not available on FreeBSD) diff --git a/src/network.c b/src/network.c index 4693b91..dfa80a4 100644 --- a/src/network.c +++ b/src/network.c @@ -282,7 +282,11 @@ static int network_server_init(server *srv, buffer *host_token, specific_config if (0 != (r = getaddrinfo(host, NULL, &hints, &res))) { hints.ai_family = AF_INET; - if (EAI_ADDRFAMILY == r && 0 == getaddrinfo(host, NULL, &hints, &res)) { + if ( + #ifdef EAI_ADDRFAMILY + EAI_ADDRFAMILY == r && + #endif + 0 == getaddrinfo(host, NULL, &hints, &res)) { memcpy(&srv_socket->addr.ipv4, res->ai_addr, res->ai_addrlen); srv_socket->addr.ipv4.sin_family = AF_INET; srv_socket->addr.ipv4.sin_port = htons(port); commit f7410da5d2228cbf2d89524a6173f9fd2d0ff691 Author: Glenn Strauss Date: Fri Jul 29 15:01:46 2016 -0400 [core] set chunkqueue tempdirs at startup /var/tmp If server.upload-dirs is not configured, then attempt to use TMPDIR from the environment, if set, or else use /var/tmp which is not often a tmpfs, unlike /tmp. Warn at startup if tempdirs are not present. diff --git a/src/configfile.c b/src/configfile.c index 36c94ab..e21a995 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1366,7 +1366,7 @@ int config_set_defaults(server *srv) { if (!srv->srvconf.upload_tempdirs->used) { data_string *ds = data_string_init(); const char *tmpdir = getenv("TMPDIR"); - if (NULL == tmpdir) tmpdir = "/tmp"; + if (NULL == tmpdir) tmpdir = "/var/tmp"; buffer_copy_string(ds->value, tmpdir); array_insert_unique(srv->srvconf.upload_tempdirs, (data_unset *)ds); }