From: Jo-Philipp Wich <jow@openwrt.org>
Date: Sun, 9 Jan 2011 23:35:45 +0000 (+0000)
Subject: uhttpd: protect tcp receive operations with select, make tcp keep-alive optional... 
X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=f38a320e693d951e6bcd0fb2ee4e81855d48a21c;p=openwrt%2Fstaging%2Fjow.git

uhttpd: protect tcp receive operations with select, make tcp keep-alive optional (#8272)

SVN-Revision: 24952
---

diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile
index a0b7d24ebf..bba7ac53ad 100644
--- a/package/uhttpd/Makefile
+++ b/package/uhttpd/Makefile
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=20
+PKG_RELEASE:=21
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_BUILD_DEPENDS := libcyassl liblua
diff --git a/package/uhttpd/files/uhttpd.config b/package/uhttpd/files/uhttpd.config
index a29910a65f..08ca5e5e02 100644
--- a/package/uhttpd/files/uhttpd.config
+++ b/package/uhttpd/files/uhttpd.config
@@ -51,6 +51,13 @@ config uhttpd main
 	# request process.
 	option network_timeout	30
 
+	# TCP Keep-Alive, send periodic keep-alive probes
+	# over established connections to detect dead peers.
+	# The value is given in seconds to specify the
+	# interval between subsequent probes.
+	# Setting this to 0 will disable TCP keep-alive.
+	option tcp_keepalive	1
+
 	# 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 f8f1754e90..069e16fff1 100755
--- a/package/uhttpd/files/uhttpd.init
+++ b/package/uhttpd/files/uhttpd.init
@@ -66,6 +66,7 @@ start_instance()
 	append_arg "$cfg" lua_handler "-L"
 	append_arg "$cfg" script_timeout "-t"
 	append_arg "$cfg" network_timeout "-T"
+	append_arg "$cfg" tcp_keepalive "-A"
 	append_arg "$cfg" error_page "-E"
 	append_arg "$cfg" index_page "-I"
 
diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c
index c7bc867aab..ac00af824e 100644
--- a/package/uhttpd/src/uhttpd-utils.c
+++ b/package/uhttpd/src/uhttpd-utils.c
@@ -167,6 +167,9 @@ int uh_tcp_recv(struct client *cl, char *buf, int len)
 	int sz = 0;
 	int rsz = 0;
 
+	fd_set reader;
+	struct timeval timeout;
+
 	/* first serve data from peek buffer */
 	if( cl->peeklen > 0 )
 	{
@@ -180,15 +183,28 @@ int uh_tcp_recv(struct client *cl, char *buf, int len)
 	/* caller wants more */
 	if( len > 0 )
 	{
+		FD_ZERO(&reader);
+		FD_SET(cl->socket, &reader);
+
+		timeout.tv_sec  = cl->server->conf->network_timeout;
+		timeout.tv_usec = 0;
+
+		if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
+		{
 #ifdef HAVE_TLS
-		if( cl->tls )
-			rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len);
-		else
+			if( cl->tls )
+				rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len);
+			else
 #endif
-			rsz = recv(cl->socket, (void *)&buf[sz], len, 0);
+				rsz = recv(cl->socket, (void *)&buf[sz], len, 0);
 
-		if( (sz == 0) || (rsz > 0) )
-			sz += rsz;
+			if( (sz == 0) || (rsz > 0) )
+				sz += rsz;
+		}
+		else if( sz == 0 )
+		{
+			sz = -1;
+		}
 	}
 
 	return sz;
@@ -233,7 +249,7 @@ int uh_http_sendc(struct client *cl, const char *data, int len)
 
 	if( len > 0 )
 	{
-	 	clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
+		clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
 		ensure_ret(uh_tcp_send(cl, chunk, clen));
 		ensure_ret(uh_tcp_send(cl, data, len));
 		ensure_ret(uh_tcp_send(cl, "\r\n", 2));
diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c
index 2c7755f8da..1fd21344fe 100644
--- a/package/uhttpd/src/uhttpd.c
+++ b/package/uhttpd/src/uhttpd.c
@@ -127,9 +127,7 @@ static int uh_socket_bind(
 	int status;
 	int bound = 0;
 
-	int tcp_ka_idl = 1;
-	int tcp_ka_int = 1;
-	int tcp_ka_cnt = 3;
+	int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt;
 
 	struct listener *l = NULL;
 	struct addrinfo *addrs = NULL, *p = NULL;
@@ -157,13 +155,20 @@ static int uh_socket_bind(
 		}
 
 		/* TCP keep-alive */
-		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)) )
+		if( conf->tcp_keepalive > 0 )
 		{
-		    fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n",
-		    	strerror(errno));
+			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 */
@@ -619,7 +624,7 @@ static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
 int main (int argc, char **argv)
 {
 	/* master file descriptor list */
-	fd_set used_fds, serv_fds, read_fds;
+	fd_set serv_fds;
 
 	/* working structs */
 	struct addrinfo hints;
@@ -650,10 +655,7 @@ int main (int argc, char **argv)
 	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;
@@ -722,7 +724,7 @@ int main (int argc, char **argv)
 #endif
 
 	while( (opt = getopt(argc, argv,
-		"fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:")) > 0
+		"fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:A:")) > 0
 	) {
 		switch(opt)
 		{
@@ -896,6 +898,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;
diff --git a/package/uhttpd/src/uhttpd.h b/package/uhttpd/src/uhttpd.h
index 6747b905ff..ff058d62bf 100644
--- a/package/uhttpd/src/uhttpd.h
+++ b/package/uhttpd/src/uhttpd.h
@@ -75,6 +75,7 @@ struct config {
 	int no_dirlists;
 	int network_timeout;
 	int rfc1918_filter;
+	int tcp_keepalive;
 #ifdef HAVE_CGI
 	char *cgi_prefix;
 #endif