changeset 943:185b241a0b4a coverity

merge
author Matt Johnston <matt@ucc.asn.au>
date Fri, 25 Jul 2014 22:23:50 +0800
parents 204dc7bd62aa (current diff) 8664fea5072f (diff)
children 9969cee83d0a
files
diffstat 29 files changed, 406 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Sat Mar 08 21:02:02 2014 +0800
+++ b/CHANGES	Fri Jul 25 22:23:50 2014 +0800
@@ -1,3 +1,29 @@
+2014.64 pending
+
+- Fix compiling with ECDSA and DSS disabled
+
+- Don't exit abruptly if too many outgoing packets are queued for writev(). Patch
+  thanks to Ronny Meeus
+
+- The -K keepalive option now behaves more like OpenSSH's "ServerAliveInterval". 
+  If no response is received after 3 keepalives then the session is terminated. This
+  will close connections faster than waiting for a TCP timeout.
+
+- Rework TCP priority setting. New settings are
+	if (connecting || ptys || x11) tos = LOWDELAY
+	else if (tcp_forwards) tos = 0
+	else tos = BULK
+  Thanks to Catalin Patulea for the suggestion.
+
+- Improve handling of many concurrent new TCP forwarded connections, should now
+  be able to handle as many as MAX_CHANNELS. Thanks to Eduardo Silva for reporting
+  and investigating it.
+
+- Make sure that exit messages from the client are printed, regression in 2013.57
+
+- Use monotonic clock where available, timeouts won't be affected by system time
+  changes
+
 2014.63 - Wednesday 19 February 2014
 
 - Fix ~. to terminate a client interactive session after waking a laptop
--- a/channel.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/channel.h	Fri Jul 25 22:23:50 2014 +0800
@@ -29,14 +29,6 @@
 #include "buffer.h"
 #include "circbuffer.h"
 
-/* channel->type values */
-#define CHANNEL_ID_NONE 0
-#define CHANNEL_ID_SESSION 1
-#define CHANNEL_ID_X11 2
-#define CHANNEL_ID_AGENT 3
-#define CHANNEL_ID_TCPDIRECT 4
-#define CHANNEL_ID_TCPFORWARDED 5
-
 #define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED    1
 #define SSH_OPEN_CONNECT_FAILED                 2
 #define SSH_OPEN_UNKNOWN_CHANNEL_TYPE           3
@@ -49,6 +41,13 @@
 
 struct ChanType;
 
+enum dropbear_channel_prio {
+	DROPBEAR_CHANNEL_PRIO_INTERACTIVE, /* pty shell, x11 */
+	DROPBEAR_CHANNEL_PRIO_UNKNOWABLE, /* tcp - can't know what's being forwarded */
+	DROPBEAR_CHANNEL_PRIO_BULK, /* the rest - probably scp or something */
+	DROPBEAR_CHANNEL_PRIO_EARLY, /* channel is still being set up */
+};
+
 struct Channel {
 
 	unsigned int index; /* the local channel index */
@@ -87,6 +86,8 @@
 	void (*read_mangler)(struct Channel*, unsigned char* bytes, int *len);
 
 	const struct ChanType* type;
+
+	enum dropbear_channel_prio prio;
 };
 
 struct ChanType {
@@ -97,7 +98,6 @@
 	int (*check_close)(struct Channel*);
 	void (*reqhandler)(struct Channel*);
 	void (*closehandler)(struct Channel*);
-
 };
 
 void chaninitialise(const struct ChanType *chantypes[]);
@@ -129,4 +129,7 @@
 void recv_msg_channel_open_failure();
 #endif
 
+void send_msg_request_success();
+void send_msg_request_failure();
+
 #endif /* _CHANNEL_H_ */
--- a/cli-auth.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-auth.c	Fri Jul 25 22:23:50 2014 +0800
@@ -174,11 +174,11 @@
 	the "none" auth request, and then a response to the immediate auth request. 
 	We need to be careful handling them. */
 	if (cli_ses.ignore_next_auth_response) {
-		TRACE(("ignore next response, state set to USERAUTH_REQ_SENT"))
 		cli_ses.state = USERAUTH_REQ_SENT;
+		cli_ses.ignore_next_auth_response = 0;
+		TRACE(("leave recv_msg_userauth_failure, ignored response, state set to USERAUTH_REQ_SENT"));
+		return;
 	} else  {
-		cli_ses.state = USERAUTH_FAIL_RCVD;
-		cli_ses.lastauthtype = AUTH_TYPE_NONE;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 		/* If it was a pubkey auth request, we should cross that key 
 		 * off the list. */
@@ -197,10 +197,10 @@
 			cli_ses.auth_interact_failed = 1;
 		}
 #endif
+		cli_ses.state = USERAUTH_FAIL_RCVD;
+		cli_ses.lastauthtype = AUTH_TYPE_NONE;
 	}
 
-	cli_ses.ignore_next_auth_response = 0;
-
 	methods = buf_getstring(ses.payload, &methlen);
 
 	partial = buf_getbool(ses.payload);
--- a/cli-chansession.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-chansession.c	Fri Jul 25 22:23:50 2014 +0800
@@ -41,7 +41,7 @@
 static void send_chansess_pty_req(struct Channel *channel);
 static void send_chansess_shell_req(struct Channel *channel);
 static void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len);
