Idletimeout patch, 4th attempt

Jani Jaakkola jjaakkol at cs.Helsinki.FI
Tue Aug 21 17:12:35 EST 2001


On Tue, 21 Aug 2001, Jani Jaakkola wrote:

>
> Here is my third attempt at the idletimeout patch. I tried to address
> the points which Marcus Friedl brought up.

After having a good night sleep I finally realized that ssh1 and ssh2
are actually different protocols and therefore may and do have
overlapping message numbers. This needs to be addressed at
idletimeout_check (which Markus was telling me, but I was too boneheaded
to believe him).

The 4th version of the attached patch is also available from:
http://www.cs.helsinki.fi/u/jjaakkol/idletimeout.patch

- Jani
-------------- next part --------------
diff -ru openssh-2.9p2.orig/CREDITS openssh-2.9p2/CREDITS
--- openssh-2.9p2.orig/CREDITS	Mon Apr 16 03:41:46 2001
+++ openssh-2.9p2/CREDITS	Wed Aug 15 22:00:02 2001
@@ -42,6 +42,7 @@
 IWAMURO Motonori <iwa at mmp.fujitsu.co.jp> - bugfixes
 Jani Hakala <jahakala at cc.jyu.fi> - Patches
 Jarno Huuskonen <jhuuskon at hytti.uku.fi> - Bugfixes
+Jani Jaakkola <jjaakkol at cs.helsinki.fi> - IdleTimeOut
 Jim Knoble <jmknoble at jmknoble.cx> - Many patches
 Jonchen (email unknown) - the original author of PAM support of SSH
 Juergen Keil <jk at tools.de> - scp bugfixing
diff -ru openssh-2.9p2.orig/clientloop.c openssh-2.9p2/clientloop.c
--- openssh-2.9p2.orig/clientloop.c	Fri Apr 20 15:50:51 2001
+++ openssh-2.9p2/clientloop.c	Mon Aug 20 22:20:38 2001
@@ -1250,3 +1250,4 @@
 	else
 		client_init_dispatch_15();
 }
+
diff -ru openssh-2.9p2.orig/packet.c openssh-2.9p2/packet.c
--- openssh-2.9p2.orig/packet.c	Fri Apr  6 02:26:33 2001
+++ openssh-2.9p2/packet.c	Tue Aug 21 09:46:40 2001
@@ -121,10 +121,97 @@
 /* True if SSH2 packet format is used */
 int use_ssh2_packet_format = 0;
 
+static time_t idletime_last=0; /* The last time something happened
+				* for idletimeout. */
+static int idletimeout=0;      /* The current idletimeout */
+
 /* Session key information for Encryption and MAC */
 Newkeys *newkeys[MODE_MAX];
 
 void
+packet_set_idletimeout(int max_idle_seconds) 
+{
+        idletimeout=max_idle_seconds;
+        if (max_idle_seconds>0) {
+	        /* Initialize */
+	        time(&idletime_last);
+        }
+}
+
+/* Called by whenever packets are sent or received.
+ * This function decides on which packets idletimeout should
+ * be reset */
+void 
+idletimeout_check(int type) 
+{
+        /* No-op, if idletimeouts are not configured */
+        if (idletimeout==0) return;
+
+	/* The following packets reset idletimeout on input or output.
+	 * Note that only actual data resets idletimeout, control packets 
+	 * do not. */
+	if (compat20) {
+	        switch(type) {
+		case SSH2_MSG_CHANNEL_DATA:
+		case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+		        time(&idletime_last);
+		}
+	} else {
+		switch(type) {
+		case SSH_MSG_CHANNEL_DATA:
+		case SSH_CMSG_STDIN_DATA:
+		case SSH_SMSG_STDOUT_DATA:
+		case SSH_SMSG_STDERR_DATA:	
+		        time(&idletime_last);
+		} 
+	}
+}
+
+int     
+packet_select(int maxfds,
+	      fd_set *readset, fd_set *writeset, fd_set *exceptset,
+	      int max_time_milliseconds)
+{
+        struct timeval tv, *tvp=NULL;
+	int ret;
+
+	if (idletimeout>0) {
+	        /* Count the time to idletimeout */
+	        time_t diff=time(NULL)-idletime_last;
+		if (diff>=idletimeout)
+		        tv.tv_sec=1;
+		else
+    		        tv.tv_sec=idletimeout-diff+1;
+		tv.tv_usec=0;
+		tvp = &tv;
+		debug("idletimeout after %ld seconds",tv.tv_sec);
+	}
+	/* If a timeout value was given and the timeout happens before
+	 * idletimeout, use it */
+	if (max_time_milliseconds>0 &&
+	    (tvp==NULL || max_time_milliseconds/1000<tv.tv_sec)) {
+	        tv.tv_sec = max_time_milliseconds / 1000;
+		tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
+		tvp = &tv;
+	        debug("timeout after %d milliseconds",max_time_milliseconds);
+	}
+	if (tvp==NULL) debug("no timeout");
+
+	ret = select( maxfds, readset, writeset, exceptset, tvp );
+	if (ret<0 && errno!=EAGAIN && errno!=EINTR) {
+	        fatal("select: %.100s", strerror(errno));
+	}
+
+	/* Disconnect on idletimeout */
+	if (idletimeout>0 && ret==0 && 
+	    time(NULL)-idletime_last>idletimeout) {
+	        packet_disconnect("Idletimeout.");
+		idletimeout=0;
+	}
+	return ret;
+}
+
+void
 packet_set_ssh2_format(void)
 {
 	DBG(debug("use_ssh2_packet_format"));
@@ -383,6 +470,7 @@
 		packet_start2(type);
 	else
 		packet_start1(type);
+        idletimeout_check(type);
 }
 
 /* Appends a character to the packet data. */
