Browse Source

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
Jo-Philipp Wich 16 years ago
parent
commit
20ef055c2f

+ 1 - 1
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)
 

+ 7 - 1
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
 

+ 1 - 0
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

+ 9 - 11
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";

+ 50 - 47
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 }
 };

+ 78 - 4
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));

+ 37 - 17
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 "

+ 3 - 1
package/uhttpd/src/uhttpd.h

@@ -32,7 +32,7 @@
 #include <linux/limits.h>
 #include <netdb.h>
 #include <ctype.h>
-
+#include <errno.h>
 #include <dlfcn.h>
 
 
@@ -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