-
+static int cli_init_netcat(struct Channel *channel);
 
 static void cli_tty_setup();
 
@@ -357,6 +357,11 @@
 	return 0;
 }
 
+static int cli_init_netcat(struct Channel *channel) {
+	channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
+	return cli_init_stdpipe_sess(channel);
+}
+
 static int cli_initchansess(struct Channel *channel) {
 
 	cli_init_stdpipe_sess(channel);
@@ -369,8 +374,9 @@
 
 	if (cli_opts.wantpty) {
 		send_chansess_pty_req(channel);
+		channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
 	} else {
-		set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK);
+		channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
 	}
 
 	send_chansess_shell_req(channel);
@@ -389,7 +395,7 @@
 static const struct ChanType cli_chan_netcat = {
 	0, /* sepfds */
 	"direct-tcpip",
-	cli_init_stdpipe_sess, /* inithandler */
+	cli_init_netcat, /* inithandler */
 	NULL,
 	NULL,
 	cli_closechansess
--- a/cli-main.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-main.c	Fri Jul 25 22:23:50 2014 +0800
@@ -75,9 +75,6 @@
 		int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
 				0, &error);
 		sock_in = sock_out = sock;
-	 	if (cli_opts.wantpty) {
-			set_sock_priority(sock, DROPBEAR_PRIO_LOWDELAY);
-	 	}
 	}
 
 	if (sock_in < 0) {
@@ -107,9 +104,10 @@
 
 	/* Do the cleanup first, since then the terminal will be reset */
 	session_cleanup();
+	/* Avoid printing onwards from terminal cruft */
+	fprintf(stderr, "\n");
 
 	_dropbear_log(LOG_INFO, fmtbuf, param);
-
 	exit(exitcode);
 }
 
@@ -121,7 +119,7 @@
 	vsnprintf(printbuf, sizeof(printbuf), format, param);
 
 	fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
-
+	fflush(stderr);
 }
 
 static void exec_proxy_cmd(void *user_data_cmd) {
--- a/cli-runopts.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-runopts.c	Fri Jul 25 22:23:50 2014 +0800
@@ -163,6 +163,8 @@
 	opts.ipv6 = 1;
 	*/
 	opts.recv_window = DEFAULT_RECV_WINDOW;
+	opts.keepalive_secs = DEFAULT_KEEPALIVE;
+	opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
 
 	fill_own_user();
 
--- a/cli-session.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-session.c	Fri Jul 25 22:23:50 2014 +0800
@@ -44,6 +44,7 @@
 static void cli_finished();
 static void recv_msg_service_accept(void);
 static void cli_session_cleanup(void);
+static void recv_msg_global_request_cli(void);
 
 struct clientsession cli_ses; /* GLOBAL */
 
@@ -68,6 +69,7 @@
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
 	{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
 	{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
+	{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli},
 #ifdef  ENABLE_CLI_REMOTETCPFWD
 	{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
 	{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
@@ -366,3 +368,9 @@
 	/* Null terminate */
 	dirtytext[j] = '\0';
 }
+
+static void recv_msg_global_request_cli(void) {
+	TRACE(("recv_msg_global_request_cli"))
+	/* Send a proper rejection */
+	send_msg_request_failure();
+}
--- a/cli-tcpfwd.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/cli-tcpfwd.c	Fri Jul 25 22:23:50 2014 +0800
@@ -52,7 +52,7 @@
 static const struct ChanType cli_chan_tcplocal = {
 	1, /* sepfds */
 	"direct-tcpip",
-	NULL,
+	tcp_prio_inithandler,
 	NULL,
 	NULL,
 	NULL
@@ -267,6 +267,8 @@
 	 * progress succeeds */
 	channel->writefd = sock;
 	channel->initconn = 1;
+
+	channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
 	
 	err = SSH_OPEN_IN_PROGRESS;
 
--- a/common-channel.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/common-channel.c	Fri Jul 25 22:23:50 2014 +0800
@@ -174,6 +174,8 @@
 	newchan->recvdonelen = 0;
 	newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN;
 
+	newchan->prio = DROPBEAR_CHANNEL_PRIO_EARLY; /* inithandler sets it */
+
 	ses.channels[i] = newchan;
 	ses.chancount++;
 
@@ -208,11 +210,14 @@
 /* Iterate through the channels, performing IO if available */
 void channelio(fd_set *readfds, fd_set *writefds) {
 
+	/* Listeners such as TCP, X11, agent-auth */
 	struct Channel *channel;
 	unsigned int i;
 
 	/* foreach channel */
 	for (i = 0; i < ses.chansize; i++) {
+		/* Close checking only needs to occur for channels that had IO events */
+		int do_check_close = 0;
 
 		channel = ses.channels[i];
 		if (channel == NULL) {
@@ -224,6 +229,7 @@
 		if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
 			TRACE(("send normal readfd"))
 			send_msg_channel_data(channel, 0);
+			do_check_close = 1;
 		}
 
 		/* read stderr data and send it over the wire */
@@ -231,6 +237,7 @@
 			&& FD_ISSET(channel->errfd, readfds)) {
 				TRACE(("send normal errfd"))
 				send_msg_channel_data(channel, 1);
+			do_check_close = 1;
 		}
 
 		/* write to program/pipe stdin */
@@ -242,20 +249,22 @@
 							 check_in_progress(), as it may be NULL */
 			}
 			writechannel(channel, channel->writefd, channel->writebuf);
+			do_check_close = 1;
 		}
 		
 		/* stderr for client mode */
 		if (ERRFD_IS_WRITE(channel)
 				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
 			writechannel(channel, channel->errfd, channel->extrabuf);
+			do_check_close = 1;
 		}
 	
 		/* handle any channel closing etc */
-		check_close(channel);
-
+		if (do_check_close) {
+			check_close(channel);
+		}
 	}
 
-	/* Listeners such as TCP, X11, agent-auth */
 #ifdef USING_LISTENERS
 	handle_listeners(readfds);
 #endif
@@ -567,14 +576,16 @@
 	}
 
 
-	/* close the FDs in case they haven't been done
-	 * yet (they might have been shutdown etc) */
-	TRACE(("CLOSE writefd %d", channel->writefd))
-	close(channel->writefd);
-	TRACE(("CLOSE readfd %d", channel->readfd))
-	close(channel->readfd);
-	TRACE(("CLOSE errfd %d", channel->errfd))
-	close(channel->errfd);
+	if (IS_DROPBEAR_SERVER || (channel->writefd != STDOUT_FILENO)) {
+		/* close the FDs in case they haven't been done
+		 * yet (they might have been shutdown etc) */
+		TRACE(("CLOSE writefd %d", channel->writefd))
+		close(channel->writefd);
+		TRACE(("CLOSE readfd %d", channel->readfd))
+		close(channel->readfd);
+		TRACE(("CLOSE errfd %d", channel->errfd))
+		close(channel->errfd);
+	}
 
 	if (!channel->close_handler_done
 		&& channel->type->closehandler) {
@@ -586,6 +597,8 @@
 	m_free(channel);
 	ses.chancount--;
 
+	update_channel_prio();
+
 	TRACE(("leave remove_channel"))
 }
 
@@ -876,6 +889,10 @@
 		}
 	}
 