@@ -708,20 +796,21 @@
 		/* If we got a packet, return it. */
 		if (type != SSH_MSG_NONE) {
 			xfree(setp);
+			idletimeout_check(type);
 			return type;
 		}
 		/*
 		 * Otherwise, wait for some data to arrive, add it to the
 		 * buffer, and try again.
 		 */
-		memset(setp, 0, howmany(connection_in + 1, NFDBITS) *
-		    sizeof(fd_mask));
-		FD_SET(connection_in, setp);
-
-		/* Wait for some data to arrive. */
-		while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 &&
-		    (errno == EAGAIN || errno == EINTR))
-			;
+		do {
+   		        memset(setp, 0, howmany(connection_in + 1, NFDBITS) *
+			       sizeof(fd_mask));
+		        FD_SET(connection_in, setp);
+			
+			/* Wait for some data to arrive. */
+		} while (packet_select(connection_in + 1,
+				       setp, NULL, NULL, 0) == -1);
 
 		/* Read data from the socket. */
 		len = read(connection_in, buf, sizeof(buf));
@@ -974,6 +1063,7 @@
 		    packet_read_poll2(payload_len_ptr):
 		    packet_read_poll1(payload_len_ptr);
 
+		idletimeout_check(type);
 		if(compat20) {
 			int reason;
 			if (type != 0)
@@ -1217,12 +1307,12 @@
 	    sizeof(fd_mask));
 	packet_write_poll();
 	while (packet_have_data_to_write()) {
-		memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
-		    sizeof(fd_mask));
-		FD_SET(connection_out, setp);
-		while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 &&
-		    (errno == EAGAIN || errno == EINTR))
-			;
+	        do {
+		        memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
+			       sizeof(fd_mask));
+			FD_SET(connection_out, setp);
+		} while (packet_select(connection_out + 1, 
+				       NULL, setp, NULL, 0) == -1);
 		packet_write_poll();
 	}
 	xfree(setp);
diff -ru openssh-2.9p2.orig/packet.h openssh-2.9p2/packet.h
--- openssh-2.9p2.orig/packet.h	Sun Apr 15 02:13:03 2001
+++ openssh-2.9p2/packet.h	Mon Aug 20 23:36:10 2001
@@ -220,4 +220,20 @@
 /* add an ignore message and make sure size (current+ignore) = n*sumlen */
 void	packet_inject_ignore(int sumlen);
 
+/* This sets the maximum idle time before packet_select() automatically
+ * disconnects with packet_disconnect("Idletimeout"). 
+ * Never autodisconnects if set to zero. zero is the default */
+void    packet_set_idletimeout(int max_idle_seconds);
+
+/* This is an quite normal select, except that it implements idletimeouts
+ * set with packet_set_idletimeout().
+ * It also returns exits, if select() returns any other error than AGAIN
+ * or EINTR. So if packet_select returns -1, you can safely reinit fd_sets
+ * and call packet_select again, without checking errno.
+ */
+int     packet_select(int maxfds,
+		      fd_set *readset, fd_set *writeset, fd_set *exceptset,
+		      int max_time_milliseconds);
+
+
 #endif				/* PACKET_H */
diff -ru openssh-2.9p2.orig/servconf.c openssh-2.9p2/servconf.c
--- openssh-2.9p2.orig/servconf.c	Wed Apr 25 15:44:15 2001
+++ openssh-2.9p2/servconf.c	Wed Aug 15 22:10:23 2001
@@ -102,6 +102,7 @@
 	options->client_alive_interval = -1;
 	options->client_alive_count_max = -1;
 	options->pam_authentication_via_kbd_int = -1;
+	options->idletimeout = -1;
 }
 
 void
@@ -210,6 +211,8 @@
 		options->client_alive_count_max = 3;
 	if (options->pam_authentication_via_kbd_int == -1)
 		options->pam_authentication_via_kbd_int = 0;
