X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/e417063c23716f0dd35f98f1990bb8dad7b963f4..d08229790289e2bbf1e285275dd1bf3189cad078:/package/uhttpd/src/uhttpd.c

diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c
index 82729627e..50c3b32a9 100644
--- a/package/uhttpd/src/uhttpd.c
+++ b/package/uhttpd/src/uhttpd.c
@@ -51,8 +51,8 @@ static void uh_config_parse(struct config *conf)
 {
 	FILE *c;
 	char line[512];
-	char *user = NULL;
-	char *pass = NULL;
+	char *col1 = NULL;
+	char *col2 = NULL;
 	char *eol  = NULL;
 
 	const char *path = conf->file ? conf->file : "/etc/httpd.conf";
@@ -66,35 +66,52 @@ static void uh_config_parse(struct config *conf)
 		{
 			if( (line[0] == '/') && (strchr(line, ':') != NULL) )
 			{
-				if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-				    !(pass = strchr(user, ':')) || (*pass++ = 0) ||
-					!(eol = strchr(pass, '\n')) || (*eol++  = 0) )
+				if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+				    !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+					!(eol = strchr(col2, '\n')) || (*eol++  = 0) )
 						continue;
 
-				if( !uh_auth_add(line, user, pass) )
+				if( !uh_auth_add(line, col1, col2) )
 				{
 					fprintf(stderr,
 						"Notice: No password set for user %s, ignoring "
-						"authentication on %s\n", user, line
+						"authentication on %s\n", col1, line
 					);
 				}
 			}
 			else if( !strncmp(line, "I:", 2) )
 			{
-				if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-				    !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+				if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+				    !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
 				    	continue;
 
-				conf->index_file = strdup(user);
+				conf->index_file = strdup(col1);
 			}
 			else if( !strncmp(line, "E404:", 5) )
 			{
-				if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-				    !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+				if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+				    !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
 						continue;
 
-				conf->error_handler = strdup(user);
+				conf->error_handler = strdup(col1);
 			}
+#ifdef HAVE_CGI
+			else if( (line[0] == '*') && (strchr(line, ':') != NULL) )
+			{
+				if( !(col1 = strchr(line, '*')) || (*col1++ = 0) ||
+				    !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+				    !(eol = strchr(col2, '\n')) || (*eol++  = 0) )
+						continue;
+
+				if( !uh_interpreter_add(col1, col2) )
+				{
+					fprintf(stderr,
+						"Unable to add interpreter %s for extension %s: "
+						"Out of memory\n", col2, col1
+					);
+				}
+			}
+#endif
 		}
 
 		fclose(c);
@@ -110,6 +127,8 @@ static int uh_socket_bind(
 	int status;
 	int bound = 0;
 
+	int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt;
+
 	struct listener *l = NULL;
 	struct addrinfo *addrs = NULL, *p = NULL;
 
@@ -129,12 +148,29 @@ static int uh_socket_bind(
 		}
 
 		/* "address already in use" */
-		if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1 )
+		if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) )
 		{
 			perror("setsockopt()");
 			goto error;
 		}
 