+	if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
+		channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
+	}
+
 	chan_initwritebuf(channel);
 
 	/* success */
@@ -889,6 +906,8 @@
 
 cleanup:
 	m_free(type);
+	
+	update_channel_prio();
 
 	TRACE(("leave recv_msg_channel_open"))
 }
@@ -1004,7 +1023,7 @@
  * for X11, agent, tcp forwarding, and should be filled with channel-specific
  * options, with the calling function calling encrypt_packet() after
  * completion. It is mandatory for the caller to encrypt_packet() if
- * DROPBEAR_SUCCESS is returned */
+ * a channel is returned. NULL is returned on failure. */
 int send_msg_channel_open_init(int fd, const struct ChanType *type) {
 
 	struct Channel* chan;
@@ -1073,6 +1092,10 @@
 		}
 	}
 
+	if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
+		channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
+	}
+	update_channel_prio();
 	
 	TRACE(("leave recv_msg_channel_open_confirmation"))
 }
@@ -1092,3 +1115,15 @@
 	remove_channel(channel);
 }
 #endif /* USING_LISTENERS */
+
+void send_msg_request_success() {
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
+	encrypt_packet();
+}
+
+void send_msg_request_failure() {
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
+	encrypt_packet();
+}
--- a/common-kex.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/common-kex.c	Fri Jul 25 22:23:50 2014 +0800
@@ -270,7 +270,7 @@
 
 	ses.kexstate.our_first_follows_matches = 0;
 
-	ses.kexstate.lastkextime = time(NULL);
+	ses.kexstate.lastkextime = monotonic_now();
 
 }
 