+	if (options->idletimeout == -1)
+		options->idletimeout=0;
 }
 
 /* Keyword tokens. */
@@ -235,7 +238,8 @@
 	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
 	sBanner, sReverseMappingCheck, sHostbasedAuthentication,
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 
-	sClientAliveCountMax, sPAMAuthenticationViaKbdInt
+	sClientAliveCountMax, sPAMAuthenticationViaKbdInt,
+	sIdleTimeout
 } ServerOpCodes;
 
 /* Textual representation of the tokens. */
@@ -302,6 +306,7 @@
 	{ "clientaliveinterval", sClientAliveInterval },
 	{ "clientalivecountmax", sClientAliveCountMax },
 	{ "PAMAuthenticationViaKbdInt", sPAMAuthenticationViaKbdInt },
+	{ "idletimeout", sIdleTimeout },
 	{ NULL, 0 }
 };
 
@@ -801,7 +806,28 @@
 		case sPAMAuthenticationViaKbdInt:
 			intptr = &options->pam_authentication_via_kbd_int;
 			goto parse_flag;
-
+               case sIdleTimeout:
+			arg = strdelim(&cp);
+			if (!arg || *arg == '\0')
+				fatal("%s line %d: Missing IdleTimeout argument",
+					filename,linenum);
+			options->idletimeout=atoi(arg);
+			switch(arg[strlen(arg)-1]) {
+				case 'w': options->idletimeout*=7;
+				case 'd': options->idletimeout*=24;
+				case 'h': options->idletimeout*=60;
+				case 'm': options->idletimeout*=60;
+				case 's': 
+				case '0': case '1': case '2': case '3': 
+				case '4': case '5': case '6': case '7': 
+				case '8': case '9':
+	                               break;
+				default:
+					fatal("%s line %d: Invalid IdleTimeout argument",
+						filename,linenum);
+			}
+			break;
+	
 		default:
 			fatal("%s line %d: Missing handler for opcode %s (%d)",
 			    filename, linenum, arg, opcode);
diff -ru openssh-2.9p2.orig/servconf.h openssh-2.9p2/servconf.h
--- openssh-2.9p2.orig/servconf.h	Wed Apr 25 15:44:16 2001
+++ openssh-2.9p2/servconf.h	Wed Aug 15 22:09:33 2001
@@ -125,6 +125,10 @@
 					 * diconnect the session 
 					 */
 	int	pam_authentication_via_kbd_int;
+	int	idletimeout;		/*
+					 * If nonzero, the number of second
+					 * after which idle connections
+					 * will be terminated */
 }       ServerOptions;
 /*
  * Initializes the server options to special values that indicate that they
diff -ru openssh-2.9p2.orig/serverloop.c openssh-2.9p2/serverloop.c
--- openssh-2.9p2.orig/serverloop.c	Sat Apr 14 02:28:03 2001
+++ openssh-2.9p2/serverloop.c	Mon Aug 20 23:33:30 2001
@@ -190,7 +190,6 @@
 wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
     u_int max_time_milliseconds)
 {
-	struct timeval tv, *tvp;
 	int ret;
 	int client_alive_scheduled = 0;
 
@@ -259,25 +258,15 @@
 		if (max_time_milliseconds == 0 || client_alive_scheduled)
 			max_time_milliseconds = 100;
 
-	if (max_time_milliseconds == 0)
-		tvp = NULL;
-	else {
-		tv.tv_sec = max_time_milliseconds / 1000;
-		tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
-		tvp = &tv;
-	}
-	if (tvp!=NULL)
-		debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
 
-	/* Wait for something to happen, or the timeout to expire. */
-	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
+	/* Wait for something to happen, or the timeout to expire. 
+	 * packet select also implements server idletimeouts for us. */
+	ret = packet_select((*maxfdp)+1, *readsetp, *writesetp, NULL, 
+			    max_time_milliseconds);
+
+	if (ret == -1)
+	        goto retry_select;
 
-	if (ret == -1) {
-		if (errno != EINTR)
-			error("select: %.100s", strerror(errno));
-		else
-			goto retry_select;
-	}
 	if (ret == 0 && client_alive_scheduled) {
 		/* timeout, check to see how many we have had */
 		client_alive_timeouts++;
diff -ru openssh-2.9p2.orig/session.c openssh-2.9p2/session.c
--- openssh-2.9p2.orig/session.c	Sun Jun 17 06:40:51 2001
+++ openssh-2.9p2/session.c	Mon Aug 20 22:47:16 2001
@@ -170,6 +170,11 @@
 	 * authentication.
 	 */
 	alarm(0);
+	/*
+	 * Now that the login grace alarm is cleared it is time to apply
+	 * idletimeout */
+	packet_set_idletimeout(options.idletimeout);
+
 	if (startup_pipe != -1) {
 		close(startup_pipe);
 		startup_pipe = -1;


More information about the openssh-unix-dev mailing list