[PATCH] Add an exit delay to Openssh-3.0.2p1 for use in tunneling

John Bowman bowman at math.ualberta.ca
Mon Jan 28 06:49:41 EST 2002


Here is a patch to implement an exit delay in OpenSSH-3.0.2p1,
proposed by John Hardin. This is his description of the feature:

New option for OpenSSH: Delay before exit.

Command line option: -S delay

Config file option:  sleep {delay}

Purpose: Wait the specified number of seconds after last traffic before
dropping the connection and exiting. If ports are forwarded, this causes
the ssh client to allow another forwarded connection to begin after the
current one closes. This permits multiple sequential port-forwarded
connections without using a long-running remote sleep command.

For example, for fetchmail polling of several accounts on a remote POP
server over an SSH tunnel, you might say:

    ssh -n -L 11000:popserver:110 -S 30 host exit
or
    ssh -N -L 11000:popserver:110 -S 30 host

The ssh client would exit after thirty seconds of inactivity, rather
than exiting immediately when the first forwarded connection closes.
This would allow multiple sequential POP sessions to be carried over the
same tunnel without specifying a long-running remote sleep command.

Why not just use a long-running remote sleep command? In the above
example, you may wish the ssh session to terminate promptly if no new
mail is spooled: maybe this is running over a demand-dialled ISP
connection.

The -S option also removes the need to run a sleep command on the remote
host in the first place. A delay value of zero means wait forever.

This patch and others are maintained on the secure nfs (SNFS) web page:
http://www.math.ualberta.ca/imaging/snfs/

-- John Bowman
University of Alberta

diff -ur openssh-3.0.2p1/clientloop.c openssh-3.0.2p1S/clientloop.c
--- openssh-3.0.2p1/clientloop.c	Sun Nov 11 17:06:33 2001
+++ openssh-3.0.2p1S/clientloop.c	Sat Jan 19 14:50:11 2002
@@ -84,6 +84,7 @@
 
 /* import options */
 extern Options options;
+extern int no_tty_flag;
 
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
@@ -122,7 +123,8 @@
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
 static int need_rekeying;	/* Set to non-zero if rekeying is requested. */
-static int session_closed = 0;	/* In SSH2: login session closed. */
+enum SessionStatus {SessionOpen, SessionClose, SessionWait};
+static int session_status = SessionOpen; /* In SSH2: login session closed. */
 
 static void client_init_dispatch(void);
 int	session_ident = -1;