+		/* TCP keep-alive */
+		if( conf->tcp_keepalive > 0 )
+		{
+			tcp_ka_idl = 1;
+			tcp_ka_cnt = 3;
+			tcp_ka_int = conf->tcp_keepalive;
+
+			if( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) ||
+			    setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,  &tcp_ka_idl, sizeof(tcp_ka_idl)) ||
+			    setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) ||
+			    setsockopt(sock, SOL_TCP, TCP_KEEPCNT,   &tcp_ka_cnt, sizeof(tcp_ka_cnt)) )
+			{
+			    fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n",
+			    	strerror(errno));
+			}
+		}
+
 		/* required to get parallel v4 + v6 working */
 		if( p->ai_family == AF_INET6 )
 		{
@@ -162,11 +198,7 @@ static int uh_socket_bind(
 		/* add listener to global list */
 		if( ! (l = uh_listener_add(sock, conf)) )
 		{
-			fprintf(stderr,
-				"uh_listener_add(): Can not create more than "
-				"%i listen sockets\n", UH_LIMIT_LISTENERS
-			);
-
+			fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
 			goto error;
 		}
 
@@ -266,7 +298,8 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe
 		}
 
 		/* check version */
-		if( strcmp(version, "HTTP/0.9") && strcmp(version, "HTTP/1.0") && strcmp(version, "HTTP/1.1") )
+		if( (version == NULL) || (strcmp(version, "HTTP/0.9") &&
+		    strcmp(version, "HTTP/1.0") && strcmp(version, "HTTP/1.1")) )
 		{
 			/* unsupported version */
 			uh_http_response(cl, 400, "Bad Request");
@@ -311,8 +344,8 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe
 				hdrdata = &buffer[i+2];
 			}
 
-			/* have no name and found [A-Z], start of name */
-			else if( !hdrname && isalpha(buffer[i]) && isupper(buffer[i]) )
+			/* have no name and found [A-Za-z], start of name */
+			else if( !hdrname && isalpha(buffer[i]) )
 			{
 				hdrname = &buffer[i];
 			}
@@ -342,7 +375,6 @@ static struct http_request * uh_http_header_recv(struct client *cl)
 	ssize_t blen = sizeof(buffer)-1;
 	ssize_t rlen = 0;
 
-
 	memset(buffer, 0, sizeof(buffer));
 
 	while( blen > 0 )
@@ -358,44 +390,45 @@ static struct http_request * uh_http_header_recv(struct client *cl)
 		if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
 		{
 			/* receive data */
-			rlen = uh_tcp_peek(cl, bufptr, blen);
+			ensure_out(rlen = uh_tcp_peek(cl, bufptr, blen));
 
-			if( rlen > 0 )
+			if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
 			{
-				if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
-				{
-					blen -= uh_tcp_recv(cl, bufptr, (int)(idxptr - bufptr) + 4);
+				ensure_out(rlen = uh_tcp_recv(cl, bufptr,
+					(int)(idxptr - bufptr) + 4));
 
-					/* header read complete ... */
-					return uh_http_header_parse(cl, buffer, sizeof(buffer) - blen - 1);
-				}
-				else
-				{
-					rlen = uh_tcp_recv(cl, bufptr, rlen);
-					blen -= rlen;
-					bufptr += rlen;
-				}
+				/* header read complete ... */
+				blen -= rlen;
+				return uh_http_header_parse(cl, buffer,
+					sizeof(buffer) - blen - 1);
 			}
 			else
 			{
-				/* invalid request (unexpected eof/timeout) */
-				uh_http_response(cl, 408, "Request Timeout");
-				return NULL;
+				ensure_out(rlen = uh_tcp_recv(cl, bufptr, rlen));
+
+				/* unexpected eof - #7904 */
+				if( rlen == 0 )
+					return NULL;
+
+				blen -= rlen;
+				bufptr += rlen;
 			}
 		}
 		else
 		{
 			/* invalid request (unexpected eof/timeout) */
-			uh_http_response(cl, 408, "Request Timeout");
 			return NULL;
 		}
 	}
 
 	/* request entity too large */
 	uh_http_response(cl, 413, "Request Entity Too Large");
+
+out:
 	return NULL;
 }
 
