From 20ef055c2f698618142a280d5cbc010acacfd651 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 15 Apr 2010 19:46:35 +0000 Subject: [PATCH] uhttpd: - make network timeout configurable, increase default to 30 seconds (#7067) - follow symlinks in docroot and add option to disable that - fix mimetype detection for files with combined extensions (.tar.gz, ...) SVN-Revision: 20883 --- package/uhttpd/Makefile | 2 +- package/uhttpd/files/uhttpd.config | 8 ++- package/uhttpd/files/uhttpd.init | 1 + package/uhttpd/src/uhttpd-file.c | 20 +++--- package/uhttpd/src/uhttpd-mimetypes.h | 97 ++++++++++++++------------- package/uhttpd/src/uhttpd-utils.c | 82 ++++++++++++++++++++-- package/uhttpd/src/uhttpd.c | 54 ++++++++++----- package/uhttpd/src/uhttpd.h | 4 +- 8 files changed, 186 insertions(+), 82 deletions(-) diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile index 6b4db3e15f..0f267be2af 100644 --- a/package/uhttpd/Makefile +++ b/package/uhttpd/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uhttpd -PKG_RELEASE:=7 +PKG_RELEASE:=8 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) diff --git a/package/uhttpd/files/uhttpd.config b/package/uhttpd/files/uhttpd.config index 79b018cf80..acdd62ea4e 100644 --- a/package/uhttpd/files/uhttpd.config +++ b/package/uhttpd/files/uhttpd.config @@ -29,10 +29,16 @@ config uhttpd main # CGI/Lua timeout, if the called script does not # write data within the given amount of seconds, - # the server will temrinate the request with + # the server will terminate the request with # 504 Gateway Timeout response. option script_timeout 60 + # Network timeout, if the current connection is + # blocked for the specified amount of seconds, + # the server will terminate the associated + # request process. + option network_timeout 30 + # Basic auth realm, defaults to local hostname # option realm OpenWrt diff --git a/package/uhttpd/files/uhttpd.init b/package/uhttpd/files/uhttpd.init index 4b747a0da1..14f8fc4c46 100755 --- a/package/uhttpd/files/uhttpd.init +++ b/package/uhttpd/files/uhttpd.init @@ -58,6 +58,7 @@ start_instance() append_arg "$cfg" lua_prefix "-l" append_arg "$cfg" lua_handler "-L" append_arg "$cfg" script_timeout "-t" + append_arg "$cfg" network_timeout "-T" config_get http "$cfg" listen_http for listen in $http; do diff --git a/package/uhttpd/src/uhttpd-file.c b/package/uhttpd/src/uhttpd-file.c index 2a06f85206..81f66a34b0 100644 --- a/package/uhttpd/src/uhttpd-file.c +++ b/package/uhttpd/src/uhttpd-file.c @@ -29,23 +29,21 @@ static const char * uh_file_mime_lookup(const char *path) { struct mimetype *m = &uh_mime_types[0]; - char *p, *pd, *ps; + char *e; - ps = strrchr(path, '/'); - pd = strrchr(path, '.'); - - /* use either slash or dot as separator, whatever comes last */ - p = (ps && pd && (ps > pd)) ? ps : pd; - - if( (p != NULL) && (*(++p) != 0) ) + while( m->extn ) { - while( m->extn ) + e = &path[strlen(path)-1]; + + while( e >= path ) { - if( ! strcasecmp(p, m->extn) ) + if( (*e == '.') && !strcasecmp(&e[1], m->extn) ) return m->mime; - m++; + e--; } + + m++; } return "application/octet-stream"; diff --git a/package/uhttpd/src/uhttpd-mimetypes.h b/package/uhttpd/src/uhttpd-mimetypes.h index 1c93f31133..032d3d0c25 100644 --- a/package/uhttpd/src/uhttpd-mimetypes.h +++ b/package/uhttpd/src/uhttpd-mimetypes.h @@ -20,58 +20,61 @@ static struct mimetype uh_mime_types[] = { - { "txt", "text/plain" }, - { "log", "text/plain" }, - { "js", "text/javascript" }, - { "css", "text/css" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "diff", "text/x-patch" }, - { "patch", "text/x-patch" }, - { "c", "text/x-csrc" }, - { "h", "text/x-chdr" }, - { "o", "text/x-object" }, - { "ko", "text/x-object" }, + { "txt", "text/plain" }, + { "log", "text/plain" }, + { "js", "text/javascript" }, + { "css", "text/css" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "diff", "text/x-patch" }, + { "patch", "text/x-patch" }, + { "c", "text/x-csrc" }, + { "h", "text/x-chdr" }, + { "o", "text/x-object" }, + { "ko", "text/x-object" }, - { "bmp", "image/bmp" }, - { "gif", "image/gif" }, - { "png", "image/png" }, - { "jpg", "image/jpeg" }, - { "jpeg", "image/jpeg" }, - { "svg", "image/svg+xml" }, + { "bmp", "image/bmp" }, + { "gif", "image/gif" }, + { "png", "image/png" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "svg", "image/svg+xml" }, - { "zip", "application/zip" }, - { "pdf", "application/pdf" }, - { "xml", "application/xml" }, - { "xsl", "application/xml" }, - { "doc", "application/msword" }, - { "ppt", "application/vnd.ms-powerpoint" }, - { "xls", "application/vnd.ms-excel" }, - { "odt", "application/vnd.oasis.opendocument.text" }, - { "odp", "application/vnd.oasis.opendocument.presentation" }, - { "pl", "application/x-perl" }, - { "sh", "application/x-shellscript" }, - { "php", "application/x-php" }, - { "deb", "application/x-deb" }, - { "iso", "application/x-cd-image" }, - { "tgz", "application/x-compressed-tar" }, - { "gz", "application/x-gzip" }, - { "bz2", "application/x-bzip" }, - { "tar", "application/x-tar" }, - { "rar", "application/x-rar-compressed" }, + { "zip", "application/zip" }, + { "pdf", "application/pdf" }, + { "xml", "application/xml" }, + { "xsl", "application/xml" }, + { "doc", "application/msword" }, + { "ppt", "application/vnd.ms-powerpoint" }, + { "xls", "application/vnd.ms-excel" }, + { "odt", "application/vnd.oasis.opendocument.text" }, + { "odp", "application/vnd.oasis.opendocument.presentation" }, + { "pl", "application/x-perl" }, + { "sh", "application/x-shellscript" }, + { "php", "application/x-php" }, + { "deb", "application/x-deb" }, + { "iso", "application/x-cd-image" }, + { "tar.gz", "application/x-compressed-tar" }, + { "tgz", "application/x-compressed-tar" }, + { "gz", "application/x-gzip" }, + { "tar.bz2", "application/x-bzip-compressed-tar" }, + { "tbz", "application/x-bzip-compressed-tar" }, + { "bz2", "application/x-bzip" }, + { "tar", "application/x-tar" }, + { "rar", "application/x-rar-compressed" }, - { "mp3", "audio/mpeg" }, - { "ogg", "audio/x-vorbis+ogg" }, - { "wav", "audio/x-wav" }, + { "mp3", "audio/mpeg" }, + { "ogg", "audio/x-vorbis+ogg" }, + { "wav", "audio/x-wav" }, - { "mpg", "video/mpeg" }, - { "mpeg", "video/mpeg" }, - { "avi", "video/x-msvideo" }, + { "mpg", "video/mpeg" }, + { "mpeg", "video/mpeg" }, + { "avi", "video/x-msvideo" }, - { "README", "text/plain" }, - { "log", "text/plain" }, - { "cfg", "text/plain" }, - { "conf", "text/plain" }, + { "README", "text/plain" }, + { "log", "text/plain" }, + { "cfg", "text/plain" }, + { "conf", "text/plain" }, { NULL, NULL } }; diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c index 55b2c410e3..e65f2136d6 100644 --- a/package/uhttpd/src/uhttpd-utils.c +++ b/package/uhttpd/src/uhttpd-utils.c @@ -116,8 +116,8 @@ int uh_tcp_send(struct client *cl, const char *buf, int len) FD_ZERO(&writer); FD_SET(cl->socket, &writer); - timeout.tv_sec = 0; - timeout.tv_usec = 500000; + timeout.tv_sec = cl->server->conf->network_timeout; + timeout.tv_usec = 0; if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 ) { @@ -376,6 +376,78 @@ int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen) return len; } +static char * canonpath(const char *path, char *path_resolved) +{ + char path_copy[PATH_MAX]; + char *path_cpy = path_copy; + char *path_res = path_resolved; + + struct stat s; + + + /* relative -> absolute */ + if( *path != '/' ) + { + getcwd(path_copy, PATH_MAX); + strncat(path_copy, "/", PATH_MAX - strlen(path_copy)); + strncat(path_copy, path, PATH_MAX - strlen(path_copy)); + } + else + { + strncpy(path_copy, path, PATH_MAX); + } + + /* normalize */ + while( (*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2)) ) + { + if( *path_cpy == '/' ) + { + /* skip repeating / */ + if( path_cpy[1] == '/' ) + { + path_cpy++; + continue; + } + + /* /./ or /../ */ + else if( path_cpy[1] == '.' ) + { + /* skip /./ */ + if( (path_cpy[2] == '/') || (path_cpy[2] == '\0') ) + { + path_cpy += 2; + continue; + } + + /* collapse /x/../ */ + else if( path_cpy[2] == '.' ) + { + while( (path_res > path_resolved) && (*--path_res != '/') ) + ; + + path_cpy += 3; + continue; + } + } + } + + *path_res++ = *path_cpy++; + } + + /* remove trailing slash if not root / */ + if( (path_res > (path_resolved+1)) && (path_res[-1] == '/') ) + path_res--; + else if( path_res == path_resolved ) + *path_res++ = '/'; + + *path_res = '\0'; + + /* test access */ + if( !stat(path_resolved, &s) && (s.st_mode & S_IROTH) ) + return path_resolved; + + return NULL; +} struct path_info * uh_path_lookup(struct client *cl, const char *url) { @@ -387,6 +459,7 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) char *docroot = cl->server->conf->docroot; char *pathptr = NULL; + int no_sym = cl->server->conf->no_symlinks; int i = 0; struct stat s; @@ -432,8 +505,9 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) memset(path_info, 0, sizeof(path_info)); memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1)); - if( realpath(path_info, path_phys) ) - { + if( no_sym ? realpath(path_info, path_phys) + : canonpath(path_info, path_phys) + ) { memset(path_info, 0, sizeof(path_info)); memcpy(path_info, &buffer[i], min(strlen(buffer) - i, sizeof(path_info) - 1)); diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c index da3779413e..9de77c814d 100644 --- a/package/uhttpd/src/uhttpd.c +++ b/package/uhttpd/src/uhttpd.c @@ -416,8 +416,11 @@ int main (int argc, char **argv) /* maximum file descriptor number */ int new_fd, cur_fd, max_fd = 0; +#ifdef HAVE_TLS int tls = 0; int keys = 0; +#endif + int bound = 0; int nofork = 0; @@ -426,9 +429,10 @@ int main (int argc, char **argv) char bind[128]; char *port = NULL; - /* library handles */ - void *tls_lib; - void *lua_lib; +#if defined(HAVE_TLS) || defined(HAVE_LUA) + /* library handle */ + void *lib; +#endif /* clear the master and temp sets */ FD_ZERO(&used_fds); @@ -466,7 +470,7 @@ int main (int argc, char **argv) #ifdef HAVE_TLS /* load TLS plugin */ - if( ! (tls_lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) ) + if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load TLS plugin - disabling SSL support! " @@ -476,14 +480,14 @@ int main (int argc, char **argv) else { /* resolve functions */ - if( !(conf.tls_init = dlsym(tls_lib, "uh_tls_ctx_init")) || - !(conf.tls_cert = dlsym(tls_lib, "uh_tls_ctx_cert")) || - !(conf.tls_key = dlsym(tls_lib, "uh_tls_ctx_key")) || - !(conf.tls_free = dlsym(tls_lib, "uh_tls_ctx_free")) || - !(conf.tls_accept = dlsym(tls_lib, "uh_tls_client_accept")) || - !(conf.tls_close = dlsym(tls_lib, "uh_tls_client_close")) || - !(conf.tls_recv = dlsym(tls_lib, "uh_tls_client_recv")) || - !(conf.tls_send = dlsym(tls_lib, "uh_tls_client_send")) + if( !(conf.tls_init = dlsym(lib, "uh_tls_ctx_init")) || + !(conf.tls_cert = dlsym(lib, "uh_tls_ctx_cert")) || + !(conf.tls_key = dlsym(lib, "uh_tls_ctx_key")) || + !(conf.tls_free = dlsym(lib, "uh_tls_ctx_free")) || + !(conf.tls_accept = dlsym(lib, "uh_tls_client_accept")) || + !(conf.tls_close = dlsym(lib, "uh_tls_client_close")) || + !(conf.tls_recv = dlsym(lib, "uh_tls_client_recv")) || + !(conf.tls_send = dlsym(lib, "uh_tls_client_send")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " @@ -501,7 +505,7 @@ int main (int argc, char **argv) } #endif - while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:t:")) > 0 ) + while( (opt = getopt(argc, argv, "fSC:K:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0 ) { switch(opt) { @@ -592,6 +596,11 @@ int main (int argc, char **argv) } break; + /* don't follow symlinks */ + case 'S': + conf.no_symlinks = 1; + break; + #ifdef HAVE_CGI /* cgi prefix */ case 'x': @@ -618,6 +627,11 @@ int main (int argc, char **argv) break; #endif + /* network timeout */ + case 'T': + conf.network_timeout = atoi(optarg); + break; + /* no fork */ case 'f': nofork = 1; @@ -663,6 +677,7 @@ int main (int argc, char **argv) " -K file ASN.1 server private key file\n" #endif " -h directory Specify the document root, default is '.'\n" + " -S Do not follow symbolic links outside of the docroot\n" #ifdef HAVE_LUA " -l string URL prefix for Lua handler, default is '/lua'\n" " -L file Lua handler script, omit to disable Lua\n" @@ -673,6 +688,7 @@ int main (int argc, char **argv) #if defined(HAVE_CGI) || defined(HAVE_LUA) " -t seconds CGI and Lua script timeout in seconds, default is 60\n" #endif + " -T seconds Network timeout in seconds, default is 30\n" " -d string URL decode given string\n" " -r string Specify basic auth realm\n" " -m string MD5 crypt given string\n" @@ -712,6 +728,10 @@ int main (int argc, char **argv) /* config file */ uh_config_parse(conf.file); + /* default network timeout */ + if( conf.network_timeout <= 0 ) + conf.network_timeout = 30; + #if defined(HAVE_CGI) || defined(HAVE_LUA) /* default script timeout */ if( conf.script_timeout <= 0 ) @@ -726,7 +746,7 @@ int main (int argc, char **argv) #ifdef HAVE_LUA /* load Lua plugin */ - if( ! (lua_lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) ) + if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load Lua plugin - disabling Lua support! " @@ -736,9 +756,9 @@ int main (int argc, char **argv) else { /* resolve functions */ - if( !(conf.lua_init = dlsym(lua_lib, "uh_lua_init")) || - !(conf.lua_close = dlsym(lua_lib, "uh_lua_close")) || - !(conf.lua_request = dlsym(lua_lib, "uh_lua_request")) + if( !(conf.lua_init = dlsym(lib, "uh_lua_init")) || + !(conf.lua_close = dlsym(lib, "uh_lua_close")) || + !(conf.lua_request = dlsym(lib, "uh_lua_request")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " diff --git a/package/uhttpd/src/uhttpd.h b/package/uhttpd/src/uhttpd.h index 0e9f1ee886..32e3970072 100644 --- a/package/uhttpd/src/uhttpd.h +++ b/package/uhttpd/src/uhttpd.h @@ -32,7 +32,7 @@ #include #include #include - +#include #include @@ -64,6 +64,8 @@ struct config { char docroot[PATH_MAX]; char *realm; char *file; + int no_symlinks; + int network_timeout; #ifdef HAVE_CGI char *cgi_prefix; #endif