diff common-session.c @ 478:d4f32c3443ac dbclient-netcat-alike

propagate from branch 'au.asn.ucc.matt.dropbear' (head f21045c791002d81fc6b8dde6537ea481e513eb2) to branch 'au.asn.ucc.matt.dropbear.dbclient-netcat-alike' (head d1f69334581dc4c35f9ca16aa5355074c9dd315d)
author Matt Johnston <matt@ucc.asn.au>
date Sun, 14 Sep 2008 06:47:51 +0000
parents 7e43f5e473b9
children e3db1f7a2e43
line wrap: on
line diff
--- a/common-session.c	Tue Mar 21 16:16:41 2006 +0000
+++ b/common-session.c	Sun Sep 14 06:47:51 2008 +0000
@@ -34,8 +34,10 @@
 #include "kex.h"
 #include "channel.h"
 #include "atomicio.h"
+#include "runopts.h"
 
 static void checktimeouts();
+static long select_timeout();
 static int ident_readln(int fd, char* buf, int count);
 
 struct sshsession ses; /* GLOBAL */
@@ -59,11 +61,18 @@
 	ses.sock = sock;
 	ses.maxfd = sock;
 
-	ses.connecttimeout = 0;
+	ses.connect_time = 0;
+	ses.last_packet_time = 0;
+	
+	if (pipe(ses.signal_pipe) < 0) {
+		dropbear_exit("signal pipe failed");
+	}
+	setnonblocking(ses.signal_pipe[0]);
+	setnonblocking(ses.signal_pipe[1]);
 	
 	kexfirstinitialise(); /* initialise the kex state */
 
-	ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
+	ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
 	ses.transseq = 0;
 
 	ses.readbuf = NULL;
@@ -74,9 +83,12 @@
 	initqueue(&ses.writequeue);
 
 	ses.requirenext = SSH_MSG_KEXINIT;
-	ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */
+	ses.dataallowed = 1; /* we can send data until we actually 
+							send the SSH_MSG_KEXINIT */
 	ses.ignorenext = 0;
 	ses.lastpacket = 0;
+	ses.reply_queue_head = NULL;
+	ses.reply_queue_tail = NULL;
 
 	/* set all the algos to none */
 	ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
@@ -108,7 +120,6 @@
 
 	ses.allowprivport = 0;
 
-
 	TRACE(("leave session_init"))
 }
 
@@ -121,7 +132,7 @@
 	/* main loop, select()s for all sockets in use */
 	for(;;) {
 
-		timeout.tv_sec = SELECT_TIMEOUT;
+		timeout.tv_sec = select_timeout();
 		timeout.tv_usec = 0;
 		FD_ZERO(&writefd);
 		FD_ZERO(&readfd);
@@ -132,6 +143,10 @@
 				FD_SET(ses.sock, &writefd);
 			}
 		}
+		
+		/* We get woken up when signal handlers write to this pipe.
+		   SIGCHLD in svr-chansession is the only one currently. */
+		FD_SET(ses.signal_pipe[0], &readfd);
 
 		/* set up for channels which require reading/writing */
 		if (ses.dataallowed) {
@@ -143,27 +158,29 @@
 			dropbear_exit("Terminated by signal");
 		}
 		
-		if (val < 0) {
-			if (errno == EINTR) {
-				/* This must happen even if we've been interrupted, so that
-				 * changed signal-handler vars can take effect etc */
-				if (loophandler) {
-					loophandler();
-				}
-				continue;
-			} else {
-				dropbear_exit("Error in select");
-			}
+		if (val < 0 && errno != EINTR) {
+			dropbear_exit("Error in select");
+		}
+
+		if (val <= 0) {
+			/* If we were interrupted or the select timed out, we still
+			 * want to iterate over channels etc for reading, to handle
+			 * server processes exiting etc. 
+			 * We don't want to read/write FDs. */
+			FD_ZERO(&writefd);
+			FD_ZERO(&readfd);
+		}
+		
+		/* We'll just empty out the pipe if required. We don't do
+		any thing with the data, since the pipe's purpose is purely to
+		wake up the select() above. */
+		if (FD_ISSET(ses.signal_pipe[0], &readfd)) {
+			char x;
+			while (read(ses.signal_pipe[0], &x, 1) > 0) {}
 		}
 
 		/* check for auth timeout, rekeying required etc */
 		checktimeouts();
-		
-		if (val == 0) {
-			/* timeout */
-			TRACE(("select timeout"))
-			continue;
-		}
 
 		/* process session socket's incoming/outgoing data */
 		if (ses.sock != -1) {
@@ -181,6 +198,10 @@
 				process_packet();
 			}
 		}
+		
+		/* if required, flush out any queued reply packets that
+		were being held up during a KEX */
+		maybe_flush_reply_queue();
 
 		/* process pipes etc for the channels, ses.dataallowed == 0
 		 * during rekeying ) */
@@ -229,7 +250,7 @@
 	/* write our version string, this blocks */
 	if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
 				strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
-		dropbear_exit("Error writing ident string");
+		ses.remoteclosed();
 	}
 
     /* If they send more than 50 lines, something is wrong */
@@ -250,7 +271,7 @@
 
 	if (!done) {
 		TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
-		dropbear_exit("Failed to get remote version");
+		ses.remoteclosed();
 	} else {
 		/* linebuf is already null terminated */
 		ses.remoteident = m_malloc(len);
@@ -341,20 +362,22 @@
 	return pos+1;
 }
 
+void send_msg_ignore() {
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
+	buf_putstring(ses.writepayload, "", 0);
+	encrypt_packet();
+}
+
 /* Check all timeouts which are required. Currently these are the time for
  * user authentication, and the automatic rekeying. */
 static void checktimeouts() {
 
-	struct timeval tv;
-	long secs;
+	time_t now;
 
-	if (gettimeofday(&tv, 0) < 0) {
-		dropbear_exit("Error getting time");
-	}
-
-	secs = tv.tv_sec;
+	now = time(NULL);
 	
-	if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
+	if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
 			dropbear_close("Timeout before auth");
 	}
 
@@ -364,10 +387,27 @@
 	}
 
 	if (!ses.kexstate.sentkexinit
-			&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
-			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
+			&& (now - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
+			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)) {
 		TRACE(("rekeying after timeout or max data reached"))
 		send_msg_kexinit();
 	}
+	
+	if (opts.keepalive_secs > 0 
+		&& now - ses.last_packet_time >= opts.keepalive_secs) {
+		send_msg_ignore();
+	}
 }
 
+static long select_timeout() {
+	/* determine the minimum timeout that might be required, so
+	as to avoid waking when unneccessary */
+	long ret = LONG_MAX;
+	if (KEX_REKEY_TIMEOUT > 0)
+		ret = MIN(KEX_REKEY_TIMEOUT, ret);
+	if (AUTH_TIMEOUT > 0)
+		ret = MIN(AUTH_TIMEOUT, ret);
+	if (opts.keepalive_secs > 0)
+		ret = MIN(opts.keepalive_secs, ret);
+	return ret;
+}