+#if defined(HAVE_LUA) || defined(HAVE_CGI)
 static int uh_path_match(const char *prefix, const char *url)
 {
 	if( (strstr(url, prefix) == url) &&
@@ -408,23 +441,243 @@ static int uh_path_match(const char *prefix, const char *url)
 
 	return 0;
 }
+#endif
 
+static void uh_dispatch_request(
+	struct client *cl, struct http_request *req, struct path_info *pin
+) {
+#ifdef HAVE_CGI
+	struct interpreter *ipr = NULL;
 
-int main (int argc, char **argv)
-{
-#ifdef HAVE_LUA
-	/* Lua runtime */
-	lua_State *L = NULL;
+	if( uh_path_match(cl->server->conf->cgi_prefix, pin->name) ||
+		(ipr = uh_interpreter_lookup(pin->phys)) )
+	{
+		uh_cgi_request(cl, req, pin, ipr);
+	}
+	else
 #endif
+	{
+		uh_file_request(cl, req, pin);
+	}
+}
 
+static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
+{
 	/* master file descriptor list */
-	fd_set used_fds, serv_fds, read_fds;
+	fd_set used_fds, read_fds;
 
 	/* working structs */
-	struct addrinfo hints;
 	struct http_request *req;
 	struct path_info *pin;
 	struct client *cl;
+
+	/* maximum file descriptor number */
+	int new_fd, cur_fd = 0;
+
+	/* clear the master and temp sets */
+	FD_ZERO(&used_fds);
+	FD_ZERO(&read_fds);
+
+	/* backup server descriptor set */
+	used_fds = serv_fds;
+
+	/* loop */
+	while(run)
+	{
+		/* create a working copy of the used fd set */
+		read_fds = used_fds;
+
+		/* sleep until socket activity */
+		if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
+		{
+			perror("select()");
+			exit(1);
+		}
+
+		/* run through the existing connections looking for data to be read */
+		for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
+		{
+			/* is a socket managed by us */
+			if( FD_ISSET(cur_fd, &read_fds) )
+			{
+				/* is one of our listen sockets */
+				if( FD_ISSET(cur_fd, &serv_fds) )
+				{
+					/* handle new connections */
+					if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
+					{
+						/* add to global client list */
+						if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
+						{
+#ifdef HAVE_TLS
+							/* setup client tls context */
+							if( conf->tls )
+								conf->tls_accept(cl);
+#endif
+
+							/* add client socket to global fdset */
+							FD_SET(new_fd, &used_fds);
+							fd_cloexec(new_fd);
+							max_fd = max(max_fd, new_fd);
+						}
+
+						/* insufficient resources */
+						else
+						{
+							fprintf(stderr,
+								"uh_client_add(): Cannot allocate memory\n");
+
+							close(new_fd);
+						}
+					}
+				}
+
+				/* is a client socket */
+				else
+				{
+					if( ! (cl = uh_client_lookup(cur_fd)) )
+					{
+						/* this should not happen! */
+						fprintf(stderr,
+							"uh_client_lookup(): No entry for fd %i!\n",
+							cur_fd);
+
+						goto cleanup;
+					}
+
+					/* parse message header */
+					if( (req = uh_http_header_recv(cl)) != NULL )
+					{
+						/* RFC1918 filtering required? */
+						if( conf->rfc1918_filter &&
+						    sa_rfc1918(&cl->peeraddr) &&
+						    !sa_rfc1918(&cl->servaddr) )
+						{
+							uh_http_sendhf(cl, 403, "Forbidden",
+								"Rejected request from RFC1918 IP "
+								"to public server address");
+						}
+						else
+#ifdef HAVE_LUA
+						/* Lua request? */
+						if( conf->lua_state &&
+						    uh_path_match(conf->lua_prefix, req->url) )
+						{
+							conf->lua_request(cl, req, conf->lua_state);
+						}
+						else
+#endif
+						/* dispatch request */
+						if( (pin = uh_path_lookup(cl, req->url)) != NULL )
+						{
+							/* auth ok? */
+							if( !pin->redirected && uh_auth_check(cl, req, pin) )
+								uh_dispatch_request(cl, req, pin);
+						}
+
+						/* 404 */
+						else
+						{
+							/* Try to invoke an error handler */
+							pin = uh_path_lookup(cl, conf->error_handler);
+
+							if( pin && uh_auth_check(cl, req, pin) )
+							{
+								req->redirect_status = 404;
+								uh_dispatch_request(cl, req, pin);
+							}
+							else
+							{
+								uh_http_sendhf(cl, 404, "Not Found",
+									"No such file or directory");
+							}
+						}
+					}
+
+#ifdef HAVE_TLS
+					/* free client tls context */
+					if( conf->tls )
+						conf->tls_close(cl);
+#endif
+
+					cleanup:
+
+					/* close client socket */
+					close(cur_fd);
+					FD_CLR(cur_fd, &used_fds);
+
+					/* remove from global client list */
+					uh_client_remove(cur_fd);
+				}
+			}
+		}
+	}
+
+#ifdef HAVE_LUA
+	/* destroy the Lua state */
+	if( conf->lua_state != NULL )
+		conf->lua_close(conf->lua_state);
+#endif
+}
+
+#ifdef HAVE_TLS
+static inline uh_inittls(struct config *conf)
+{
+	/* library handle */
+	void *lib;
+
+	/* already loaded */
+	if( conf->tls != NULL )
+		return 0;
+
+	/* load TLS plugin */
+	if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
+	{
+		fprintf(stderr,
+			"Notice: Unable to load TLS plugin - disabling SSL support! "
+			"(Reason: %s)\n", dlerror()
+		);
+
+		return 1;
+	}
+	else
+	{
+		/* resolve functions */
+		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 "
+				"in TLS plugin: %s\n", dlerror()
+			);
+			exit(1);
+		}
+
+		/* init SSL context */
+		if( ! (conf->tls = conf->tls_init()) )
+		{
+			fprintf(stderr, "Error: Failed to initalize SSL context\n");
+			exit(1);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+int main (int argc, char **argv)
+{
+	/* master file descriptor list */
+	fd_set serv_fds;
+
+	/* working structs */
+	struct addrinfo hints;
 	struct sigaction sa;
 	struct config conf;
 
@@ -432,7 +685,7 @@ int main (int argc, char **argv)
 	sigset_t ss;
 
 	/* maximum file descriptor number */
-	int new_fd, cur_fd, max_fd = 0;
+	int cur_fd, max_fd = 0;
 
 #ifdef HAVE_TLS
 	int tls = 0;
@@ -447,15 +700,12 @@ int main (int argc, char **argv)
 	char bind[128];
 	char *port = NULL;
 
-#if defined(HAVE_TLS) || defined(HAVE_LUA)
+#ifdef HAVE_LUA
 	/* library handle */
 	void *lib;
 #endif
 
-	/* clear the master and temp sets */
-	FD_ZERO(&used_fds);
 	FD_ZERO(&serv_fds);
-	FD_ZERO(&read_fds);
 
 	/* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */
 	sa.sa_flags = 0;
@@ -486,45 +736,9 @@ int main (int argc, char **argv)
 	memset(&conf, 0, sizeof(conf));
 	memset(bind, 0, sizeof(bind));
 
-#ifdef HAVE_TLS
-	/* load TLS plugin */
-	if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
-	{
-		fprintf(stderr,
-			"Notice: Unable to load TLS plugin - disabling SSL support! "
-			"(Reason: %s)\n", dlerror()
-		);
-	}
-	else
-	{
-		/* resolve functions */
-		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 "
-				"in TLS plugin: %s\n", dlerror()
-			);
-			exit(1);
-		}
-
-		/* init SSL context */
-		if( ! (conf.tls = conf.tls_init()) )
-		{
-			fprintf(stderr, "Error: Failed to initalize SSL context\n");
-			exit(1);
-		}
-	}
-#endif
 
 	while( (opt = getopt(argc, argv,
-		"fSDC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+		"fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:A:")) > 0
 	) {
 		switch(opt)
 		{
@@ -550,7 +764,7 @@ int main (int argc, char **argv)
 #ifdef HAVE_TLS
 				if( opt == 's' )
 				{
-					if( !conf.tls )
+					if( uh_inittls(&conf) )
 					{
 						fprintf(stderr,
 							"Notice: TLS support is disabled, "
@@ -575,7 +789,7 @@ int main (int argc, char **argv)
 #ifdef HAVE_TLS
 			/* certificate */
 			case 'C':
-				if( conf.tls )
+				if( !uh_inittls(&conf) )
 				{
 					if( conf.tls_cert(conf.tls, optarg) < 1 )
 					{
@@ -591,7 +805,7 @@ int main (int argc, char **argv)
 
 			/* key */
 			case 'K':
-				if( conf.tls )
+				if( !uh_inittls(&conf) )
 				{
 					if( conf.tls_key(conf.tls, optarg) < 1 )
 					{
@@ -648,11 +862,30 @@ int main (int argc, char **argv)
 				conf.no_dirlists = 1;
 				break;
 
+			case 'R':
+				conf.rfc1918_filter = 1;
+				break;
+
 #ifdef HAVE_CGI
 			/* cgi prefix */
 			case 'x':
 				conf.cgi_prefix = optarg;
 				break;
+
+			/* interpreter */
+			case 'i':
+				if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
+				{
+					*port++ = 0;
+					uh_interpreter_add(optarg, port);
+				}
+				else
+				{
+					fprintf(stderr, "Error: Invalid interpreter: %s\n",
+						optarg);
+					exit(1);
+				}
+				break;
 #endif
 
 #ifdef HAVE_LUA
@@ -679,6 +912,11 @@ int main (int argc, char **argv)
 				conf.network_timeout = atoi(optarg);
 				break;
 
+			/* tcp keep-alive */
+			case 'A':
+				conf.tcp_keepalive = atoi(optarg);
+				break;
+
 			/* no fork */
 			case 'f':
 				nofork = 1;
@@ -688,8 +926,14 @@ int main (int argc, char **argv)
 			case 'd':
 				if( (port = malloc(strlen(optarg)+1)) != NULL )
 				{
+					/* "decode" plus to space to retain compat */
+					for (opt = 0; optarg[opt]; opt++)
+						if (optarg[opt] == '+')
+							optarg[opt] = ' ';
+
 					memset(port, 0, strlen(optarg)+1);
 					uh_urldecode(port, strlen(optarg), optarg, strlen(optarg));
+
 					printf("%s", port);
 					free(port);
 					exit(0);
@@ -728,12 +972,14 @@ int main (int argc, char **argv)
 					"	-I string       Use given filename as index page for directories\n"
 					"	-S              Do not follow symbolic links outside of the docroot\n"
 					"	-D              Do not allow directory listings, send 403 instead\n"
+					"	-R              Enable RFC1918 filter\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"
 #endif
 #ifdef HAVE_CGI
 					"	-x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
+					"	-i .ext=path    Use interpreter at path for files with the given extension\n"
 #endif
 #if defined(HAVE_CGI) || defined(HAVE_LUA)
 					"	-t seconds      CGI and Lua script timeout in seconds, default is 60\n"
@@ -824,7 +1070,7 @@ int main (int argc, char **argv)
 			if( ! conf.lua_prefix )
 				conf.lua_prefix = "/lua";
 
-			L = conf.lua_init(conf.lua_handler);
+			conf.lua_state = conf.lua_init(conf.lua_handler);
 		}
 	}
 #endif
@@ -859,167 +1105,14 @@ int main (int argc, char **argv)
 		}
 	}
 
-	/* backup server descriptor set */
-	used_fds = serv_fds;
-
-	/* loop */
-	while(run)
-	{
-		/* create a working copy of the used fd set */
-		read_fds = used_fds;
-
-		/* sleep until socket activity */
-		if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
-		{
-			perror("select()");
-			exit(1);
-		}
-
-		/* run through the existing connections looking for data to be read */
-		for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
-		{
-			/* is a socket managed by us */
-			if( FD_ISSET(cur_fd, &read_fds) )
-			{
-				/* is one of our listen sockets */
-				if( FD_ISSET(cur_fd, &serv_fds) )
-				{
-					/* handle new connections */
-					if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
-					{
-						/* add to global client list */
-						if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
-						{
-#ifdef HAVE_TLS
-							/* setup client tls context */
-							if( conf.tls )
-								conf.tls_accept(cl);
-#endif
-
-							/* add client socket to global fdset */
-							FD_SET(new_fd, &used_fds);
-							fd_cloexec(new_fd);
-							max_fd = max(max_fd, new_fd);
-						}
-
-						/* insufficient resources */
-						else
-						{
-							fprintf(stderr,
-								"uh_client_add(): Can not manage more than "
-								"%i client sockets, connection dropped\n",
-								UH_LIMIT_CLIENTS
-							);
-
-							close(new_fd);
-						}
-					}
-				}
-
-				/* is a client socket */
-				else
-				{
-					if( ! (cl = uh_client_lookup(cur_fd)) )
-					{
-						/* this should not happen! */
-						fprintf(stderr,
-							"uh_client_lookup(): No entry for fd %i!\n",
-							cur_fd);
-
-						goto cleanup;
-					}
-
-					/* parse message header */
-					if( (req = uh_http_header_recv(cl)) != NULL )
-					{
-#ifdef HAVE_LUA
-						/* Lua request? */
-						if( L && uh_path_match(conf.lua_prefix, req->url) )
-						{
-							conf.lua_request(cl, req, L);
-						}
-						else
-#endif
-						/* dispatch request */
-						if( (pin = uh_path_lookup(cl, req->url)) != NULL )
-						{
-							/* auth ok? */
-							if( uh_auth_check(cl, req, pin) )
-							{
-#ifdef HAVE_CGI
-								if( uh_path_match(conf.cgi_prefix, pin->name) )
-								{
-									uh_cgi_request(cl, req, pin);
-								}
-								else
-#endif
-								{
-									uh_file_request(cl, req, pin);
-								}
-							}
-						}
-
-						/* 404 */
-						else
-						{
-							/* Try to invoke an error handler */
-							pin = uh_path_lookup(cl, conf.error_handler);
-
-							if( pin && uh_auth_check(cl, req, pin) )
-							{
-								req->redirect_status = 404;
-
-#ifdef HAVE_CGI
-								if( uh_path_match(conf.cgi_prefix, pin->name) )
-								{
-									uh_cgi_request(cl, req, pin);
-								}
-								else
-#endif
-								{
-									uh_file_request(cl, req, pin);
-								}
-							}
-							else
-							{
-								uh_http_sendhf(cl, 404, "Not Found",
-									"No such file or directory");
-							}
-						}
-					}
-
-					/* 400 */
-					else
-					{
-						uh_http_sendhf(cl, 400, "Bad Request",
-							"Malformed request received");
-					}
-
-#ifdef HAVE_TLS
-					/* free client tls context */
-					if( conf.tls )
-						conf.tls_close(cl);
-#endif
-
-					cleanup:
-
-					/* close client socket */
-					close(cur_fd);
-					FD_CLR(cur_fd, &used_fds);
-
-					/* remove from global client list */
-					uh_client_remove(cur_fd);
-				}
-			}
-		}
-	}
+	/* server main loop */
+	uh_mainloop(&conf, serv_fds, max_fd);
 
 #ifdef HAVE_LUA
 	/* destroy the Lua state */
-	if( L != NULL )
-		conf.lua_close(L);
+	if( conf.lua_state != NULL )
+		conf.lua_close(conf.lua_state);
 #endif
 
 	return 0;
 }
-