--- a/common-session.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/common-session.c	Fri Jul 25 22:23:50 2014 +0800
@@ -51,6 +51,7 @@
 
 /* called only at the start of a session, set up initial state */
 void common_session_init(int sock_in, int sock_out) {
+	time_t now;
 
 	TRACE(("enter session_init"))
 
@@ -58,9 +59,15 @@
 	ses.sock_out = sock_out;
 	ses.maxfd = MAX(sock_in, sock_out);
 
-	ses.connect_time = 0;
-	ses.last_trx_packet_time = 0;
-	ses.last_packet_time = 0;
+	ses.socket_prio = DROPBEAR_PRIO_DEFAULT;
+	/* Sets it to lowdelay */
+	update_channel_prio();
+
+	now = monotonic_now();
+	ses.last_packet_time_keepalive_recv = now;
+	ses.last_packet_time_idle = now;
+	ses.last_packet_time_any_sent = 0;
+	ses.last_packet_time_keepalive_sent = 0;
 	
 	if (pipe(ses.signal_pipe) < 0) {
 		dropbear_exit("Signal pipe failed");
@@ -186,13 +193,7 @@
 		/* check for auth timeout, rekeying required etc */
 		checktimeouts();
 
-		/* process session socket's incoming/outgoing data */
-		if (ses.sock_out != -1) {
-			if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) {
-				write_packet();
-			}
-		}
-
+		/* process session socket's incoming data */
 		if (ses.sock_in != -1) {
 			if (FD_ISSET(ses.sock_in, &readfd)) {
 				if (!ses.remoteident) {
@@ -218,6 +219,14 @@
 		 * during rekeying ) */
 		channelio(&readfd, &writefd);
 
+		/* process session socket's outgoing data */
+		if (ses.sock_out != -1) {
+			if (!isempty(&ses.writequeue)) {
+				write_packet();
+			}
+		}
+
+
 		if (loophandler) {
 			loophandler();
 		}
@@ -385,11 +394,21 @@
 	return pos+1;
 }
 
-void send_msg_ignore() {
+static void send_msg_keepalive() {
 	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
-	buf_putstring(ses.writepayload, "", 0);
+	time_t old_time_idle = ses.last_packet_time_idle;
+	/* Try to force a response from the other end. Some peers will
+	reply with SSH_MSG_REQUEST_FAILURE, some will reply with SSH_MSG_UNIMPLEMENTED */
+	buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
+	/* A short string */
+	buf_putstring(ses.writepayload, "[email protected]", 0);
+	buf_putbyte(ses.writepayload, 1); /* want_reply */
 	encrypt_packet();
+
+	ses.last_packet_time_keepalive_sent = monotonic_now();
+
+	/* keepalives shouldn't update idle timeout, reset it back */
+	ses.last_packet_time_idle = old_time_idle;
 }
 
 /* Check all timeouts which are required. Currently these are the time for
@@ -397,13 +416,8 @@
 static void checktimeouts() {
 
 	time_t now;
-
-	now = time(NULL);
+	now = monotonic_now();
 	
-	if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
-			dropbear_close("Timeout before auth");
-	}
-
 	/* we can't rekey if we haven't done remote ident exchange yet */
 	if (ses.remoteident == NULL) {
 		return;
@@ -416,13 +430,27 @@
 		send_msg_kexinit();
 	}
 	
-	if (opts.keepalive_secs > 0 
-			&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
-		send_msg_ignore();
+	if (opts.keepalive_secs > 0) {
+		/* Send keepalives if we've been idle */
+		if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
+			send_msg_keepalive();
+		}
+
+		/* Also send an explicit keepalive message to trigger a response
+		if the remote end hasn't sent us anything */
+		if (now - ses.last_packet_time_keepalive_recv >= opts.keepalive_secs
+			&& now - ses.last_packet_time_keepalive_sent >= opts.keepalive_secs) {
+			send_msg_keepalive();
+		}
+
+		if (now - ses.last_packet_time_keepalive_recv 
+			>= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) {
+			dropbear_exit("Keepalive timeout");
+		}
 	}
 
-	if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
-			&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
+	if (opts.idle_timeout_secs > 0 
+			&& now - ses.last_packet_time_idle >= opts.idle_timeout_secs) {
 		dropbear_close("Idle timeout");
 	}
 }
@@ -433,12 +461,13 @@
 	long ret = LONG_MAX;
 	if (KEX_REKEY_TIMEOUT > 0)
 		ret = MIN(KEX_REKEY_TIMEOUT, ret);
-	if (AUTH_TIMEOUT > 0)
+	/* AUTH_TIMEOUT is only relevant before authdone */
+	if (ses.authstate.authdone != 1 && AUTH_TIMEOUT > 0)
 		ret = MIN(AUTH_TIMEOUT, ret);
 	if (opts.keepalive_secs > 0)
 		ret = MIN(opts.keepalive_secs, ret);
-    if (opts.idle_timeout_secs > 0)
-        ret = MIN(opts.idle_timeout_secs, ret);
+	if (opts.idle_timeout_secs > 0)
+		ret = MIN(opts.idle_timeout_secs, ret);
 	return ret;
 }
 
@@ -487,3 +516,47 @@
 	}
 }
 
+/* Called when channels are modified */
+void update_channel_prio() {
+	enum dropbear_prio new_prio;
+	int any = 0;
+	unsigned int i;
+
+	TRACE(("update_channel_prio"))
+
+	new_prio = DROPBEAR_PRIO_BULK;
+	for (i = 0; i < ses.chansize; i++) {
+		struct Channel *channel = ses.channels[i];
+		if (!channel || channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
+			if (channel && channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
+				TRACE(("update_channel_prio: early %d", channel->index))
+			}
+			continue;
+		}
+		any = 1;
+		if (channel->prio == DROPBEAR_CHANNEL_PRIO_INTERACTIVE)
+		{
+			TRACE(("update_channel_prio: lowdelay %d", channel->index))
+			new_prio = DROPBEAR_PRIO_LOWDELAY;
+			break;
+		} else if (channel->prio == DROPBEAR_CHANNEL_PRIO_UNKNOWABLE
+			&& new_prio == DROPBEAR_PRIO_BULK)
+		{
+			TRACE(("update_channel_prio: unknowable %d", channel->index))
+			new_prio = DROPBEAR_PRIO_DEFAULT;
+		}
+	}
+
+	if (any == 0) {
+		/* lowdelay during setup */
+		TRACE(("update_channel_prio: not any"))
+		new_prio = DROPBEAR_PRIO_LOWDELAY;
+	}
+
+	if (new_prio != ses.socket_prio) {
+		TRACE(("Dropbear priority transitioning %4.4s -> %4.4s", (char*)&ses.socket_prio, (char*)&new_prio))
+		set_sock_priority(ses.sock_out, new_prio);
+		ses.socket_prio = new_prio;
+	}
+}
+
--- a/configure.ac	Sat Mar 08 21:02:02 2014 +0800
+++ b/configure.ac	Fri Jul 25 22:23:50 2014 +0800
@@ -361,6 +361,10 @@
 AC_CHECK_FUNCS(setutxent utmpxname)
 AC_CHECK_FUNCS(logout updwtmp logwtmp)
 