@@ -320,6 +322,10 @@
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
     int *maxfdp, int *nallocp, int rekeying)
 {
+	struct timeval timer;
+	struct timeval *timerp;
+	int rc;
+	
 	/* Add any selections by the channel mechanism. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
 
@@ -343,7 +349,7 @@
 			FD_SET(fileno(stderr), *writesetp);
 	} else {
 		/* channel_prepare_select could have closed the last channel */
-		if (session_closed && !channel_still_open() &&
+		if (session_status == SessionClose && !channel_still_open() &&
 		    !packet_have_data_to_write()) {
 			/* clear mask since we did not call select() */
 			memset(*readsetp, 0, *nallocp);
@@ -367,7 +373,16 @@
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+	if(session_status == SessionWait && options.sleep > 0) {
+		timer.tv_sec=options.sleep > 0 ? options.sleep : 0;
+		timer.tv_usec=0;
+		timerp=&timer;
+	} else {
+		timerp=NULL;
+	}
+	
+	rc=select((*maxfdp)+1, *readsetp, *writesetp, NULL, timerp);
+	if (rc < 0) {
 		char buf[100];
 
 		/*
@@ -384,7 +399,8 @@
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
-	}
+	} else if (rc == 0 && session_status == SessionWait)
+		session_status=SessionClose;
 }
 
 static void
@@ -760,7 +776,7 @@
 		error("client_channel_closed: id %d != session_ident %d",
 		    id, session_ident);
 	channel_cancel_cleanup(id);
-	session_closed = 1;
+ 	session_status = (options.sleep >= 0) ? SessionWait : SessionClose;
 	if (in_raw_mode())
 		leave_raw_mode();
 }
@@ -785,6 +801,7 @@
 	start_time = get_current_time();
 
 	/* Initialize variables. */
+	if(!have_pty) session_status=SessionWait;
 	escape_pending = 0;
 	last_was_cr = 1;
 	exit_status = -1;
@@ -848,7 +865,8 @@
 		/* Process buffered packets sent by the server. */
 		client_process_buffered_input_packets();
 
-		if (compat20 && session_closed && !channel_still_open())
+		if (compat20 && (session_status == SessionClose)
+		    && !channel_still_open())
 			break;
 
 		rekeying = (xxx_kex != NULL && !xxx_kex->done);
diff -ur openssh-3.0.2p1/readconf.c openssh-3.0.2p1S/readconf.c
--- openssh-3.0.2p1/readconf.c	Wed Oct  3 11:39:39 2001
+++ openssh-3.0.2p1S/readconf.c	Sat Jan 19 14:49:50 2002
@@ -115,7 +115,7 @@
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
-	oClearAllForwardings, oNoHostAuthenticationForLocalhost 
+	oClearAllForwardings, oNoHostAuthenticationForLocalhost, oSleep 
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -187,6 +187,7 @@
 	{ "smartcarddevice", oSmartcardDevice },
 	{ "clearallforwardings", oClearAllForwardings }, 
 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, 
+  	{ "sleep", oSleep },
 	{ NULL, 0 }
 };
 
@@ -528,6 +529,10 @@
 		intptr = &options->connection_attempts;
 		goto parse_int;
 
+	case oSleep:
+		intptr = &options->sleep;
+		goto parse_int;
+
 	case oCipher:
 		intptr = &options->cipher;
 		arg = strdelim(&s);
@@ -799,6 +804,7 @@
 	options->bind_address = NULL;
 	options->smartcard_device = NULL;
 	options->no_host_authentication_for_localhost = - 1;
+	options->sleep = -1;
 }
 
 /*
diff -ur openssh-3.0.2p1/readconf.h openssh-3.0.2p1S/readconf.h
--- openssh-3.0.2p1/readconf.h	Wed Oct  3 11:39:39 2001
+++ openssh-3.0.2p1S/readconf.h	Sat Jan 19 14:49:50 2002
@@ -102,6 +102,7 @@
 	Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
 	int	clear_forwardings;
 	int	no_host_authentication_for_localhost;
+        int     sleep;		/* Exit delay in seconds */
 }       Options;
 
 
diff -ur openssh-3.0.2p1/ssh.c openssh-3.0.2p1S/ssh.c
--- openssh-3.0.2p1/ssh.c	Sun Nov 11 16:52:04 2001
+++ openssh-3.0.2p1S/ssh.c	Sat Jan 19 14:49:51 2002
@@ -187,6 +187,7 @@
 	fprintf(stderr, "              These cause %s to listen for connections on a port, and\n", __progname);
 	fprintf(stderr, "              forward them to the other side by connecting to host:port.\n");
 	fprintf(stderr, "  -D port     Enable dynamic application-level port forwarding.\n");
+ 	fprintf(stderr, "  -S delay    Set exit delay (in seconds; 0 means wait forever).\n");
 	fprintf(stderr, "  -C          Enable compression.\n");
 	fprintf(stderr, "  -N          Do not execute a shell or command.\n");
 	fprintf(stderr, "  -g          Allow remote hosts to connect to forwarded ports.\n");
@@ -312,7 +313,7 @@
 
 again:
 	while ((opt = getopt(ac, av,
-	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVX")) != -1) {
+	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:S:TVX")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -487,7 +488,13 @@
 				add_remote_forward(&options, fwd_port, buf,
 				     fwd_host_port);
 			break;
-
+		case 'S':
+			options.sleep = atoi(optarg);
+			if (options.sleep < 0) {
+				fprintf(stderr, "Bad delay value '%s'\n", optarg);
+				exit(1);
+			}
+			break;
 		case 'D':
 			fwd_port = a2port(optarg);
 			if (fwd_port == 0) {



More information about the openssh-unix-dev mailing list