diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 14 | ||||
| -rw-r--r-- | src/Makefile.in | 14 | ||||
| -rw-r--r-- | src/SConscript | 195 | ||||
| -rw-r--r-- | src/array.h | 8 | ||||
| -rw-r--r-- | src/base.h | 1 | ||||
| -rw-r--r-- | src/configfile-glue.c | 20 | ||||
| -rw-r--r-- | src/configparser.c | 191 | ||||
| -rw-r--r-- | src/configparser.y | 50 | ||||
| -rw-r--r-- | src/connections.c | 29 | ||||
| -rw-r--r-- | src/etag.c | 9 | ||||
| -rw-r--r-- | src/fdevent_solaris_devpoll.c | 2 | ||||
| -rw-r--r-- | src/http_auth.c | 5 | ||||
| -rw-r--r-- | src/mod_auth.c | 4 | ||||
| -rw-r--r-- | src/mod_cgi.c | 9 | ||||
| -rw-r--r-- | src/mod_compress.c | 118 | ||||
| -rw-r--r-- | src/mod_extforward.c | 100 | ||||
| -rw-r--r-- | src/mod_fastcgi.c | 76 | ||||
| -rw-r--r-- | src/mod_proxy.c | 16 | ||||
| -rw-r--r-- | src/mod_scgi.c | 8 | ||||
| -rw-r--r-- | src/mod_secure_download.c | 3 | ||||
| -rw-r--r-- | src/mod_ssi.c | 108 | ||||
| -rw-r--r-- | src/mod_staticfile.c | 24 | ||||
| -rw-r--r-- | src/mod_status.c | 18 | ||||
| -rw-r--r-- | src/mod_userdir.c | 16 | ||||
| -rw-r--r-- | src/network_linux_sendfile.c | 5 | ||||
| -rw-r--r-- | src/network_openssl.c | 27 | ||||
| -rw-r--r-- | src/response.c | 46 | ||||
| -rw-r--r-- | src/server.c | 43 | ||||
| -rw-r--r-- | src/spawn-fcgi.c | 258 | ||||
| -rw-r--r-- | src/stream.c | 3 |
30 files changed, 952 insertions, 468 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 24967c8..a112789 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,20 +16,20 @@ if CROSS_COMPILING configparser.c configparser.h: mod_ssi_exprparser.c mod_ssi_exprparser.h: else -configparser.y: lemon -mod_ssi_exprparser.y: lemon +$(srcdir)/configparser.y: lemon +$(srcdir)/mod_ssi_exprparser.y: lemon -configparser.c configparser.h: configparser.y +configparser.c configparser.h: $(srcdir)/configparser.y rm -f configparser.h $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c -mod_ssi_exprparser.c mod_ssi_exprparser.h: mod_ssi_exprparser.y +mod_ssi_exprparser.c mod_ssi_exprparser.h: $(srcdir)/mod_ssi_exprparser.y rm -f mod_ssi_exprparser.h $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c endif -configfile.c: configparser.h -mod_ssi_expr.c: mod_ssi_exprparser.h +$(srcdir)/configfile.c: configparser.h +$(srcdir)/mod_ssi_expr.c: mod_ssi_exprparser.h common_src=buffer.c log.c \ keyvalue.c chunk.c \ @@ -281,4 +281,4 @@ proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN #ajp_SOURCES = ajp.c noinst_HEADERS = $(hdr) -EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c +EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c SConscript diff --git a/src/Makefile.in b/src/Makefile.in index 2952518..826af20 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -755,7 +755,7 @@ proc_open_CPPFLAGS = -DDEBUG_PROC_OPEN #bench_SOURCES = buffer.c bench.c #ajp_SOURCES = ajp.c noinst_HEADERS = $(hdr) -EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c +EXTRA_DIST = mod_skeleton.c configparser.y mod_ssi_exprparser.y lempar.c SConscript all: all-am .SUFFIXES: @@ -1669,19 +1669,19 @@ uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ @CROSS_COMPILING_TRUE@configparser.c configparser.h: @CROSS_COMPILING_TRUE@mod_ssi_exprparser.c mod_ssi_exprparser.h: -@CROSS_COMPILING_FALSE@configparser.y: lemon -@CROSS_COMPILING_FALSE@mod_ssi_exprparser.y: lemon +@CROSS_COMPILING_FALSE@$(srcdir)/configparser.y: lemon +@CROSS_COMPILING_FALSE@$(srcdir)/mod_ssi_exprparser.y: lemon -@CROSS_COMPILING_FALSE@configparser.c configparser.h: configparser.y +@CROSS_COMPILING_FALSE@configparser.c configparser.h: $(srcdir)/configparser.y @CROSS_COMPILING_FALSE@ rm -f configparser.h @CROSS_COMPILING_FALSE@ $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c -@CROSS_COMPILING_FALSE@mod_ssi_exprparser.c mod_ssi_exprparser.h: mod_ssi_exprparser.y +@CROSS_COMPILING_FALSE@mod_ssi_exprparser.c mod_ssi_exprparser.h: $(srcdir)/mod_ssi_exprparser.y @CROSS_COMPILING_FALSE@ rm -f mod_ssi_exprparser.h @CROSS_COMPILING_FALSE@ $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c -configfile.c: configparser.h -mod_ssi_expr.c: mod_ssi_exprparser.h +$(srcdir)/configfile.c: configparser.h +$(srcdir)/mod_ssi_expr.c: mod_ssi_exprparser.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 0000000..0ad2a4e --- /dev/null +++ b/src/SConscript @@ -0,0 +1,195 @@ +import os +import re +import types + +Import('env') + +common_src = Split("buffer.c log.c \ + keyvalue.c chunk.c \ + http_chunk.c stream.c fdevent.c \ + stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_count.c data_array.c \ + data_integer.c md5.c data_fastcgi.c \ + fdevent_select.c fdevent_linux_rtsig.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ + data_config.c bitset.c \ + inet_ntop_cache.c crc32.c \ + connections-glue.c \ + configfile-glue.c \ + http-header-glue.c \ + splaytree.c network_writev.c \ + network_write.c network_linux_sendfile.c \ + network_freebsd_sendfile.c \ + network_solaris_sendfilev.c network_openssl.c \ + status_counter.c \ +") + +src = Split("server.c response.c connections.c network.c \ + configfile.c configparser.c request.c proc_open.c") + +lemon = env.Program('lemon', 'lemon.c') + +configparser = env.Command(['configparser.c', 'configparser.h'], 'configparser.y', '(cd build; ../' + lemon[0].path + ' -q ../$SOURCE ../src/lempar.c; cd ..)') +env.Depends(configparser, lemon) + +mod_ssi_exprparser = env.Command(['mod_ssi_exprparser.c', 'mod_ssi_exprparser.h'], 'mod_ssi_exprparser.y', '(cd build; ../' + lemon[0].path + ' -q ../$SOURCE ../src/lempar.c; cd ..)') +env.Depends(mod_ssi_exprparser, lemon) + +## the modules and how they are built +modules = { + 'mod_access' : { 'src' : [ 'mod_access.c' ] }, + 'mod_alias' : { 'src' : [ 'mod_alias.c' ] }, + 'mod_cgi' : { 'src' : [ 'mod_cgi.c' ] }, + 'mod_fastcgi' : { 'src' : [ 'mod_fastcgi.c' ] }, + 'mod_scgi' : { 'src' : [ 'mod_scgi.c' ] }, + 'mod_staticfile' : { 'src' : [ 'mod_staticfile.c' ] }, + 'mod_dirlisting' : { 'src' : [ 'mod_dirlisting.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_indexfile' : { 'src' : [ 'mod_indexfile.c' ] }, + 'mod_setenv' : { 'src' : [ 'mod_setenv.c' ] }, + 'mod_rrdtool' : { 'src' : [ 'mod_rrdtool.c' ] }, + 'mod_usertrack' : { 'src' : [ 'mod_usertrack.c' ] }, + 'mod_proxy' : { 'src' : [ 'mod_proxy.c' ] }, + 'mod_userdir' : { 'src' : [ 'mod_userdir.c' ] }, + 'mod_secdownload' : { 'src' : [ 'mod_secure_download.c' ] }, + 'mod_accesslog' : { 'src' : [ 'mod_accesslog.c' ] }, + 'mod_simple_vhost' : { 'src' : [ 'mod_simple_vhost.c' ] }, + 'mod_evhost' : { 'src' : [ 'mod_evhost.c' ] }, + 'mod_expire' : { 'src' : [ 'mod_expire.c' ] }, + 'mod_status' : { 'src' : [ 'mod_status.c' ] }, + 'mod_compress' : { 'src' : [ 'mod_compress.c' ], 'lib' : [ env['LIBZ'], env['LIBBZ2'] ] }, + 'mod_redirect' : { 'src' : [ 'mod_redirect.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_rewrite' : { 'src' : [ 'mod_rewrite.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_auth' : { + 'src' : [ 'mod_auth.c', 'http_auth_digest.c', 'http_auth.c' ], + 'lib' : [ env['LIBCRYPT'], env['LIBLDAP'], env['LIBLBER'] ] }, + 'mod_webdav' : { 'src' : [ 'mod_webdav.c' ], 'lib' : [ env['LIBXML2'], env['LIBSQLITE3'], env['LIBUUID'] ] }, + 'mod_mysql_vhost' : { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] }, + 'mod_trigger_b4_dl' : { 'src' : [ 'mod_trigger_b4_dl.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_cml' : { + 'src' : [ 'mod_cml_lua.c', 'mod_cml.c', 'mod_cml_funcs.c' ], + 'lib' : [ env['LIBPCRE'], env['LIBMEMCACHE'], env['LIBLUA'], env['LIBLUALIB'] ] }, + 'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] }, + 'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] }, + 'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] }, + 'mod_magnet' : { 'src' : [ 'mod_magnet.c', 'mod_magnet_cache.c' ], 'lib' : [ env['LIBLUA'] ] }, +} + +staticenv = env.Copy(CPPFLAGS=[ env['CPPFLAGS'], '-DLIGHTTPD_STATIC', '-DOPENSSL_NO_KRB5']) + +## all the core-sources + the modules +staticsrc = src + common_src + +staticlib = env['LIBS'] +staticinit = '' +for module in modules.keys(): + staticsrc += modules[module]['src'] + staticinit += "PLUGIN_INIT(%s)\n"%module + if modules[module].has_key('lib'): + staticlib += modules[module]['lib'] + +open('plugin-static.h', 'w+').write(staticinit) + +## turn all src-files into objects +staticobj = [] +for cfile in staticsrc: + staticobj += [ staticenv.Object('static-' + cfile.replace('.c', ''), cfile) ] + +staticbin = staticenv.Program('lighttpd-semi-static', + staticobj, + LIBS = staticlib + ) + +## you might have to adjust the list of libs and the order for your setup +## this is tricky, be warned +fullstaticlib = [] + +## try to calculate the libs for fullstatic with ldd +## 1. find the lib +## 2. check the deps +## 3. add them to the libs +searchlibs = os.pathsep.join([ '/lib/', '/usr/lib/', '/usr/local/lib/' ]) +lddre = re.compile(r'^\s+lib([^=-]+)(?:-[\.0-9]+)?\.so\.[0-9]+ =>', re.MULTILINE) +for libs in staticlib: + if type(libs) is types.StringType: libs = [ libs ] + for lib in libs: + fullstaticlib += [ lib ] + solibpath = env.WhereIs('lib' + lib + '.so', searchlibs) + fullstaticlib += [ lib ] + if solibpath is None: + continue + + f = os.popen('ldd ' + solibpath, 'r') + for aword in lddre.findall(f.read()): + fullstaticlib += [ aword ] + f.close + + +fullstaticbin = staticenv.Program('lighttpd-static', + staticobj, + LIBS = fullstaticlib, + LINKFLAGS= ['-static'] + ) + +Alias('static', staticbin) +Alias('fullstatic', fullstaticbin) + +implib = 'lighttpd.exe.a' +bin_targets = ['lighttpd'] +bin_linkflags = [ env['LINKFLAGS'] ] +if env['COMMON_LIB'] == 'lib': + common_lib = env.SharedLibrary('liblighttpd', common_src, LINKFLAGS = [ env['LINKFLAGS'], '-Wl,--export-dynamic' ]) +else: + src += common_src + common_lib = [] + if env['COMMON_LIB'] == 'bin': + bin_linkflags += [ '-Wl,--export-all-symbols', '-Wl,--out-implib=build/' + implib ] + bin_targets += [ implib ] + else: + bin_linkflags += [ '-Wl,--export-dynamic' ] + +instbin = env.Program(bin_targets, src, LINKFLAGS = bin_linkflags, LIBS= [ env['LIBS'], common_lib, env['LIBDL'] ]) +env.Depends(instbin, configparser) + +spawn_fcgi = env.Program("spawn-fcgi", "spawn-fcgi.c") + +if env['COMMON_LIB'] == 'bin': + common_lib = instbin[1] + +env['SHLIBPREFIX'] = '' +instlib = [] +for module in modules.keys(): + libs = [ common_lib ] + if modules[module].has_key('lib'): + libs += modules[module]['lib'] + instlib += env.SharedLibrary(module, modules[module]['src'], LIBS= [ libs ]) + +inst = [] + +Default(spawn_fcgi) +inst += env.Install('${bindir}', spawn_fcgi) + +if env['build_dynamic']: + Default(instbin[0], instlib) + inst += env.Install('${sbindir}', instbin[0]) + inst += env.Install('${libdir}', instlib) + if env['COMMON_LIB'] == 'lib': + Default(common_lib) + inst += env.Install('${bindir}', common_lib) + +if env['build_static']: + Default(staticbin) + inst += env.Install('${sbindir}', staticbin) + +if env['build_fullstatic']: + Default(fullstaticbin) + inst += env.Install('${sbindir}', fullstaticbin) + +env.Alias('dynamic', instbin) +# default all to be installed +env.Alias('install', inst) + +pkgdir = '.' +tarname = env['package'] + '-' + env['version'] + diff --git a/src/array.h b/src/array.h index b8a8345..9dacf10 100644 --- a/src/array.h +++ b/src/array.h @@ -86,10 +86,12 @@ typedef enum { COMP_HTTP_URL, COMP_HTTP_HOST, COMP_HTTP_REFERER, - COMP_HTTP_USERAGENT, + COMP_HTTP_USER_AGENT, COMP_HTTP_COOKIE, - COMP_HTTP_REMOTEIP, - COMP_HTTP_QUERYSTRING, + COMP_HTTP_REMOTE_IP, + COMP_HTTP_QUERY_STRING, + COMP_HTTP_SCHEME, + COMP_HTTP_REQUEST_METHOD, COMP_LAST_ELEMENT } comp_key_t; @@ -515,6 +515,7 @@ typedef struct { #ifdef USE_OPENSSL SSL_CTX *ssl_ctx; #endif + unsigned short is_proxy_ssl; } server_socket; typedef struct { diff --git a/src/configfile-glue.c b/src/configfile-glue.c index f6ead92..66a596e 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -277,7 +277,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } - case COMP_HTTP_REMOTEIP: { + case COMP_HTTP_REMOTE_IP: { char *nm_slash; /* handle remoteip limitations * @@ -341,11 +341,15 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } + case COMP_HTTP_SCHEME: + l = con->uri.scheme; + break; + case COMP_HTTP_URL: l = con->uri.path; break; - case COMP_HTTP_QUERYSTRING: + case COMP_HTTP_QUERY_STRING: l = con->uri.query; break; @@ -372,7 +376,7 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } - case COMP_HTTP_USERAGENT: { + case COMP_HTTP_USER_AGENT: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) { l = ds->value; @@ -381,7 +385,17 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat } break; } + case COMP_HTTP_REQUEST_METHOD: { + const char *method = get_http_method_name(con->request.http_method); + + /* we only have the request method as const char but we need a buffer for comparing */ + + buffer_copy_string(srv->tmp_buf, method); + l = srv->tmp_buf; + + break; + } default: return COND_RESULT_FALSE; } diff --git a/src/configparser.c b/src/configparser.c index 363b395..58a03fe 100644 --- a/src/configparser.c +++ b/src/configparser.c @@ -54,7 +54,7 @@ static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { return NULL; } -/* op1 is to be eat/return by this function, op1->key is not cared +/* op1 is to be eat/return by this function if success, op1->key is not cared op2 is left untouch, unreferenced */ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { @@ -72,7 +72,6 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { return (data_unset *)ds; } else { fprintf(stderr, "data type mismatch, cannot be merge\n"); - op1->free(op1); return NULL; } } @@ -106,7 +105,7 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { } -#line 110 "configparser.c" +#line 109 "configparser.c" /* Next is all token values, in a form suitable for use by makeheaders. ** This section will be null unless lemon is run with the -m switch. */ @@ -515,42 +514,42 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ case 25: #line 142 "./configparser.y" { buffer_free((yypminor->yy0)); } -#line 518 "configparser.c" +#line 517 "configparser.c" break; case 35: #line 133 "./configparser.y" { (yypminor->yy41)->free((yypminor->yy41)); } -#line 523 "configparser.c" +#line 522 "configparser.c" break; case 36: #line 134 "./configparser.y" { (yypminor->yy41)->free((yypminor->yy41)); } -#line 528 "configparser.c" +#line 527 "configparser.c" break; case 37: #line 135 "./configparser.y" { (yypminor->yy41)->free((yypminor->yy41)); } -#line 533 "configparser.c" +#line 532 "configparser.c" break; case 39: #line 136 "./configparser.y" { array_free((yypminor->yy40)); } -#line 538 "configparser.c" +#line 537 "configparser.c" break; case 40: #line 137 "./configparser.y" { array_free((yypminor->yy40)); } -#line 543 "configparser.c" +#line 542 "configparser.c" break; case 41: #line 138 "./configparser.y" { buffer_free((yypminor->yy43)); } -#line 548 "configparser.c" +#line 547 "configparser.c" break; case 42: #line 139 "./configparser.y" { buffer_free((yypminor->yy43)); } -#line 553 "configparser.c" +#line 552 "configparser.c" break; default: break; /* If no destructor action specified: do nothing */ } @@ -818,9 +817,9 @@ static void yy_reduce( /* No destructor defined for global */ break; case 5: -#line 116 "./configparser.y" +#line 115 "./configparser.y" { yymsp[-1].minor.yy78 = NULL; } -#line 823 "configparser.c" +#line 822 "configparser.c" yy_destructor(1,&yymsp[0].minor); break; case 6: @@ -835,31 +834,33 @@ static void yy_reduce( case 9: #line 144 "./configparser.y" { - buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); - if (strncmp(yymsp[-2].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { - fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, yymsp[-2].minor.yy43->ptr); - ctx->ok = 0; - } else if (NULL == array_get_element(ctx->current->value, yymsp[0].minor.yy41->key->ptr)) { - array_insert_unique(ctx->current->value, yymsp[0].minor.yy41); - yymsp[0].minor.yy41 = NULL; - } else { - fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, yymsp[0].minor.yy41->key->ptr); - ctx->ok = 0; - yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); - yymsp[0].minor.yy41 = NULL; + if (ctx->ok) { + buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); + if (strncmp(yymsp[-2].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy43->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element(ctx->current->value, yymsp[0].minor.yy41->key->ptr)) { + array_insert_unique(ctx->current->value, yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[0].minor.yy41->key->ptr); + ctx->ok = 0; + yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); + yymsp[0].minor.yy41 = NULL; + } } buffer_free(yymsp[-2].minor.yy43); yymsp[-2].minor.yy43 = NULL; } -#line 858 "configparser.c" +#line 859 "configparser.c" yy_destructor(2,&yymsp[-1].minor); break; case 10: -#line 166 "./configparser.y" +#line 168 "./configparser.y" { array *vars = ctx->current->value; data_unset *du; @@ -884,6 +885,7 @@ static void yy_reduce( du = configparser_merge_data(du, yymsp[0].minor.yy41); if (NULL == du) { ctx->ok = 0; + du->free(du); } else { buffer_copy_string_buffer(du->key, yymsp[-2].minor.yy43); @@ -898,11 +900,11 @@ static void yy_reduce( yymsp[-2].minor.yy43 = NULL; yymsp[0].minor.yy41 = NULL; } -#line 901 "configparser.c" +#line 903 "configparser.c" yy_destructor(3,&yymsp[-1].minor); break; case 11: -#line 205 "./configparser.y" +#line 208 "./configparser.y" { if (strchr(yymsp[0].minor.yy0->ptr, '.') == NULL) { yygotominor.yy43 = buffer_init_string("var."); @@ -914,10 +916,10 @@ static void yy_reduce( yymsp[0].minor.yy0 = NULL; } } -#line 917 "configparser.c" +#line 919 "configparser.c" break; case 12: -#line 217 "./configparser.y" +#line 220 "./configparser.y" { yygotominor.yy41 = configparser_merge_data(yymsp[-2].minor.yy41, yymsp[0].minor.yy41); if (NULL == yygotominor.yy41) { @@ -927,19 +929,19 @@ static void yy_reduce( yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); yymsp[0].minor.yy41 = NULL; } -#line 930 "configparser.c" +#line 932 "configparser.c" yy_destructor(5,&yymsp[-1].minor); break; case 13: -#line 227 "./configparser.y" +#line 230 "./configparser.y" { yygotominor.yy41 = yymsp[0].minor.yy41; yymsp[0].minor.yy41 = NULL; } -#line 939 "configparser.c" +#line 941 "configparser.c" break; case 14: -#line 232 "./configparser.y" +#line 235 "./configparser.y" { yygotominor.yy41 = NULL; if (strncmp(yymsp[0].minor.yy43->ptr, "env.", sizeof("env.") - 1) == 0) { @@ -966,59 +968,59 @@ static void yy_reduce( buffer_free(yymsp[0].minor.yy43); yymsp[0].minor.yy43 = NULL; } -#line 969 "configparser.c" +#line 971 "configparser.c" break; case 15: -#line 259 "./configparser.y" +#line 262 "./configparser.y" { yygotominor.yy41 = (data_unset *)data_string_init(); buffer_copy_string_buffer(((data_string *)(yygotominor.yy41))->value, yymsp[0].minor.yy0); buffer_free(yymsp[0].minor.yy0); yymsp[0].minor.yy0 = NULL; } -#line 979 "configparser.c" +#line 981 "configparser.c" break; case 16: -#line 266 "./configparser.y" +#line 269 "./configparser.y" { yygotominor.yy41 = (data_unset *)data_integer_init(); ((data_integer *)(yygotominor.yy41))->value = strtol(yymsp[0].minor.yy0->ptr, NULL, 10); buffer_free(yymsp[0].minor.yy0); yymsp[0].minor.yy0 = NULL; } -#line 989 "configparser.c" +#line 991 "configparser.c" break; case 17: -#line 272 "./configparser.y" +#line 275 "./configparser.y" { yygotominor.yy41 = (data_unset *)data_array_init(); array_free(((data_array *)(yygotominor.yy41))->value); ((data_array *)(yygotominor.yy41))->value = yymsp[0].minor.yy40; yymsp[0].minor.yy40 = NULL; } -#line 999 "configparser.c" +#line 1001 "configparser.c" break; case 18: -#line 278 "./configparser.y" +#line 281 "./configparser.y" { yygotominor.yy40 = array_init(); } -#line 1006 "configparser.c" +#line 1008 "configparser.c" yy_destructor(8,&yymsp[-1].minor); yy_destructor(9,&yymsp[0].minor); break; case 19: -#line 281 "./configparser.y" +#line 284 "./configparser.y" { yygotominor.yy40 = yymsp[-1].minor.yy40; yymsp[-1].minor.yy40 = NULL; } -#line 1016 "configparser.c" +#line 1018 "configparser.c" yy_destructor(8,&yymsp[-2].minor); yy_destructor(9,&yymsp[0].minor); break; case 20: -#line 286 "./configparser.y" +#line 289 "./configparser.y" { if (buffer_is_empty(yymsp[0].minor.yy41->key) || NULL == array_get_element(yymsp[-2].minor.yy40, yymsp[0].minor.yy41->key->ptr)) { @@ -1035,37 +1037,37 @@ static void yy_reduce( yygotominor.yy40 = yymsp[-2].minor.yy40; yymsp[-2].minor.yy40 = NULL; } -#line 1038 "configparser.c" +#line 1040 "configparser.c" yy_destructor(10,&yymsp[-1].minor); break; case 21: -#line 303 "./configparser.y" +#line 306 "./configparser.y" { yygotominor.yy40 = yymsp[-1].minor.yy40; yymsp[-1].minor.yy40 = NULL; } -#line 1047 "configparser.c" +#line 1049 "configparser.c" yy_destructor(10,&yymsp[0].minor); break; case 22: -#line 308 "./configparser.y" +#line 311 "./configparser.y" { yygotominor.yy40 = array_init(); array_insert_unique(yygotominor.yy40, yymsp[0].minor.yy41); yymsp[0].minor.yy41 = NULL; } -#line 1057 "configparser.c" +#line 1059 "configparser.c" break; case 23: -#line 314 "./configparser.y" +#line 317 "./configparser.y" { yygotominor.yy41 = yymsp[0].minor.yy41; yymsp[0].minor.yy41 = NULL; } -#line 1065 "configparser.c" +#line 1067 "configparser.c" break; case 24: -#line 318 "./configparser.y" +#line 321 "./configparser.y" { buffer_copy_string_buffer(yymsp[0].minor.yy41->key, yymsp[-2].minor.yy43); buffer_free(yymsp[-2].minor.yy43); @@ -1074,7 +1076,7 @@ static void yy_reduce( yygotominor.yy41 = yymsp[0].minor.yy41; yymsp[0].minor.yy41 = NULL; } -#line 1077 "configparser.c" +#line 1079 "configparser.c" yy_destructor(11,&yymsp[-1].minor); break; case 25: @@ -1083,18 +1085,18 @@ static void yy_reduce( case 26: break; case 27: -#line 330 "./configparser.y" +#line 333 "./configparser.y" { data_config *dc; dc = (data_config *)array_get_element(ctx->srv->config_context, "global"); assert(dc); configparser_push(ctx, dc, 0); } -#line 1093 "configparser.c" +#line 1095 "configparser.c" yy_destructor(12,&yymsp[0].minor); break; case 28: -#line 337 "./configparser.y" +#line 340 "./configparser.y" { data_config *cur; @@ -1103,16 +1105,16 @@ static void yy_reduce( assert(cur && ctx->current); - yygotominor.yy0 = cur; + yygotominor.yy78 = cur; } -#line 1108 "configparser.c" +#line 1110 "configparser.c" /* No destructor defined for globalstart */ yy_destructor(13,&yymsp[-2].minor); /* No destructor defined for metalines */ yy_destructor(14,&yymsp[0].minor); break; case 29: -#line 348 "./configparser.y" +#line 351 "./configparser.y" { assert(yymsp[-3].minor.yy78->context_ndx < yymsp[0].minor.yy78->context_ndx); yymsp[0].minor.yy78->prev = yymsp[-3].minor.yy78; @@ -1121,20 +1123,20 @@ static void yy_reduce( yymsp[-3].minor.yy78 = NULL; yymsp[0].minor.yy78 = NULL; } -#line 1124 "configparser.c" +#line 1126 "configparser.c" /* No destructor defined for eols */ yy_destructor(15,&yymsp[-1].minor); break; case 30: -#line 357 "./configparser.y" +#line 360 "./configparser.y" { yygotominor.yy78 = yymsp[0].minor.yy78; yymsp[0].minor.yy78 = NULL; } -#line 1134 "configparser.c" +#line 1136 "configparser.c" break; case 31: -#line 362 "./configparser.y" +#line 365 "./configparser.y" { data_config *cur; @@ -1145,14 +1147,14 @@ static void yy_reduce( yygotominor.yy78 = cur; } -#line 1148 "configparser.c" +#line 1150 "configparser.c" /* No destructor defined for context */ yy_destructor(13,&yymsp[-2].minor); /* No destructor defined for metalines */ yy_destructor(14,&yymsp[0].minor); break; case 32: -#line 373 "./configparser.y" +#line 376 "./configparser.y" { data_config *dc; buffer *b, *rvalue, *op; @@ -1201,10 +1203,15 @@ static void yy_reduce( { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) }, - { COMP_HTTP_USERAGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) }, - { COMP_HTTP_REMOTEIP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, - { COMP_HTTP_QUERYSTRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, { COMP_UNSET, NULL, 0 }, }; size_t i; @@ -1288,45 +1295,45 @@ static void yy_reduce( yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); yymsp[0].minor.yy41 = NULL; } -#line 1291 "configparser.c" +#line 1298 "configparser.c" yy_destructor(16,&yymsp[-6].minor); yy_destructor(18,&yymsp[-4].minor); yy_destructor(19,&yymsp[-2].minor); break; case 33: -#line 508 "./configparser.y" +#line 516 "./configparser.y" { yygotominor.yy27 = CONFIG_COND_EQ; } -#line 1301 "configparser.c" +#line 1308 "configparser.c" yy_destructor(20,&yymsp[0].minor); break; case 34: -#line 511 "./configparser.y" +#line 519 "./configparser.y" { yygotominor.yy27 = CONFIG_COND_MATCH; } -#line 1309 "configparser.c" +#line 1316 "configparser.c" yy_destructor(21,&yymsp[0].minor); break; case 35: -#line 514 "./configparser.y" +#line 522 "./configparser.y" { yygotominor.yy27 = CONFIG_COND_NE; } -#line 1317 "configparser.c" +#line 1324 "configparser.c" yy_destructor(22,&yymsp[0].minor); break; case 36: -#line 517 "./configparser.y" +#line 525 "./configparser.y" { yygotominor.yy27 = CONFIG_COND_NOMATCH; } -#line 1325 "configparser.c" +#line 1332 "configparser.c" yy_destructor(23,&yymsp[0].minor); break; case 37: -#line 521 "./configparser.y" +#line 529 "./configparser.y" { yygotominor.yy43 = NULL; if (ctx->ok) { @@ -1343,10 +1350,10 @@ static void yy_reduce( yymsp[0].minor.yy41->free(yymsp[0].minor.yy41); yymsp[0].minor.yy41 = NULL; } -#line 1346 "configparser.c" +#line 1353 "configparser.c" break; case 38: -#line 538 "./configparser.y" +#line 546 "./configparser.y" { if (ctx->ok) { if (0 != config_parse_file(ctx->srv, ctx, yymsp[0].minor.yy43->ptr)) { @@ -1356,11 +1363,11 @@ static void yy_reduce( yymsp[0].minor.yy43 = NULL; } } -#line 1359 "configparser.c" +#line 1366 "configparser.c" yy_destructor(24,&yymsp[-1].minor); break; case 39: -#line 548 "./configparser.y" +#line 556 "./configparser.y" { if (ctx->ok) { if (0 != config_parse_cmd(ctx->srv, ctx, yymsp[0].minor.yy43->ptr)) { @@ -1370,7 +1377,7 @@ static void yy_reduce( yymsp[0].minor.yy43 = NULL; } } -#line 1373 "configparser.c" +#line 1380 "configparser.c" yy_destructor(25,&yymsp[-1].minor); break; }; @@ -1400,11 +1407,11 @@ static void yy_parse_failed( while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ -#line 107 "./configparser.y" +#line 106 "./configparser.y" ctx->ok = 0; -#line 1407 "configparser.c" +#line 1414 "configparser.c" configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ } diff --git a/src/configparser.y b/src/configparser.y index 30f5fd9..57e2dc5 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -51,7 +51,7 @@ static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { return NULL; } -/* op1 is to be eat/return by this function, op1->key is not cared +/* op1 is to be eat/return by this function if success, op1->key is not cared op2 is left untouch, unreferenced */ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { @@ -69,7 +69,6 @@ data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { return (data_unset *)ds; } else { fprintf(stderr, "data type mismatch, cannot be merge\n"); - op1->free(op1); return NULL; } } @@ -123,6 +122,7 @@ metaline ::= EOL. %type aelement {data_unset *} %type condline {data_config *} %type condlines {data_config *} +%type global {data_config *} %type aelements {array *} %type array {array *} %type key {buffer *} @@ -142,22 +142,24 @@ metaline ::= EOL. %token_destructor { buffer_free($$); } varline ::= key(A) ASSIGN expression(B). { - buffer_copy_string_buffer(B->key, A); - if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { - fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, A->ptr); - ctx->ok = 0; - } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) { - array_insert_unique(ctx->current->value, B); - B = NULL; - } else { - fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", - ctx->current->context_ndx, - ctx->current->key->ptr, B->key->ptr); - ctx->ok = 0; - B->free(B); - B = NULL; + if (ctx->ok) { + buffer_copy_string_buffer(B->key, A); + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) { + array_insert_unique(ctx->current->value, B); + B = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, B->key->ptr); + ctx->ok = 0; + B->free(B); + B = NULL; + } } buffer_free(A); A = NULL; @@ -187,6 +189,7 @@ varline ::= key(A) APPEND expression(B). { du = configparser_merge_data(du, B); if (NULL == du) { ctx->ok = 0; + du->free(du); } else { buffer_copy_string_buffer(du->key, A); @@ -418,10 +421,15 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) }, - { COMP_HTTP_USERAGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) }, - { COMP_HTTP_REMOTEIP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, - { COMP_HTTP_QUERYSTRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, { COMP_UNSET, NULL, 0 }, }; size_t i; diff --git a/src/connections.c b/src/connections.c index 94d36b4..184ecb9 100644 --- a/src/connections.c +++ b/src/connections.c @@ -394,10 +394,13 @@ static int connection_handle_write_prepare(server *srv, connection *con) { * 403 is from the response handler when noone else catched it * * */ - if (con->uri.path->used && + if ((!con->http_status || con->http_status == 200) && con->uri.path->used && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + con->parsed_response &= ~HTTP_CONTENT_LENGTH; + con->http_status = 200; con->file_finished = 1; @@ -498,9 +501,11 @@ static int connection_handle_write_prepare(server *srv, connection *con) { case 207: case 200: /* class: header + body */ case 201: + case 300: case 301: case 302: case 303: + case 307: break; case 206: /* write_queue is already prepared */ @@ -511,13 +516,13 @@ static int connection_handle_write_prepare(server *srv, connection *con) { default: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; } - if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ @@ -536,12 +541,16 @@ static int connection_handle_write_prepare(server *srv, connection *con) { if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { + data_string *ds; /* no Content-Body, no Content-Length */ - } else if (qlen >= 0) { + if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { + buffer_reset(ds->value); // Headers with empty values are ignored for output + } + } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ - buffer_copy_off_t(srv->tmp_buf, chunkqueue_length(con->write_queue)); + buffer_copy_off_t(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } @@ -582,6 +591,8 @@ static int connection_handle_write_prepare(server *srv, connection *con) { * a HEAD request has the same as a GET * without the content */ + con->file_finished = 1; + chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } @@ -834,14 +845,8 @@ int connection_reset(server *srv, connection *con) { con->plugin_ctx[pd->id] = 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 + /* The cond_cache gets reset in response.c */ +// config_cond_cache_reset(srv, con); #ifdef USE_OPENSSL if (con->ssl_error_want_reuse_buffer) { @@ -1,5 +1,14 @@ #include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined HAVE_STDINT_H #include <stdint.h> +#elif defined HAVE_INTTYPES_H +#include <inttypes.h> +#endif #include "buffer.h" #include "etag.h" diff --git a/src/fdevent_solaris_devpoll.c b/src/fdevent_solaris_devpoll.c index f77daef..76414a4 100644 --- a/src/fdevent_solaris_devpoll.c +++ b/src/fdevent_solaris_devpoll.c @@ -67,7 +67,7 @@ static int fdevent_solaris_devpoll_poll(fdevents *ev, int timeout_ms) { int ret; dopoll.dp_timeout = timeout_ms; - dopoll.dp_nfds = ev->maxfds; + dopoll.dp_nfds = ev->maxfds - 1; dopoll.dp_fds = ev->devpollfds; ret = ioctl(ev->devpoll_fd, DP_POLL, &dopoll); diff --git a/src/http_auth.c b/src/http_auth.c index 1bfb460..89abde6 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -29,6 +29,7 @@ #include "log.h" #include "http_auth.h" #include "http_auth_digest.h" +#include "inet_ntop_cache.h" #include "stream.h" #ifdef USE_OPENSSL @@ -862,7 +863,7 @@ int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, /* password doesn't match */ if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) { - log_error_write(srv, __FILE__, __LINE__, "sbb", "password doesn't match for", con->uri.path, username); + log_error_write(srv, __FILE__, __LINE__, "sbbss", "password doesn't match for ", con->uri.path, username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); buffer_free(username); buffer_free(password); @@ -1130,7 +1131,7 @@ int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p } log_error_write(srv, __FILE__, __LINE__, "sss", - "digest: auth failed for", username, "wrong password"); + "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); buffer_free(b); return 0; diff --git a/src/mod_auth.c b/src/mod_auth.c index 19ed387..7bd306d 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -238,13 +238,13 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { int auth_type_len = auth_realm - http_authorization; if ((auth_type_len == 5) && - (0 == strncmp(http_authorization, "Basic", auth_type_len))) { + (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) { if (0 == strcmp(method->value->ptr, "basic")) { auth_satisfied = http_auth_basic_check(srv, con, p, req, con->uri.path, auth_realm+1); } } else if ((auth_type_len == 6) && - (0 == strncmp(http_authorization, "Digest", auth_type_len))) { + (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { if (0 == strcmp(method->value->ptr, "digest")) { if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, con->uri.path, auth_realm+1))) { con->http_status = 400; diff --git a/src/mod_cgi.c b/src/mod_cgi.c index f5e5b3a..82ae78b 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -1004,6 +1004,7 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer * case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); + return -1; break; default: { handler_ctx *hctx; @@ -1227,8 +1228,14 @@ TRIGGER_FUNC(cgi_trigger) { #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]); #endif + } else if (WIFSIGNALED(status)) { + /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ? + */ + if (WTERMSIG(status) != SIGTERM) { + log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status)); + } } else { - log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); + log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly"); } cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); diff --git a/src/mod_compress.c b/src/mod_compress.c index 464945e..79f6e74 100644 --- a/src/mod_compress.c +++ b/src/mod_compress.c @@ -102,6 +102,50 @@ FREE_FUNC(mod_compress_free) { return HANDLER_GO_ON; } +// 0 on success, -1 for error +int mkdir_recursive(char *dir) { + char *p = dir; + + if (!dir || !dir[0]) + return 0; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(dir, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return 0; // Ignore trailing slash + } + + return (mkdir(dir, 0700) != 0) && (errno != EEXIST) ? -1 : 0; +} + +// 0 on success, -1 for error +int mkdir_for_file(char *filename) { + char *p = filename; + + if (!filename || !filename[0]) + return -1; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(filename, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return -1; // Unexpected trailing slash in filename + } + + return 0; +} + SETDEFAULTS_FUNC(mod_compress_setdefaults) { plugin_data *p = p_d; size_t i = 0; @@ -134,6 +178,8 @@ SETDEFAULTS_FUNC(mod_compress_setdefaults) { } if (!buffer_is_empty(s->compress_cache_dir)) { + mkdir_recursive(s->compress_cache_dir->ptr); + struct stat st; if (0 != stat(s->compress_cache_dir->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "can't stat compress.cache-dir", @@ -342,27 +388,8 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu BUFFER_APPEND_SLASH(p->ofn); if (0 == strncmp(con->physical.path->ptr, con->physical.doc_root->ptr, con->physical.doc_root->used-1)) { - size_t offset = p->ofn->used - 1; - char *dir, *nextdir; - buffer_append_string(p->ofn, con->physical.path->ptr + con->physical.doc_root->used - 1); - buffer_copy_string_buffer(p->b, p->ofn); - - /* mkdir -p ... */ - for (dir = p->b->ptr + offset; NULL != (nextdir = strchr(dir, '/')); dir = nextdir + 1) { - *nextdir = '\0'; - - if (-1 == mkdir(p->b->ptr, 0700)) { - if (errno != EEXIST) { - log_error_write(srv, __FILE__, __LINE__, "sbss", "creating cache-directory", p->b, "failed", strerror(errno)); - - return -1; - } - } - - *nextdir = '/'; - } } else { buffer_append_string_buffer(p->ofn, con->uri.path); } @@ -384,6 +411,11 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu buffer_append_string_buffer(p->ofn, sce->etag); + if (-1 == mkdir_for_file(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "couldn't create directory for file", p->ofn); + return -1; + } + if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) { if (errno == EEXIST) { /* cache-entry exists */ @@ -407,6 +439,11 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu close(ofd); + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + return -1; } @@ -416,6 +453,12 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu close(ofd); close(ifd); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + return -1; } @@ -438,22 +481,29 @@ static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, bu break; } - if (-1 == (r = write(ofd, p->b->ptr, p->b->used))) { - munmap(start, sce->st.st_size); - close(ofd); - close(ifd); - return -1; - } - - if ((size_t)r != p->b->used) { - + if (ret == 0) { + r = write(ofd, p->b->ptr, p->b->used); + if (-1 == r) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "writing cachefile", p->ofn, "failed:", strerror(errno)); + ret = -1; + } else if ((size_t)r != p->b->used) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "writing cachefile", p->ofn, "failed: not enough bytes written"); + ret = -1; + } } munmap(start, sce->st.st_size); close(ofd); close(ifd); - if (ret != 0) return -1; + if (ret != 0) { + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } buffer_copy_string_buffer(con->physical.path, p->ofn); @@ -570,6 +620,8 @@ PHYSICALPATH_FUNC(mod_compress_physical) { off_t max_fsize; stat_cache_entry *sce = NULL; + if (con->mode != DIRECT || con->http_status) return HANDLER_GO_ON; + /* only GET and POST can get compressed */ if (con->request.http_method != HTTP_METHOD_GET && con->request.http_method != HTTP_METHOD_POST) { @@ -678,8 +730,16 @@ PHYSICALPATH_FUNC(mod_compress_physical) { } } else if (0 == deflate_file_to_buffer(srv, con, p, con->physical.path, sce, compression_type)) { + buffer *mtime; response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); + + mtime = strftime_cache_get(srv, sce->st.st_mtime); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + + etag_mutate(con->physical.etag, sce->etag); + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); return HANDLER_FINISHED; diff --git a/src/mod_extforward.c b/src/mod_extforward.c index 8ed336d..be7dd0f 100644 --- a/src/mod_extforward.c +++ b/src/mod_extforward.c @@ -20,6 +20,7 @@ /** * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu + * support chained proxies by glen@delfi.ee, #1528 * * Config example: * @@ -33,6 +34,10 @@ * Note that "all" has precedence over specific entries, * so "all except" setups will not work. * + * In case you have chained proxies, you can add all their IP's to the + * config. However "all" has effect only on connecting IP, as the + * X-Forwarded-For header can not be trusted. + * * Note: The effect of this module is variable on $HTTP["remotip"] directives and * other module's remote ip dependent actions. * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. @@ -225,18 +230,16 @@ static array *extract_forward_array(buffer *pbuffer) char *base, *curr; /* state variable, 0 means not in string, 1 means in string */ int in_str = 0; - for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) - { + for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { if (in_str) { - if ( (*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' ) { + if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':') { /* found an separator , insert value into result array */ - put_string_into_array_len(result, base, curr-base); + put_string_into_array_len(result, base, curr - base); /* change state to not in string */ in_str = 0; } } else { - if (*curr >= '0' && *curr <= '9') - { + if (*curr >= '0' && *curr <= '9') { /* found leading char of an IP address, move base pointer and change state */ base = curr; in_str = 1; @@ -244,9 +247,8 @@ static array *extract_forward_array(buffer *pbuffer) } } /* if breaking out while in str, we got to the end of string, so add it */ - if (in_str) - { - put_string_into_array_len(result, base, curr-base); + if (in_str) { + put_string_into_array_len(result, base, curr - base); } } return result; @@ -255,18 +257,40 @@ static array *extract_forward_array(buffer *pbuffer) #define IP_TRUSTED 1 #define IP_UNTRUSTED 0 /* - check whether ip is trusted, return 1 for trusted , 0 for untrusted -*/ + * check whether ip is trusted, return 1 for trusted , 0 for untrusted + */ static int is_proxy_trusted(const char *ipstr, plugin_data *p) { - data_string* allds = (data_string *) array_get_element(p->conf.forwarder,"all"); + data_string* allds = (data_string *)array_get_element(p->conf.forwarder, "all"); + if (allds) { - if (strcasecmp(allds->value->ptr,"trust") == 0) + if (strcasecmp(allds->value->ptr, "trust") == 0) { return IP_TRUSTED; - else + } else { return IP_UNTRUSTED; + } } - return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED ; + + return (data_string *)array_get_element(p->conf.forwarder, ipstr) ? IP_TRUSTED : IP_UNTRUSTED; +} + +/* + * Return char *ip of last address of proxy that is not trusted. + * Do not accept "all" keyword here. + */ +static const char *last_not_in_array(array *a, plugin_data *p) +{ + array *forwarder = p->conf.forwarder; + + for (int i = a->used - 1; i >= 0; i--) { + data_string *ds = (data_string *)a->data[i]; + const char *ip = ds->value->ptr; + + if (!array_get_element(forwarder, ip)) { + return ip; + } + } + return NULL; } struct addrinfo *ipstr_to_sockaddr(const char *host) @@ -305,7 +329,7 @@ struct addrinfo *ipstr_to_sockaddr(const char *host) static void clean_cond_cache(server *srv, connection *con) { - config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTEIP); + config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP); } URIHANDLER_FUNC(mod_extforward_uri_handler) { @@ -316,9 +340,8 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { struct addrinfo *addrlist = NULL; #endif const char *dst_addr_str = NULL; - int i; array *forward_array = NULL; - char *real_remote_addr = NULL; + const char *real_remote_addr = NULL; #ifdef HAVE_IPV6 #endif @@ -342,7 +365,6 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { return HANDLER_GO_ON; } - /* if the remote ip itself is not trusted , then do nothing */ #ifdef HAVE_IPV6 dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family, con->dst_addr.plain.sa_family == AF_INET6 ? @@ -353,7 +375,9 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { #else dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr); #endif - if (IP_UNTRUSTED == is_proxy_trusted (dst_addr_str, p) ) { + + /* if the remote ip itself is not trusted, then do nothing */ + if (IP_UNTRUSTED == is_proxy_trusted(dst_addr_str, p)) { if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "remote address is NOT a trusted proxy, skipping"); @@ -362,40 +386,34 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { return HANDLER_GO_ON; } + /* build forward_array from forwarded data_string */ forward_array = extract_forward_array(forwarded->value); - - /* Testing shows that multiple headers and multiple values in one header - come in _reverse_ order. So the first one we get is the last one in the request. */ - for (i = forward_array->used - 1; i >= 0; i--) { - data_string *ds = (data_string *) forward_array->data[i]; - if (ds) { - real_remote_addr = ds->value->ptr; - break; - } else { - /* bug ? bailing out here */ - break; - } - } + real_remote_addr = last_not_in_array(forward_array, p); if (real_remote_addr != NULL) { /* parsed */ sock_addr sock; - struct addrinfo *addrs_left; + server_socket *srv_sock = con->srv_socket; + data_string *forwarded_proto = (data_string *)array_get_element(con->request.headers, "X-Forwarded-Proto"); + + if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) { + srv_sock->is_proxy_ssl = 1; + } else { + srv_sock->is_proxy_ssl = 0; + } if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "using address:", real_remote_addr); + log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", real_remote_addr); } #ifdef HAVE_IPV6 addrlist = ipstr_to_sockaddr(real_remote_addr); sock.plain.sa_family = AF_UNSPEC; - for (addrs_left = addrlist; addrs_left != NULL; - addrs_left = addrs_left -> ai_next) { + for (addrs_left = addrlist; addrs_left != NULL; addrs_left = addrs_left -> ai_next) { sock.plain.sa_family = addrs_left->ai_family; - if ( sock.plain.sa_family == AF_INET ) { + if (sock.plain.sa_family == AF_INET) { sock.ipv4.sin_addr = ((struct sockaddr_in*)addrs_left->ai_addr)->sin_addr; break; - } else if ( sock.plain.sa_family == AF_INET6 ) { + } else if (sock.plain.sa_family == AF_INET6) { sock.ipv6.sin6_addr = ((struct sockaddr_in6*)addrs_left->ai_addr)->sin6_addr; break; } @@ -430,7 +448,7 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { if (addrlist != NULL ) freeaddrinfo(addrlist); #endif } - array_free(forward_array); + array_free(forward_array); /* not found */ return HANDLER_GO_ON; diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 75b52ad..9ce9556 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -162,8 +162,8 @@ typedef struct { * if host is one of the local IP adresses the * whole connection is local * - * if tcp/ip should be used host AND port have - * to be specified + * if port is not 0, and host is not specified, + * "localhost" (INADDR_LOOPBACK) is assumed. * */ buffer *host; @@ -823,12 +823,12 @@ static int fcgi_spawn_connection(server *srv, fcgi_addr_in.sin_family = AF_INET; if (buffer_is_empty(host->host)) { - fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } else { struct hostent *he; /* set a useful default */ - fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (NULL == (he = gethostbyname(host->host->ptr))) { @@ -858,7 +858,11 @@ static int fcgi_spawn_connection(server *srv, fcgi_addr = (struct sockaddr *) &fcgi_addr_in; buffer_copy_string(proc->connection_name, "tcp:"); - buffer_append_string_buffer(proc->connection_name, host->host); + if (!buffer_is_empty(host->host)) { + buffer_append_string_buffer(proc->connection_name, host->host); + } else { + buffer_append_string(proc->connection_name, "localhost"); + } buffer_append_string(proc->connection_name, ":"); buffer_append_long(proc->connection_name, proc->port); } @@ -1687,12 +1691,16 @@ static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *h #endif } else { fcgi_addr_in.sin_family = AF_INET; - if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "converting IP address failed for", host->host, - "\nBe sure to specify an IP address here"); - - return -1; + if (!buffer_is_empty(host->host)) { + if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "converting IP address failed for", host->host, + "\nBe sure to specify an IP address here"); + + return -1; + } + } else { + fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } fcgi_addr_in.sin_port = htons(proc->port); servlen = sizeof(fcgi_addr_in); @@ -1702,7 +1710,11 @@ static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *h if (buffer_is_empty(proc->connection_name)) { /* on remote spawing we have to set the connection-name now */ buffer_copy_string(proc->connection_name, "tcp:"); - buffer_append_string_buffer(proc->connection_name, host->host); + if (!buffer_is_empty(host->host)) { + buffer_append_string_buffer(proc->connection_name, host->host); + } else { + buffer_append_string(proc->connection_name, "localhost"); + } buffer_append_string(proc->connection_name, ":"); buffer_append_long(proc->connection_name, proc->port); } @@ -2045,12 +2057,9 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) { s = get_http_version_name(con->request.http_version); FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)),con) -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { + if (srv_sock->is_ssl || srv_sock->is_proxy_ssl) { FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")),con) } -#endif - FCGI_ENV_ADD_CHECK(fcgi_env_add_request_headers(srv, con, p), con); @@ -2343,7 +2352,7 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p /* no header */ buffer_free(packet->b); - log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header too small"); + log_error_write(srv, __FILE__, __LINE__, "sdsds", "FastCGI: header too small:", packet->b->used, "bytes <", sizeof(FCGI_Header), "bytes"); return -1; } @@ -2530,15 +2539,28 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { } if (host->allow_xsendfile && - NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file"))) { + (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file")) + || NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile")))) { stat_cache_entry *sce; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, ds->value, &sce)) { + data_string *dcls = data_string_init(); /* found */ - http_chunk_append_file(srv, con, ds->value, 0, sce->st.st_size); hctx->send_content_body = 0; /* ignore the content */ joblist_append(srv, con); + + buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1); + buffer_copy_long(dcls->value, sce->st.st_size); + dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls); + if (dcls) dcls->free((data_unset*)dcls); + + con->parsed_response |= HTTP_CONTENT_LENGTH; + con->response.content_length = sce->st.st_size; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: couldn't get stat_cache entry for:", + ds->value); } } @@ -2719,9 +2741,14 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { int ret; - /* sanity check */ + /* sanity check: + * - host != NULL + * - either: + * - tcp socket (do not check host->host->uses, as it may be not set which means INADDR_LOOPBACK) + * - unix socket + */ if (!host || - ((!host->host->used || !host->port) && !host->unixsocket->used)) { + (!host->port && !host->unixsocket->used)) { log_error_write(srv, __FILE__, __LINE__, "sxddd", "write-req: error", host, @@ -3456,8 +3483,9 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i if (s_len < ct_len) continue; /* check extension in the form "/fcgi_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - break; + if (*(extension->key->ptr) == '/') { + if (strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) + break; } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { /* check extension in the form ".fcg" */ break; @@ -3473,7 +3501,7 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i for (k = 0; k < extension->used; k++) { host = extension->hosts[k]; - /* we should have at least one proc that can do somthing */ + /* we should have at least one proc that can do something */ if (host->active_procs == 0) { host = NULL; diff --git a/src/mod_proxy.c b/src/mod_proxy.c index c4ac15f..4d8ecdd 100644 --- a/src/mod_proxy.c +++ b/src/mod_proxy.c @@ -1093,15 +1093,17 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p if (s_len < ct_len) continue; /* check extension in the form "/proxy_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - if (s_len > ct_len + 1) { - char *pi_offset; + if (*(extension->key->ptr) == '/') { + if (strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { + if (s_len > ct_len + 1) { + char *pi_offset; - if (0 != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) { - path_info_offset = pi_offset - fn->ptr; + if (0 != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) { + path_info_offset = pi_offset - fn->ptr; + } } + break; } - break; } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { /* check extension in the form ".fcg" */ break; @@ -1203,7 +1205,7 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p } /* didn't found a higher id, wrap to the start */ - if (ndx != -1 && max_usage != INT_MAX) { + if (ndx == -1 && max_usage != INT_MAX) { ndx = max_usage; } diff --git a/src/mod_scgi.c b/src/mod_scgi.c index bc487c5..b7287ba 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -1057,6 +1057,9 @@ SETDEFAULTS_FUNC(mod_scgi_set_defaults) { /* a local socket + self spawning */ size_t pno; + /* HACK: just to make sure the adaptive spawing is disabled */ + df->min_procs = df->max_procs; + if (df->min_procs > df->max_procs) df->max_procs = df->min_procs; if (df->max_load_per_proc < 1) df->max_load_per_proc = 0; @@ -2694,8 +2697,9 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i if (s_len < ct_len) continue; /* check extension in the form "/scgi_pattern" */ - if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) { - break; + if (*(extension->key->ptr) == '/') { + if (strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) + break; } else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) { /* check extension in the form ".fcg" */ break; diff --git a/src/mod_secure_download.c b/src/mod_secure_download.c index 08a0554..57e407f 100644 --- a/src/mod_secure_download.c +++ b/src/mod_secure_download.c @@ -245,7 +245,8 @@ URIHANDLER_FUNC(mod_secdownload_uri_handler) { /* timed-out */ if (srv->cur_ts - ts > p->conf.timeout || srv->cur_ts - ts < -p->conf.timeout) { - con->http_status = 408; + /* "Gone" as the url will never be valid again instead of "408 - Timeout" where the request may be repeated */ + con->http_status = 410; return HANDLER_FINISHED; } diff --git a/src/mod_ssi.c b/src/mod_ssi.c index e706e8f..d02d998 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -36,6 +36,11 @@ #include <sys/filio.h> #endif +#include "etag.h" + +/* The newest modified time of included files for include statement */ +static volatile time_t include_file_last_mtime = 0; + /* init the plugin data */ INIT_FUNC(mod_ssi_init) { plugin_data *p; @@ -575,6 +580,11 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, break; case SSI_INCLUDE: chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size); + + /* Keep the newest mtime of included files */ + if (st.st_mtime > include_file_last_mtime) + include_file_last_mtime = st.st_mtime; + break; } } else { @@ -718,50 +728,57 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, /* father */ int status; ssize_t r; + int was_interrupted = 0; close(from_exec_fds[1]); /* wait for the client to end */ /* - * FIXME: if we get interrupted by a SIGCHILD we count this as error - * - * for now it only happened on OpenBSD. - * - * that leads to zombies and missing output + * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it */ - if (-1 == waitpid(pid, &status, 0)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); - } else if (WIFEXITED(status)) { - int toread; - /* read everything from client and paste it into the output */ - - while(1) { - if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected end-of-file (perhaps the ssi-exec process died)"); - return -1; + do { + if (-1 == waitpid(pid, &status, 0)) { + if (errno == EINTR) { + was_interrupted++; + } else { + was_interrupted = 0; + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); } - - if (toread > 0) { - b = chunkqueue_get_append_buffer(con->write_queue); - - buffer_prepare_copy(b, toread + 1); - - if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { - /* read failed */ - break; + } else if (WIFEXITED(status)) { + int toread; + /* read everything from client and paste it into the output */ + was_interrupted = 0; + + while(1) { + if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected end-of-file (perhaps the ssi-exec process died)"); + return -1; + } + + if (toread > 0) { + b = chunkqueue_get_append_buffer(con->write_queue); + + buffer_prepare_copy(b, toread + 1); + + if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { + /* read failed */ + break; + } else { + b->used = r; + b->ptr[b->used++] = '\0'; + } } else { - b->used = r; - b->ptr[b->used++] = '\0'; + break; } - } else { - break; } + } else { + was_interrupted = 0; + log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); } - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); - } + } while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */ + close(from_exec_fds[0]); break; @@ -912,6 +929,9 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) build_ssi_cgi_vars(srv, con, p); p->if_is_false = 0; + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + if (-1 == stream_open(&s, con->physical.path)) { log_error_write(srv, __FILE__, __LINE__, "sb", "stream-open: ", con->physical.path); @@ -1010,6 +1030,30 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + { + /* Generate "ETag" & "Last-Modified" headers */ + + stat_cache_entry *sce = NULL; + time_t lm_time = 0; + buffer *mtime = NULL; + + stat_cache_get_entry(srv, con, con->physical.path, &sce); + + etag_mutate(con->physical.etag, sce->etag); + response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + + if (sce->st.st_mtime > include_file_last_mtime) + lm_time = sce->st.st_mtime; + else + lm_time = include_file_last_mtime; + + mtime = strftime_cache_get(srv, lm_time); + response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + } + + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + /* reset physical.path */ buffer_reset(con->physical.path); diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c index 59fafee..0ffa901 100644 --- a/src/mod_staticfile.c +++ b/src/mod_staticfile.c @@ -352,7 +352,7 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { size_t k; int s_len; stat_cache_entry *sce = NULL; - buffer *mtime; + buffer *mtime = NULL; data_string *ds; int allow_caching = 1; @@ -450,7 +450,9 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { } } - response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); + if (con->conf.range_requests) { + response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); + } if (allow_caching) { if (p->conf.etags_used && con->etag_flags != 0 && !buffer_is_empty(sce->etag)) { @@ -483,7 +485,23 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { /* if the value is the same as our ETag, we do a Range-request, * otherwise a full 200 */ - if (!buffer_is_equal(ds->value, con->physical.etag)) { + if (ds->value->ptr[0] == '"') { + /** + * client wants a ETag + */ + if (!con->physical.etag) { + do_range_request = 0; + } else if (!buffer_is_equal(ds->value, con->physical.etag)) { + do_range_request = 0; + } + } else if (!mtime) { + /** + * we don't have a Last-Modified and can match the If-Range: + * + * sending all + */ + do_range_request = 0; + } else if (!buffer_is_equal(ds->value, mtime)) { do_range_request = 0; } } diff --git a/src/mod_status.c b/src/mod_status.c index 80c0040..028cbc7 100644 --- a/src/mod_status.c +++ b/src/mod_status.c @@ -560,6 +560,8 @@ static handler_t mod_status_handle_server_status_text(server *srv, connection *c double avg; time_t ts; char buf[32]; + unsigned int k; + unsigned int l; b = chunkqueue_get_append_buffer(con->write_queue); @@ -588,6 +590,22 @@ static handler_t mod_status_handle_server_status_text(server *srv, connection *c buffer_append_long(b, srv->conns->used); BUFFER_APPEND_STRING_CONST(b, "\n"); + BUFFER_APPEND_STRING_CONST(b, "IdleServers: "); + buffer_append_long(b, srv->conns->size - srv->conns->used); + BUFFER_APPEND_STRING_CONST(b, "\n"); + + /* output scoreboard */ + BUFFER_APPEND_STRING_CONST(b, "Scoreboard: "); + for (k = 0; k < srv->conns->used; k++) { + connection *c = srv->conns->ptr[k]; + const char *state = connection_get_short_state(c->state); + buffer_append_string_len(b, state, 1); + } + for (l = 0; l < srv->conns->size - srv->conns->used; l++) { + BUFFER_APPEND_STRING_CONST(b, "_"); + } + BUFFER_APPEND_STRING_CONST(b, "\n"); + /* set text/plain output */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); diff --git a/src/mod_userdir.c b/src/mod_userdir.c index 2fef0f2..e664bf2 100644 --- a/src/mod_userdir.c +++ b/src/mod_userdir.c @@ -21,6 +21,7 @@ typedef struct { array *include_user; buffer *path; buffer *basepath; + unsigned short letterhomes; } plugin_config; typedef struct { @@ -87,6 +88,7 @@ SETDEFAULTS_FUNC(mod_userdir_set_defaults) { { "userdir.exclude-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "userdir.include-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ { "userdir.basepath", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "userdir.letterhomes", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 4 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -102,11 +104,13 @@ SETDEFAULTS_FUNC(mod_userdir_set_defaults) { s->include_user = array_init(); s->path = buffer_init(); s->basepath = buffer_init(); + s->letterhomes = 0; cv[0].destination = s->path; cv[1].destination = s->exclude_user; cv[2].destination = s->include_user; cv[3].destination = s->basepath; + cv[4].destination = &(s->letterhomes); p->config_storage[i] = s; @@ -128,6 +132,7 @@ static int mod_userdir_patch_connection(server *srv, connection *con, plugin_dat PATCH(exclude_user); PATCH(include_user); PATCH(basepath); + PATCH(letterhomes); /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { @@ -149,6 +154,8 @@ static int mod_userdir_patch_connection(server *srv, connection *con, plugin_dat PATCH(include_user); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) { PATCH(basepath); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) { + PATCH(letterhomes); } } } @@ -170,6 +177,11 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { mod_userdir_patch_connection(srv, con, p); + /* enforce the userdir.path to be set in the config, ugly fix for #1587; + * should be replaced with a clean .enabled option in 1.5 + */ + if (p->conf.path->used == 0) return HANDLER_GO_ON; + uri_len = con->uri.path->used - 1; /* /~user/foo.html -> /home/user/public_html/foo.html */ @@ -253,6 +265,10 @@ URIHANDLER_FUNC(mod_userdir_docroot_handler) { buffer_copy_string_buffer(p->temp_path, p->conf.basepath); BUFFER_APPEND_SLASH(p->temp_path); + if (p->conf.letterhomes) { + buffer_append_string_len(p->temp_path, p->username->ptr, 1); + BUFFER_APPEND_SLASH(p->temp_path); + } buffer_append_string_buffer(p->temp_path, p->username); } BUFFER_APPEND_SLASH(p->temp_path); diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c index 6586efb..5f01812 100644 --- a/src/network_linux_sendfile.c +++ b/src/network_linux_sendfile.c @@ -162,6 +162,7 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, switch (errno) { case EAGAIN: case EINTR: + /* ok, we can't send more, let's try later again */ r = 0; break; case EPIPE: @@ -172,9 +173,7 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, "sendfile failed:", strerror(errno), fd); return -1; } - } - - if (r == 0) { + } else if (r == 0) { int oerrno = errno; /* We got an event to write but we wrote nothing * diff --git a/src/network_openssl.c b/src/network_openssl.c index e6df35e..ff9fb97 100644 --- a/src/network_openssl.c +++ b/src/network_openssl.c @@ -58,31 +58,6 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); } - /* evil hack for opera 9.01 and 8.54 and earlier - * - * opera hangs if the trainling 0\r\n\r\n is in a seperate SSL-packet - * - * we try to move the packet into the previous mem-chunk if possible - */ - if ((cq == con->write_queue) && - (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) && - (con->file_finished)) { - /* merge the last chunk into the previous chunk */ - - for(c = cq->first; c && c->next && c->next->next; c = c->next); - - if (c && - c->type == MEM_CHUNK && - c->next && - c->next->type == MEM_CHUNK && - c->next->mem->used == sizeof("0\r\n\r\n") && - 0 == strcmp(c->next->mem->ptr, "0\r\n\r\n")) { - buffer_append_string_buffer(c->mem, c->next->mem); - - c->next->mem->used = 0; - } - } - for(c = cq->first; c; c = c->next) { int chunk_finished = 0; @@ -128,6 +103,7 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* no, but we have errno */ switch(errno) { case EPIPE: + case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", @@ -230,6 +206,7 @@ int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chu /* no, but we have errno */ switch(errno) { case EPIPE: + case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", diff --git a/src/response.c b/src/response.c index b8775f0..bc2bdd1 100644 --- a/src/response.c +++ b/src/response.c @@ -19,6 +19,7 @@ #include "stat_cache.h" #include "chunk.h" +#include "configfile.h" #include "connections.h" #include "plugin.h" @@ -59,7 +60,8 @@ int http_response_write_header(server *srv, connection *con) { ds = (data_string *)con->response.headers->data[i]; if (ds->value->used && ds->key->used && - 0 != strncmp(ds->key->ptr, "X-LIGHTTPD-", sizeof("X-LIGHTTPD-") - 1)) { + 0 != strncmp(ds->key->ptr, "X-LIGHTTPD-", sizeof("X-LIGHTTPD-") - 1) && + 0 != strncmp(ds->key->ptr, "X-Sendfile", sizeof("X-Sendfile") - 1)) { if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Date"))) have_date = 1; if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Server"))) have_server = 1; @@ -149,6 +151,9 @@ handler_t http_response_prepare(server *srv, connection *con) { * * */ + config_cond_cache_reset(srv, con); + config_setup_connection(srv, con); // Perhaps this could be removed at other places. + if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); } @@ -180,11 +185,13 @@ handler_t http_response_prepare(server *srv, connection *con) { buffer_copy_string_buffer(con->uri.authority, con->request.http_host); buffer_to_lower(con->uri.authority); + config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */ 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_REMOTE_IP); /* 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_USER_AGENT);/* User-Agent: */ config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ + config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */ /** their might be a fragment which has to be cut away */ if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { @@ -270,7 +277,7 @@ handler_t http_response_prepare(server *srv, connection *con) { */ config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */ - config_patch_connection(srv, con, COMP_HTTP_QUERYSTRING); /* HTTPqs */ + config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */ /* do we have to downgrade to 1.0 ? */ if (!con->conf.allow_http11) { @@ -547,17 +554,14 @@ handler_t http_response_prepare(server *srv, connection *con) { buffer_copy_string_buffer(srv->tmp_buf, con->physical.path); do { - struct stat st; - if (slash) { buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr); } else { buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); } - if (0 == stat(con->physical.path->ptr, &(st)) && - S_ISREG(st.st_mode)) { - found = 1; + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + found = S_ISREG(sce->st.st_mode); break; } @@ -589,6 +593,20 @@ handler_t http_response_prepare(server *srv, connection *con) { return HANDLER_FINISHED; } +#ifdef HAVE_LSTAT + if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + }; +#endif + /* we have a PATHINFO */ if (pathinfo) { buffer_copy_string(con->request.pathinfo, pathinfo); @@ -631,8 +649,14 @@ handler_t http_response_prepare(server *srv, connection *con) { /* if we are still here, no one wanted the file, status 403 is ok I think */ - if (con->mode == DIRECT) { - con->http_status = 403; + if (con->mode == DIRECT && con->http_status == 0) { + switch (con->request.http_method) { + case HTTP_METHOD_OPTIONS: + con->http_status = 200; + break; + default: + con->http_status = 403; + } return HANDLER_FINISHED; } diff --git a/src/server.c b/src/server.c index 132eb32..585a973 100644 --- a/src/server.c +++ b/src/server.c @@ -697,9 +697,6 @@ int main (int argc, char **argv) { } } - /* #372: solaris need some fds extra for devpoll */ - if (rlim.rlim_cur > 10) rlim.rlim_cur -= 10; - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { @@ -759,6 +756,19 @@ int main (int argc, char **argv) { return -1; } +#ifdef HAVE_PWD_H + /* + * Change group before chroot, when we have access + * to /etc/group + * */ + if (srv->srvconf.groupname->used) { + setgid(grp->gr_gid); + setgroups(0, NULL); + if (srv->srvconf.username->used) { + initgroups(srv->srvconf.username->ptr, grp->gr_gid); + } + } +#endif #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); @@ -775,15 +785,7 @@ int main (int argc, char **argv) { #endif #ifdef HAVE_PWD_H /* drop root privs */ - if (srv->srvconf.groupname->used) { - setgid(grp->gr_gid); - setgroups(0, NULL); - } - if (srv->srvconf.username->used) { - if (srv->srvconf.groupname->used) { - initgroups(srv->srvconf.username->ptr, grp->gr_gid); - } setuid(pwd->pw_uid); } #endif @@ -891,6 +893,17 @@ int main (int argc, char **argv) { pid_fd = -1; } + // Close stderr ASAP in the child process to make sure that nothing + // is being written to that fd which may not be valid anymore. + if (-1 == log_error_open(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); + + plugins_free(srv); + network_close(srv); + server_free(srv); + return -1; + } + if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); @@ -941,15 +954,7 @@ int main (int argc, char **argv) { return -1; } - if (-1 == log_error_open(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "opening errorlog failed, dying"); - plugins_free(srv); - network_close(srv); - server_free(srv); - return -1; - } #ifdef HAVE_SIGACTION diff --git a/src/spawn-fcgi.c b/src/spawn-fcgi.c index 8237e79..60e02bd 100644 --- a/src/spawn-fcgi.c +++ b/src/spawn-fcgi.c @@ -37,7 +37,7 @@ typedef int socklen_t; #endif #ifdef HAVE_SYS_UN_H -int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const char *unixsocket, int child_count, int pid_fd, int nofork) { +int fcgi_spawn_connection(char *appPath, char **appArgv, char *addr, unsigned short port, const char *unixsocket, int child_count, int pid_fd, int nofork) { int fcgi_fd; int socket_type, status; struct timeval tv = { 0, 100 * 1000 }; @@ -48,6 +48,9 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const socklen_t servlen; + pid_t child; + int val; + if (child_count < 2) { child_count = 5; } @@ -71,6 +74,25 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const #endif socket_type = AF_UNIX; fcgi_addr = (struct sockaddr *) &fcgi_addr_un; + + /* check if some backend is listening on the socket + * as if we delete the socket-file and rebind there will be no "socket already in use" error + */ + if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { + fprintf(stderr, "%s.%d\n", + __FILE__, __LINE__); + return -1; + } + + if (-1 != connect(fcgi_fd, fcgi_addr, servlen)) { + fprintf(stderr, "%s.%d: socket is already used, can't spawn\n", + __FILE__, __LINE__); + return -1; + } + + /* cleanup previous socket if it exists */ + unlink(unixsocket); + close(fcgi_fd); } else { fcgi_addr_in.sin_family = AF_INET; if (addr != NULL) { @@ -85,144 +107,128 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const fcgi_addr = (struct sockaddr *) &fcgi_addr_in; } + /* open socket */ if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return -1; } - if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) { - /* server is not up, spawn in */ - pid_t child; - int val; + val = 1; + if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { + fprintf(stderr, "%s.%d\n", + __FILE__, __LINE__); + return -1; + } - if (unixsocket) unlink(unixsocket); + /* create socket */ + if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { + fprintf(stderr, "%s.%d: bind failed: %s\n", + __FILE__, __LINE__, + strerror(errno)); + return -1; + } - close(fcgi_fd); + if (-1 == listen(fcgi_fd, 1024)) { + fprintf(stderr, "%s.%d: fd = -1\n", + __FILE__, __LINE__); + return -1; + } - /* reopen socket */ - if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } + if (!nofork) { + child = fork(); + } else { + child = 0; + } - val = 1; - if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } + switch (child) { + case 0: { + char cgi_childs[64]; - /* create socket */ - if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { - fprintf(stderr, "%s.%d: bind failed: %s\n", - __FILE__, __LINE__, - strerror(errno)); - return -1; - } + int i = 0; - if (-1 == listen(fcgi_fd, 1024)) { - fprintf(stderr, "%s.%d: fd = -1\n", - __FILE__, __LINE__); - return -1; - } + /* is safe as we limit to 256 childs */ + sprintf(cgi_childs, "PHP_FCGI_CHILDREN=%d", child_count); - if (!nofork) { - child = fork(); - } else { - child = 0; + if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { + close(FCGI_LISTENSOCK_FILENO); + dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); + close(fcgi_fd); } - switch (child) { - case 0: { - char cgi_childs[64]; - char *b; - - int i = 0; - - /* is save as we limit to 256 childs */ - sprintf(cgi_childs, "PHP_FCGI_CHILDREN=%d", child_count); + /* we don't need the client socket */ + for (i = 3; i < 256; i++) { + close(i); + } - if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { - close(FCGI_LISTENSOCK_FILENO); - dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); - close(fcgi_fd); - } + /* create environment */ - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } + putenv(cgi_childs); - /* create environment */ + /* fork and replace shell */ + if (appArgv) { + execv(appArgv[0], appArgv); - putenv(cgi_childs); - - /* fork and replace shell */ - b = malloc(strlen("exec ") + strlen(appPath) + 1); + } else { + char *b = malloc(strlen("exec ") + strlen(appPath) + 1); strcpy(b, "exec "); strcat(b, appPath); /* exec the cgi */ execl("/bin/sh", "sh", "-c", b, (char *)NULL); + } - exit(errno); + exit(errno); + + break; + } + case -1: + /* error */ + break; + default: + /* father */ + + /* wait */ + select(0, NULL, NULL, NULL, &tv); + + switch (waitpid(child, &status, WNOHANG)) { + case 0: + fprintf(stderr, "%s.%d: child spawned successfully: PID: %d\n", + __FILE__, __LINE__, + child); + + /* write pid file */ + if (pid_fd != -1) { + /* assume a 32bit pid_t */ + char pidbuf[12]; + + snprintf(pidbuf, sizeof(pidbuf) - 1, "%d", child); + + write(pid_fd, pidbuf, strlen(pidbuf)); + close(pid_fd); + pid_fd = -1; + } break; - } case -1: - /* error */ break; default: - /* father */ - - /* wait */ - select(0, NULL, NULL, NULL, &tv); - - switch (waitpid(child, &status, WNOHANG)) { - case 0: - fprintf(stderr, "%s.%d: child spawned successfully: PID: %d\n", + if (WIFEXITED(status)) { + fprintf(stderr, "%s.%d: child exited with: %d, %s\n", + __FILE__, __LINE__, + WEXITSTATUS(status), strerror(WEXITSTATUS(status))); + } else if (WIFSIGNALED(status)) { + fprintf(stderr, "%s.%d: child signaled: %d\n", + __FILE__, __LINE__, + WTERMSIG(status)); + } else { + fprintf(stderr, "%s.%d: child died somehow: %d\n", __FILE__, __LINE__, - child); - - /* write pid file */ - if (pid_fd != -1) { - /* assume a 32bit pid_t */ - char pidbuf[12]; - - snprintf(pidbuf, sizeof(pidbuf) - 1, "%d", child); - - write(pid_fd, pidbuf, strlen(pidbuf)); - close(pid_fd); - pid_fd = -1; - } - - break; - case -1: - break; - default: - if (WIFEXITED(status)) { - fprintf(stderr, "%s.%d: child exited with: %d, %s\n", - __FILE__, __LINE__, - WEXITSTATUS(status), strerror(WEXITSTATUS(status))); - } else if (WIFSIGNALED(status)) { - fprintf(stderr, "%s.%d: child signaled: %d\n", - __FILE__, __LINE__, - WTERMSIG(status)); - } else { - fprintf(stderr, "%s.%d: child died somehow: %d\n", - __FILE__, __LINE__, - status); - } + status); } - - break; } - } else { - fprintf(stderr, "%s.%d: socket is already used, can't spawn\n", - __FILE__, __LINE__); - return -1; + + break; } close(fcgi_fd); @@ -239,9 +245,12 @@ void show_version () { } void show_help () { - char *b = "spawn-fcgi" "-" PACKAGE_VERSION \ -" - spawns fastcgi processes\n" \ -"usage:\n" \ + char *b = \ +"Usage: spawn-fcgi [options] -- <fcgiapp> [fcgi app arguments]\n" \ +"\n" \ +"spawn-fcgi v" PACKAGE_VERSION " - spawns fastcgi processes\n" \ +"\n" \ +"Options:\n" \ " -f <fcgiapp> filename of the fcgi-application\n" \ " -a <addr> bind to ip address\n" \ " -p <port> bind to tcp-port\n" \ @@ -264,6 +273,7 @@ int main(int argc, char **argv) { char *fcgi_app = NULL, *changeroot = NULL, *username = NULL, *groupname = NULL, *unixsocket = NULL, *pid_file = NULL, *addr = NULL; + char **fcgi_app_argv = { NULL }; unsigned short port = 0; int child_count = 5; int i_am_root, o; @@ -274,10 +284,10 @@ int main(int argc, char **argv) { i_am_root = (getuid() == 0); - while(-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:s:P:"))) { + while(-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:s:P:"))) { switch(o) { case 'f': fcgi_app = optarg; break; - case 'a': addr = optarg;/* ip addr */ break; + case 'a': addr = optarg;/* ip addr */ break; case 'p': port = strtol(optarg, NULL, 10);/* port */ break; case 'C': child_count = strtol(optarg, NULL, 10);/* */ break; case 's': unixsocket = optarg; /* unix-domain socket */ break; @@ -294,7 +304,11 @@ int main(int argc, char **argv) { } } - if (fcgi_app == NULL || (port == 0 && unixsocket == NULL)) { + if (optind < argc) { + fcgi_app_argv = &argv[optind]; + } + + if ((fcgi_app == NULL && fcgi_app_argv == NULL) || (port == 0 && unixsocket == NULL)) { show_help(); return -1; } @@ -404,6 +418,18 @@ int main(int argc, char **argv) { } } + /* + * Change group before chroot, when we have access + * to /etc/group + */ + if (groupname) { + setgid(grp->gr_gid); + setgroups(0, NULL); + if (username) { + initgroups(username, grp->gr_gid); + } + } + if (changeroot) { if (-1 == chroot(changeroot)) { fprintf(stderr, "%s.%d: %s %s\n", @@ -420,18 +446,12 @@ int main(int argc, char **argv) { } /* drop root privs */ - if (groupname) { - setgid(grp->gr_gid); - } if (username) { - if (groupname) { - initgroups(username, grp->gr_gid); - } setuid(pwd->pw_uid); } } - return fcgi_spawn_connection(fcgi_app, addr, port, unixsocket, child_count, pid_fd, nofork); + return fcgi_spawn_connection(fcgi_app, fcgi_app_argv, addr, port, unixsocket, child_count, pid_fd, nofork); } #else int main() { diff --git a/src/stream.c b/src/stream.c index aac6cf7..d249647 100644 --- a/src/stream.c +++ b/src/stream.c @@ -64,6 +64,7 @@ int stream_open(stream *f, buffer *fn) { NULL); if (!mh) { +/* LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | @@ -73,7 +74,7 @@ int stream_open(stream *f, buffer *fn) { MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); - +*/ return -1; } |