+# OS X monotonic time
+AC_CHECK_HEADERS([mach/mach_time.h])
+AC_CHECK_FUNCS(mach_absolute_time)
+
 AC_ARG_ENABLE(bundled-libtom,
 [  --enable-bundled-libtom       Force using bundled libtomcrypt/libtommath even if a system version exists.
   --disable-bundled-libtom      Force using system libtomcrypt/libtommath, fail if it does not exist.
--- a/dbutil.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/dbutil.c	Fri Jul 25 22:23:50 2014 +0800
@@ -48,6 +48,19 @@
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 
+#include "config.h"
+
+#ifdef __linux__
+#define _GNU_SOURCE
+/* To call clock_gettime() directly */
+#include <sys/syscall.h>
+#endif /* __linux */
+
+#ifdef HAVE_MACH_MACH_TIME_H
+#include <mach/mach_time.h>
+#include <mach/mach.h>
+#endif
+
 #include "includes.h"
 #include "dbutil.h"
 #include "buffer.h"
@@ -319,7 +332,7 @@
 			continue;
 		}
 
-		if (listen(sock, 20) < 0) {
+		if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) {
 			err = errno;
 			close(sock);
 			TRACE(("listen() failed"))
@@ -932,3 +945,33 @@
 	return c;
 }
 
+time_t monotonic_now() {
+
+#if defined(__linux__) && defined(SYS_clock_gettime)
+	/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32. Probably cheaper. */
+#ifndef CLOCK_MONOTONIC_COARSE
+#define CLOCK_MONOTONIC_COARSE 6
+#endif
+	static clockid_t clock_source = CLOCK_MONOTONIC_COARSE;
+	struct timespec ts;
+
+	if (syscall(SYS_clock_gettime, clock_source, &ts) == EINVAL) {
+		clock_source = CLOCK_MONOTONIC;
+		syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts);
+	}
+	return ts.tv_sec;
+#elif defined(HAVE_MACH_ABSOLUTE_TIME)
+	/* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
+	static mach_timebase_info_data_t timebase_info;
+	if (timebase_info.denom == 0) {
+		mach_timebase_info(&timebase_info);
+	}
+	return mach_absolute_time() * timebase_info.numer / timebase_info.denom
+		/ 1e9;
+#else 
+	/* Fallback for everything else - this will sometimes go backwards */
+	return time(NULL);
+#endif
+
+}
+
--- a/dbutil.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/dbutil.h	Fri Jul 25 22:23:50 2014 +0800
@@ -62,9 +62,9 @@
 #endif
 
 enum dropbear_prio {
-	DROPBEAR_PRIO_DEFAULT,
-	DROPBEAR_PRIO_LOWDELAY,
-	DROPBEAR_PRIO_BULK,
+	DROPBEAR_PRIO_DEFAULT = 'dffd',
+	DROPBEAR_PRIO_LOWDELAY = 'lddl',
+	DROPBEAR_PRIO_BULK = 'bllb',
 };
 
 char * stripcontrol(const char * text);
@@ -106,4 +106,9 @@
 /* Returns 0 if a and b have the same contents */
 int constant_time_memcmp(const void* a, const void *b, size_t n);
 
+/* Returns a time in seconds that doesn't go backwards - does not correspond to
+a real-world clock */
+time_t monotonic_now();
+
+
 #endif /* _DBUTIL_H_ */
--- a/keyimport.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/keyimport.c	Fri Jul 25 22:23:50 2014 +0800
@@ -602,13 +602,18 @@
 	 */
 	blobbuf = buf_new(3000);
 
+#ifdef DROPBEAR_DSS
 	if (key->type == OSSH_DSA) {
 		buf_putstring(blobbuf, "ssh-dss", 7);
 		retkey->type = DROPBEAR_SIGNKEY_DSS;
-	} else if (key->type == OSSH_RSA) {
+	} 
+#endif
+#ifdef DROPBEAR_RSA
+	if (key->type == OSSH_RSA) {
 		buf_putstring(blobbuf, "ssh-rsa", 7);
 		retkey->type = DROPBEAR_SIGNKEY_RSA;
 	}
+#endif
 
 	for (i = 0; i < num_integers; i++) {
 		ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
@@ -831,7 +836,14 @@
 	mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */
 #endif
 
-	if (key->type == DROPBEAR_SIGNKEY_RSA || key->type == DROPBEAR_SIGNKEY_DSS)
+	if (
+#ifdef DROPBEAR_RSA
+			key->type == DROPBEAR_SIGNKEY_RSA ||
+#endif
+#ifdef DROPBEAR_DSS
+			key->type == DROPBEAR_SIGNKEY_DSS ||
+#endif
+			0)
 	{
 		/*
 		 * Fetch the key blobs.
--- a/options.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/options.h	Fri Jul 25 22:23:50 2014 +0800
@@ -308,6 +308,11 @@
 be overridden at runtime with -K. 0 disables keepalives */
 #define DEFAULT_KEEPALIVE 0
 
+/* If this many KEEPALIVES are sent with no packets received from the
+other side, exit. Not run-time configurable - if you have a need
+for runtime configuration please mail the Dropbear list */
+#define DEFAULT_KEEPALIVE_LIMIT 3
+
 /* Ensure that data is received within IDLE_TIMEOUT seconds. This can
 be overridden at runtime with -I. 0 disables idle timeouts */
 #define DEFAULT_IDLE_TIMEOUT 0
--- a/packet.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/packet.c	Fri Jul 25 22:23:50 2014 +0800
@@ -57,42 +57,52 @@
 
 	int len, written;
 	buffer * writebuf = NULL;
-	time_t now;
 	unsigned packet_type;
-	int all_ignore = 1;
 #ifdef HAVE_WRITEV
 	struct iovec *iov = NULL;
 	int i;
 	struct Link *l;
+	int iov_max_count;
 #endif
 	
 	TRACE2(("enter write_packet"))
 	dropbear_assert(!isempty(&ses.writequeue));
 
-#ifdef HAVE_WRITEV
-	iov = m_malloc(sizeof(*iov) * ses.writequeue.count);
+#if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV))
+
+#ifndef IOV_MAX
+#define IOV_MAX UIO_MAXIOV
+#endif
+
+	/* Make sure the size of the iov is below the maximum allowed by the OS. */
+	iov_max_count = ses.writequeue.count;
+	if (iov_max_count > IOV_MAX)
+	{
+		iov_max_count = IOV_MAX;
+	}
+
+	iov = m_malloc(sizeof(*iov) * iov_max_count);
 	for (l = ses.writequeue.head, i = 0; l; l = l->link, i++)
 	{
 		writebuf = (buffer*)l->item;
 		packet_type = writebuf->data[writebuf->len-1];
 		len = writebuf->len - 1 - writebuf->pos;
 		dropbear_assert(len > 0);
-		all_ignore &= (packet_type == SSH_MSG_IGNORE);
 		TRACE2(("write_packet writev #%d  type %d len %d/%d", i, packet_type,
 				len, writebuf->len-1))
 		iov[i].iov_base = buf_getptr(writebuf, len);
 		iov[i].iov_len = len;
 	}
-	written = writev(ses.sock_out, iov, ses.writequeue.count);
+	written = writev(ses.sock_out, iov, iov_max_count);
 	if (written < 0) {
 		if (errno == EINTR) {
 			m_free(iov);
-			TRACE2(("leave writepacket: EINTR"))
+			TRACE2(("leave write_packet: EINTR"))
 			return;
 		} else {
-			dropbear_exit("Error writing");
+			dropbear_exit("Error writing: %s", strerror(errno));
 		}
-	} 
+	}
 
 	if (written == 0) {
 		ses.remoteclosed();
@@ -113,8 +123,7 @@
 	}
 
 	m_free(iov);
-
-#else
+#else /* No writev () */
 	/* Get the next buffer in the queue of encrypted packets to write*/
 	writebuf = (buffer*)examine(&ses.writequeue);
 
@@ -131,10 +140,9 @@
 			TRACE2(("leave writepacket: EINTR"))
 			return;
 		} else {
-			dropbear_exit("Error writing");
+			dropbear_exit("Error writing: %s", strerror(errno));
 		}
 	} 
-	all_ignore = (packet_type == SSH_MSG_IGNORE);
 
 	if (written == 0) {
 		ses.remoteclosed();
@@ -149,14 +157,7 @@
 		/* More packet left to write, leave it in the queue for later */
 		buf_incrpos(writebuf, written);
 	}
-
-#endif
-	now = time(NULL);
-	ses.last_trx_packet_time = now;
-
-	if (!all_ignore) {
-		ses.last_packet_time = now;
-	}
+#endif /* writev */
 
 	TRACE2(("leave write_packet"))
 }
@@ -503,6 +504,8 @@
 	unsigned char packet_type;
 	unsigned int len, encrypt_buf_size;
 	unsigned char mac_bytes[MAX_MAC_LEN];
+
+	time_t now;
 	
 	TRACE2(("enter encrypt_packet()"))
 
@@ -610,6 +613,18 @@
 	ses.kexstate.datatrans += writebuf->len;
 	ses.transseq++;
 
+	now = monotonic_now();
+	ses.last_packet_time_any_sent = now;
+	/* idle timeout shouldn't be affected by responses to keepalives.
+	send_msg_keepalive() itself also does tricks with 
+	ses.last_packet_idle_time - read that if modifying this code */
+	if (packet_type != SSH_MSG_REQUEST_FAILURE
+		&& packet_type != SSH_MSG_UNIMPLEMENTED
+		&& packet_type != SSH_MSG_IGNORE) {
+		ses.last_packet_time_idle = now;
+
+	}
+
 	TRACE2(("leave encrypt_packet()"))
 }
 
--- a/process-packet.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/process-packet.c	Fri Jul 25 22:23:50 2014 +0800
@@ -44,6 +44,7 @@
 
 	unsigned char type;
 	unsigned int i;
+	time_t now;
 
 	TRACE2(("enter process_packet"))
 
@@ -52,7 +53,8 @@
 
 	ses.lastpacket = type;
 
-    ses.last_packet_time = time(NULL);
+	now = monotonic_now();
+	ses.last_packet_time_keepalive_recv = now;
 
 	/* These packets we can receive at any time */
 	switch(type) {
@@ -65,13 +67,21 @@
 		case SSH_MSG_UNIMPLEMENTED:
 			/* debugging XXX */
 			TRACE(("SSH_MSG_UNIMPLEMENTED"))
-			dropbear_exit("Received SSH_MSG_UNIMPLEMENTED");
+			goto out;
 			
 		case SSH_MSG_DISCONNECT:
 			/* TODO cleanup? */
 			dropbear_close("Disconnect received");
 	}
 
+	/* Ignore these packet types so that keepalives don't interfere with
+	idle detection. This is slightly incorrect since a tcp forwarded
+	global request with failure won't trigger the idle timeout,
+	but that's probably acceptable */
+	if (!(type == SSH_MSG_GLOBAL_REQUEST || type == SSH_MSG_REQUEST_FAILURE)) {
+		ses.last_packet_time_idle = now;
+	}
+
 	/* This applies for KEX, where the spec says the next packet MUST be
 	 * NEWKEYS */
 	if (ses.requirenext != 0) {
--- a/runopts.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/runopts.h	Fri Jul 25 22:23:50 2014 +0800
@@ -37,8 +37,8 @@
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	time_t keepalive_secs;
-	time_t idle_timeout_secs;
+	time_t keepalive_secs; /* Time between sending keepalives. 0 is off */
+	time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */
 
 #ifndef DISABLE_ZLIB
 	/* TODO: add a commandline flag. Currently this is on by default if compression
--- a/session.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/session.h	Fri Jul 25 22:23:50 2014 +0800
@@ -48,6 +48,8 @@
 void send_session_identification();
 void send_msg_ignore();
 
+void update_channel_prio();
+
 const char* get_user_shell();
 void fill_passwd(const char* username);
 
@@ -104,10 +106,6 @@
 	/* Is it a client or server? */
 	unsigned char isserver;
 
-	time_t connect_time; /* time the connection was established
-							(cleared after auth once we're not
-							respecting AUTH_TIMEOUT any more) */
-
 	int sock_in;
 	int sock_out;
 
@@ -146,11 +144,14 @@
 	int signal_pipe[2]; /* stores endpoints of a self-pipe used for
 						   race-free signal handling */
 						
-	time_t last_trx_packet_time; /* time of the last packet transmission, for
-							keepalive purposes */
+	/* time of the last packet send/receive, for keepalive. Not real-world clock */
+	time_t last_packet_time_keepalive_sent;
+	time_t last_packet_time_keepalive_recv;
+	time_t last_packet_time_any_sent;
 
-	time_t last_packet_time; /* time of the last packet transmission or receive, for
-								idle timeout purposes */
+	time_t last_packet_time_idle; /* time of the last packet transmission or receive, for
+								idle timeout purposes so ignores SSH_MSG_IGNORE
+								or responses to keepalives. Not real-world clock */
 
 
 	/* KEX/encryption related */
@@ -187,7 +188,9 @@
 	unsigned int chancount; /* the number of Channel*s in use */
 	const struct ChanType **chantypes; /* The valid channel types */
 
-	
+	/* TCP priority level for the main "port 22" tcp socket */
+	enum dropbear_prio socket_prio;
+
 	/* TCP forwarding - where manage listeners */
 	struct Listener ** listeners;
 	unsigned int listensize;
@@ -217,6 +220,11 @@
 	/* The resolved remote address, used for lastlog etc */
 	char *remotehost;
 
+	time_t connect_time; /* time the connection was established
+							(cleared after auth once we're not
+							respecting AUTH_TIMEOUT any more).
+							A monotonic time, not realworld */
+
 #ifdef USE_VFORK
 	pid_t server_pid;
 #endif
--- a/signkey.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/signkey.c	Fri Jul 25 22:23:50 2014 +0800
@@ -106,6 +106,7 @@
 void **
 signkey_key_ptr(sign_key *key, enum signkey_type type) {
 	switch (type) {
+#ifdef DROPBEAR_ECDSA
 #ifdef DROPBEAR_ECC_256
 		case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
 			return (void**)&key->ecckey256;
@@ -118,6 +119,7 @@
 		case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
 			return (void**)&key->ecckey521;
 #endif
+#endif /* DROPBEAR_ECDSA */
 #ifdef DROPBEAR_RSA
 		case DROPBEAR_SIGNKEY_RSA:
 			return (void**)&key->rsakey;
--- a/svr-auth.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/svr-auth.c	Fri Jul 25 22:23:50 2014 +0800
@@ -392,8 +392,7 @@
 	/* authdone must be set after encrypt_packet() for 
 	 * delayed-zlib mode */
 	ses.authstate.authdone = 1;
-	ses.connect_time = 0;
-
+	svr_ses.connect_time = 0;
 
 	if (ses.authstate.pw_uid == 0) {
 		ses.allowprivport = 1;
--- a/svr-chansession.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/svr-chansession.c	Fri Jul 25 22:23:50 2014 +0800
@@ -253,6 +253,8 @@
 	chansess->agentdir = NULL;
 #endif
 
+	channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
+
 	return 0;
 
 }
@@ -668,8 +670,11 @@
 
 	if (chansess->term == NULL) {
 		/* no pty */
-		set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK);
 		ret = noptycommand(channel, chansess);
+		if (ret == DROPBEAR_SUCCESS) {
+			channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
+			update_channel_prio();
+		}
 	} else {
 		/* want pty */
 		ret = ptycommand(channel, chansess);
--- a/svr-session.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/svr-session.c	Fri Jul 25 22:23:50 2014 +0800
@@ -80,12 +80,22 @@
 	svr_pubkey_options_cleanup();
 }
 
+static void
+svr_sessionloop() {
+	if (svr_ses.connect_time != 0 
+		&& monotonic_now() - svr_ses.connect_time >= AUTH_TIMEOUT) {
+		dropbear_close("Timeout before auth");
+	}
+}
+
 void svr_session(int sock, int childpipe) {
 	char *host, *port;
 	size_t len;
 
 	common_session_init(sock, sock);
 
+	svr_ses.connect_time = monotonic_now();;
+
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
 #ifdef USE_VFORK
@@ -95,8 +105,6 @@
 	chaninitialise(svr_chantypes);
 	svr_chansessinitialise();
 
-	ses.connect_time = time(NULL);
-
 	/* for logging the remote address */
 	get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
 	len = strlen(host) + strlen(port) + 2;
@@ -128,7 +136,7 @@
 
 	/* Run the main for loop. NULL is for the dispatcher - only the client
 	 * code makes use of it */
-	session_loop(NULL);
+	session_loop(svr_sessionloop);
 
 	/* Not reached */
 
--- a/svr-tcpfwd.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/svr-tcpfwd.c	Fri Jul 25 22:23:50 2014 +0800
@@ -34,14 +34,6 @@
 #include "runopts.h"
 #include "auth.h"
 
-static void send_msg_request_failure();
-
-static void send_msg_request_failure() {
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
-	encrypt_packet();
-}
-
 #ifndef ENABLE_SVR_REMOTETCPFWD
 
 /* This is better than SSH_MSG_UNIMPLEMENTED */
@@ -53,7 +45,6 @@
 /* */
 #endif /* !ENABLE_SVR_REMOTETCPFWD */
 
-static void send_msg_request_success();
 static int svr_cancelremotetcp();
 static int svr_remotetcpreq();
 static int newtcpdirect(struct Channel * channel);
@@ -62,7 +53,7 @@
 static const struct ChanType svr_chan_tcpremote = {
 	1, /* sepfds */
 	"forwarded-tcpip",
-	NULL,
+	tcp_prio_inithandler,
 	NULL,
 	NULL,
 	NULL
@@ -115,15 +106,6 @@
 	TRACE(("leave recv_msg_global_request"))
 }
 
-
-static void send_msg_request_success() {
-
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
-	encrypt_packet();
-
-}
-
 static int matchtcp(void* typedata1, void* typedata2) {
 
 	const struct TCPListener *info1 = (struct TCPListener*)typedata1;
@@ -258,6 +240,8 @@
 	int len;
 	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
 
+	TRACE(("newtcpdirect channel %d", channel->index))
+
 	if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
 		TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
 		goto out;
@@ -299,6 +283,8 @@
 	 * progress succeeds */
 	channel->writefd = sock;
 	channel->initconn = 1;
+
+	channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
 	
 	err = SSH_OPEN_IN_PROGRESS;
 
--- a/svr-x11fwd.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/svr-x11fwd.c	Fri Jul 25 22:23:50 2014 +0800
@@ -182,10 +182,15 @@
 	}
 }
 
+static int x11_inithandler(struct Channel *channel) {
+	channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
+	return 0;
+}
+
 static const struct ChanType chan_x11 = {
 	0, /* sepfds */
 	"x11",
-	NULL, /* inithandler */
+	x11_inithandler, /* inithandler */
 	NULL, /* checkclose */
 	NULL, /* reqhandler */
 	NULL /* closehandler */
--- a/sysoptions.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/sysoptions.h	Fri Jul 25 22:23:50 2014 +0800
@@ -251,4 +251,10 @@
 #define USE_VFORK
 #endif  /* don't HAVE_FORK */
 
+#if MAX_UNAUTH_CLIENTS > MAX_CHANNELS
+#define DROPBEAR_LISTEN_BACKLOG MAX_UNAUTH_CLIENTS
+#else
+#define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS
+#endif
+
 /* no include guard for this file */
--- a/tcp-accept.c	Sat Mar 08 21:02:02 2014 +0800
+++ b/tcp-accept.c	Fri Jul 25 22:23:50 2014 +0800
@@ -30,6 +30,7 @@
 #include "buffer.h"
 #include "packet.h"
 #include "listener.h"
+#include "listener.h"
 #include "runopts.h"
 
 #ifdef DROPBEAR_TCP_ACCEPT
@@ -44,6 +45,13 @@
 	m_free(tcpinfo);
 }
 
+int tcp_prio_inithandler(struct Channel* channel)
+{
+	TRACE(("tcp_prio_inithandler channel %d", channel->index))
+	channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
+	return 0;
+}
+
 static void tcp_acceptor(struct Listener *listener, int sock) {
 
 	int fd;
--- a/tcpfwd.h	Sat Mar 08 21:02:02 2014 +0800
+++ b/tcpfwd.h	Fri Jul 25 22:23:50 2014 +0800
@@ -70,5 +70,8 @@
 
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);
+int tcp_prio_inithandler(struct Channel* chan);
+
+#define CHANNEL_ID_TCPFORWARDED 'tcpf'
 
 #endif