changeset 989:73ea0dce9a57 pam

Merge up to date
author Matt Johnston <matt@ucc.asn.au>
date Fri, 23 Jan 2015 21:38:47 +0800
parents bae0b34bc059 (current diff) 6c0fb5428aaa (diff)
children 9a6395ddb1b6
files auth.h cli-session.c common-session.c options.h process-packet.c session.h svr-auth.c svr-session.c sysoptions.h
diffstat 46 files changed, 750 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Wed Mar 12 23:40:02 2014 +0800
+++ b/.hgsigs	Fri Jan 23 21:38:47 2015 +0800
@@ -10,3 +10,6 @@
 9ec083a21adfcb099f21eb03704b66d14a4ba800 0 iEYEABECAAYFAlKE4JoACgkQjPn4sExkf7wLDgCghkVGwMjI138bEv+ORVzN7zIH7cEAoLckaxZc1k1aXlmlSCRlP8cuKH3o
 3d1d7d151c0ce3a79da62e86463f5632fa2b144a 0 iEYEABECAAYFAlKd5AEACgkQjPn4sExkf7wzWgCfdvPEEIdlMPqcbOQMJ7b+eAyy164An2ip1lPh1eS5g26/gSfruvWBVym4
 277429102f1337bd10c89107d3e01de509cc1a7e 0 iEYEABECAAYFAlMEvF4ACgkQjPn4sExkf7xeVQCgtbxJ4G3hsFwUOM0K1WGr1J2vsbEAoMM8dEyr1mdrbgO1tzNLfD1nxbyn
+96584b934d04ebab443f603e78d38fe692d36313 0 iEYEABECAAYFAlPVFrQACgkQjPn4sExkf7xr6ACglRiLE21vRrS1rJ809o2yMADIKtwAn1f5SyZUngSde8eE55JxCMwtMC5m
+caac692b366c153cea0e9cd59aa2d79a7d843d4e 0 iEYEABECAAYFAlPk1mcACgkQjPn4sExkf7wLpgCeOqMYqpkf4lYUuyrn9VYThNpc7PkAn3JOSNgIqkKUcmSy6FstrI8jwJzq
+2d421bc0545d1be6d59a4ebfe61606d94b124b0c 0 iEYEABECAAYFAlRJDCQACgkQjPn4sExkf7xUYACcCwVJkYWXJn5x/D5A+qMupy778lEAn0rg1oNiq96YU/4jOPsS5IMItihu
--- a/.hgtags	Wed Mar 12 23:40:02 2014 +0800
+++ b/.hgtags	Fri Jan 23 21:38:47 2015 +0800
@@ -43,3 +43,6 @@
 e894dbc015ba7ff4c3bf897ee20e28ca90c55a16 DROPBEAR_2013.61test
 3d1d7d151c0ce3a79da62e86463f5632fa2b144a DROPBEAR_2013.62
 2351b2da8e0d08dcc6e64fcc328b53b9630bda68 DROPBEAR_2014.63
+0d2d39957c029adb7f4327d37fe6b4900f0736d9 DROPBEAR_2014.64
+e9579816f20ea85affc6135e87f8477992808948 DROPBEAR_2014.65
+735511a4c761141416ad0e6728989d2dafa55bc2 DROPBEAR_2014.66
--- a/CHANGES	Wed Mar 12 23:40:02 2014 +0800
+++ b/CHANGES	Fri Jan 23 21:38:47 2015 +0800
@@ -1,3 +1,61 @@
+2014.66 - Thursday 23 October 2014
+
+- Use the same keepalive handling behaviour as OpenSSH. This will work better 
+  with some SSH implementations that have different behaviour with unknown 
+  message types.
+
+- Don't reply with SSH_MSG_UNIMPLEMENTED when we receive a reply to our own 
+  keepalive message
+
+- Set $SSH_CLIENT to keep bash happy, patch from Ryan Cleere
+
+- Fix wtmp which broke since 2013.62, patch from Whoopie
+
+2014.65 - Friday 8 August 2014
+
+- Fix 2014.64 regression, server session hang on exit with scp (and probably
+  others), thanks to NiLuJe for tracking it down
+
+- Fix 2014.64 regression, clock_gettime() error handling which broke on older
+  Linux kernels, reported by NiLuJe
+
+- Fix 2014.64 regression, writev() could occassionally fail with EAGAIN which
+  wasn't caught
+
+- Avoid error message when trying to set QoS on proxycommand or multihop pipes
+
+- Use /usr/bin/xauth, thanks to Mike Frysinger
+
+- Don't exit the client if the local user entry can't be found, thanks to iquaba
+
+2014.64 - Sunday 27 July 2014
+
+- 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
+
+- Add -V for version
+
 2014.63 - Wednesday 19 February 2014
 
 - Fix ~. to terminate a client interactive session after waking a laptop
--- a/LICENSE	Wed Mar 12 23:40:02 2014 +0800
+++ b/LICENSE	Fri Jan 23 21:38:47 2015 +0800
@@ -8,7 +8,7 @@
 Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
 same license:
 
-Copyright (c) 2002-2013 Matt Johnston
+Copyright (c) 2002-2014 Matt Johnston
 Portions copyright (c) 2004 Mihnea Stoenescu
 All rights reserved.
 
--- a/auth.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/auth.h	Fri Jan 23 21:38:47 2015 +0800
@@ -108,7 +108,7 @@
 								valid */
 	unsigned int failcount; /* Number of (failed) authentication attempts.*/
 	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
-							  client and server (though has differing [obvious]
+							  client and server (though has differing 
 							  meanings). */
 	unsigned perm_warn : 1; /* Server only, set if bad permissions on 
 							   ~/.ssh/authorized_keys have already been
--- a/channel.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/channel.h	Fri Jan 23 21:38:47 2015 +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[]);
@@ -105,6 +105,9 @@
 void setchannelfds(fd_set *readfd, fd_set *writefd);
 void channelio(fd_set *readfd, fd_set *writefd);
 struct Channel* getchannel();
+/* Returns an arbitrary channel that is in a ready state - not
+being initialised and no EOF in either direction. NULL if none. */
+struct Channel* get_any_ready_channel();
 
 void recv_msg_channel_open();
 void recv_msg_channel_request();
@@ -128,5 +131,10 @@
 void recv_msg_channel_open_confirmation();
 void recv_msg_channel_open_failure();
 #endif
+void start_send_channel_request(struct Channel *channel, unsigned char *type);
+
+void send_msg_request_success();
+void send_msg_request_failure();
+
 
 #endif /* _CHANNEL_H_ */
--- a/chansession.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/chansession.h	Fri Jan 23 21:38:47 2015 +0800
@@ -51,9 +51,12 @@
 	/* exit details */
 	struct exitinfo exit;
 
-	/* Used to set $SSH_CONNECTION in the child session. 
-	Is only set temporarily before forking */
+
+	/* These are only set temporarily before forking */
+	/* Used to set $SSH_CONNECTION in the child session.  */
 	char *connection_string;
+	/* Used to set $SSH_CLIENT in the child session. */
+	char *client_string;
 	
 #ifndef DISABLE_X11FWD
 	struct Listener * x11listener;
@@ -89,7 +92,6 @@
 #ifdef ENABLE_CLI_NETCAT
 void cli_send_netcat_request();
 #endif
-void cli_start_send_channel_request(struct Channel *channel, unsigned char *type);
 
 void svr_chansessinitialise();
 extern const struct ChanType svrchansess;
--- a/cli-agentfwd.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-agentfwd.c	Fri Jan 23 21:38:47 2015 +0800
@@ -210,13 +210,14 @@
 		ret = buf_get_pub_key(key_buf, pubkey, &key_type);
 		buf_free(key_buf);
 		if (ret != DROPBEAR_SUCCESS) {
-			/* This is slack, properly would cleanup vars etc */
-			dropbear_exit("Bad pubkey received from agent");
+			TRACE(("Skipping bad/unknown type pubkey from agent"));
+			sign_key_free(pubkey);
+		} else {
+			pubkey->type = key_type;
+			pubkey->source = SIGNKEY_SOURCE_AGENT;
+
+			list_append(ret_list, pubkey);
 		}
-		pubkey->type = key_type;
-		pubkey->source = SIGNKEY_SOURCE_AGENT;
-
-		list_append(ret_list, pubkey);
 
 		/* We'll ignore the comment for now. might want it later.*/
 		buf_eatstring(inbuf);
@@ -234,7 +235,7 @@
 		return;
 	}
 	
-	cli_start_send_channel_request(channel, "[email protected]");
+	start_send_channel_request(channel, "[email protected]");
 	/* Don't want replies */
 	buf_putbyte(ses.writepayload, 0);
 	encrypt_packet();
--- a/cli-auth.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-auth.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-chansession.c	Fri Jan 23 21:38:47 2015 +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();
 
@@ -92,17 +92,6 @@
 	}
 }
 
-void cli_start_send_channel_request(struct Channel *channel, 
-		unsigned char *type) {
-
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
-	buf_putint(ses.writepayload, channel->remotechan);
-
-	buf_putstring(ses.writepayload, type, strlen(type));
-
-}
-
 /* Taken from OpenSSH's sshtty.c:
  * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
 static void cli_tty_setup() {
@@ -287,7 +276,7 @@
 
 	TRACE(("enter send_chansess_pty_req"))
 
-	cli_start_send_channel_request(channel, "pty-req");
+	start_send_channel_request(channel, "pty-req");
 
 	/* Don't want replies */
 	buf_putbyte(ses.writepayload, 0);
@@ -330,7 +319,7 @@
 		reqtype = "shell";
 	}
 
-	cli_start_send_channel_request(channel, reqtype);
+	start_send_channel_request(channel, reqtype);
 
 	/* XXX TODO */
 	buf_putbyte(ses.writepayload, 0); /* Don't want replies */
@@ -357,6 +346,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 +363,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 +384,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-main.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-runopts.c	Fri Jan 23 21:38:47 2015 +0800
@@ -90,6 +90,7 @@
 					"-c <cipher list> Specify preferred ciphers ('-c help' to list options)\n"
 					"-m <MAC list> Specify preferred MACs for packet verification (or '-m help')\n"
 #endif
+					"-V    Version\n"
 #ifdef DEBUG_TRACE
 					"-v    verbose (compiled with DEBUG_TRACE)\n"
 #endif
@@ -163,6 +164,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();
 
@@ -322,6 +325,10 @@
 #ifndef ENABLE_CLI_LOCALTCPFWD
 				case 'L':
 #endif
+				case 'V':
+					print_version();
+					exit(EXIT_SUCCESS);
+					break;
 				case 'o':
 				case 'b':
 					next = &dummy;
@@ -676,11 +683,13 @@
 	uid = getuid();
 
 	pw = getpwuid(uid);
-	if (pw == NULL || pw->pw_name == NULL) {
-		dropbear_exit("Unknown own user");
+	if (pw && pw->pw_name != NULL) {
+		cli_opts.own_user = m_strdup(pw->pw_name);
+	} else {
+		dropbear_log(LOG_INFO, "Warning: failed to identify current user. Trying anyway.");
+		cli_opts.own_user = m_strdup("unknown");
 	}
 
-	cli_opts.own_user = m_strdup(pw->pw_name);
 }
 
 #ifdef ENABLE_CLI_ANYTCPFWD
--- a/cli-session.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-session.c	Fri Jan 23 21:38:47 2015 +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,9 +69,16 @@
 	{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},
+	{SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response},
+	{SSH_MSG_CHANNEL_FAILURE, ignore_recv_response},
 #ifdef  ENABLE_CLI_REMOTETCPFWD
 	{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
 	{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
+#else
+	/* For keepalive */
+	{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
+	{SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
 #endif
 	{0, 0} /* End */
 };
@@ -367,3 +375,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/cli-tcpfwd.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/common-channel.c	Fri Jan 23 21:38:47 2015 +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,28 @@
 							 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;
+		}
+
+		if (ses.channel_signal_pending) {
+			/* SIGCHLD can change channel state for server sessions */
+			do_check_close = 1;
+			ses.channel_signal_pending = 0;
 		}
 	
 		/* 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 +582,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 +603,8 @@
 	m_free(channel);
 	ses.chancount--;
 
+	update_channel_prio();
+
 	TRACE(("leave remove_channel"))
 }
 
@@ -608,7 +627,12 @@
 			&& !channel->close_handler_done) {
 		channel->type->reqhandler(channel);
 	} else {
-		send_msg_channel_failure(channel);
+		int wantreply;
+		buf_eatstring(ses.payload);
+		wantreply = buf_getbool(ses.payload);
+		if (wantreply) {
+			send_msg_channel_failure(channel);
+		}
 	}
 
 	TRACE(("leave recv_msg_channel_request"))
@@ -876,6 +900,10 @@
 		}
 	}
 
+	if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
+		channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
+	}
+
 	chan_initwritebuf(channel);
 
 	/* success */
@@ -889,6 +917,8 @@
 
 cleanup:
 	m_free(type);
+	
+	update_channel_prio();
 
 	TRACE(("leave recv_msg_channel_open"))
 }
@@ -1004,7 +1034,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;
@@ -1070,9 +1100,14 @@
 		if (ret > 0) {
 			remove_channel(channel);
 			TRACE(("inithandler returned failure %d", ret))
+			return;
 		}
 	}
 
+	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 +1127,42 @@
 	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();
+}
+
+struct Channel* get_any_ready_channel() {
+	if (ses.chancount == 0) {
+		return NULL;
+	}
+	size_t i;
+	for (i = 0; i < ses.chansize; i++) {
+		struct Channel *chan = ses.channels[i];
+		if (chan
+				&& !(chan->sent_eof || chan->recv_eof)
+				&& !(chan->await_open || chan->initconn)) {
+			return chan;
+		}
+	}
+	return NULL;
+}
+
+void start_send_channel_request(struct Channel *channel, 
+		unsigned char *type) {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	buf_putstring(ses.writepayload, type, strlen(type));
+
+}
--- a/common-kex.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/common-kex.c	Fri Jan 23 21:38:47 2015 +0800
@@ -270,7 +270,7 @@
 
 	ses.kexstate.our_first_follows_matches = 0;
 
-	ses.kexstate.lastkextime = time(NULL);
+	ses.kexstate.lastkextime = monotonic_now();
 
 }
 
@@ -303,7 +303,7 @@
 		hash_desc->done(&hs2, tmpout);
 		memcpy(&out[offset], tmpout, MIN(outlen - offset, hash_desc->hashsize));
 	}
-
+	m_burn(&hs2, sizeof(hash_state));
 }
 
 /* Generate the actual encryption/integrity keys, using the results of the
@@ -403,6 +403,7 @@
 	m_burn(C2S_key, sizeof(C2S_key));
 	m_burn(S2C_IV, sizeof(S2C_IV));
 	m_burn(S2C_key, sizeof(S2C_key));
+	m_burn(&hs, sizeof(hash_state));
 
 	TRACE(("leave gen_new_keys"))
 }
@@ -798,6 +799,7 @@
 
 	buf_burn(ses.kexhashbuf);
 	buf_free(ses.kexhashbuf);
+	m_burn(&hs, sizeof(hash_state));
 	ses.kexhashbuf = NULL;
 	
 	/* first time around, we set the session_id to H */
@@ -805,7 +807,6 @@
 		/* create the session_id, this never needs freeing */
 		ses.session_id = buf_newcopy(ses.hash);
 	}
-
 }
 
 /* read the other side's algo list. buf_match_algo is a callback to match
--- a/common-runopts.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/common-runopts.c	Fri Jan 23 21:38:47 2015 +0800
@@ -106,3 +106,8 @@
 }
 #endif
 
+void print_version() {
+	fprintf(stderr, "Dropbear v%s\n", DROPBEAR_VERSION);
+}
+
+
--- a/common-session.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/common-session.c	Fri Jan 23 21:38:47 2015 +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");
@@ -189,13 +196,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) {
@@ -221,8 +222,21 @@
 		 * during rekeying ) */
 		channelio(&readfd, &writefd);
 
+<<<<<<< local
 		if (ses.loop_handler) {
 			ses.loop_handler();
+=======
+		/* process session socket's outgoing data */
+		if (ses.sock_out != -1) {
+			if (!isempty(&ses.writequeue)) {
+				write_packet();
+			}
+		}
+
+
+		if (loophandler) {
+			loophandler();
+>>>>>>> other
 		}
 
 	} /* for(;;) */
@@ -388,11 +402,37 @@
 	return pos+1;
 }
 
-void send_msg_ignore() {
+void ignore_recv_response() {
+	// Do nothing
+	TRACE(("Ignored msg_request_response"))
+}
+
+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;
+
+	struct Channel *chan = get_any_ready_channel();
+
+	if (chan) {
+		/* Channel requests are preferable, more implementations
+		handle them than SSH_MSG_GLOBAL_REQUEST */
+		TRACE(("keepalive channel request %d", chan->index))
+		start_send_channel_request(chan, DROPBEAR_KEEPALIVE_STRING);
+	} else {
+		TRACE(("keepalive global request"))
+		/* Some peers will reply with SSH_MSG_REQUEST_FAILURE, 
+		some will reply with SSH_MSG_UNIMPLEMENTED, some will exit. */
+		buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); 
+		buf_putstring(ses.writepayload, DROPBEAR_KEEPALIVE_STRING,
+			strlen(DROPBEAR_KEEPALIVE_STRING));
+	}
+	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
@@ -400,13 +440,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;
@@ -419,13 +454,30 @@
 		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 && ses.authstate.authdone) {
+		/* Avoid sending keepalives prior to auth - those are
+		not valid pre-auth packet types */
+
+		/* 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");
 	}
 }
@@ -436,12 +488,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;
 }
 
@@ -490,3 +543,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/configure.ac	Fri Jan 23 21:38:47 2015 +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/dbclient.1	Wed Mar 12 23:40:02 2014 +0800
+++ b/dbclient.1	Fri Jan 23 21:38:47 2015 +0800
@@ -19,8 +19,7 @@
 
 .SH DESCRIPTION
 .B dbclient
-is a SSH client designed to be small enough to be used in small memory
-environments, while still being functional and secure enough for general use.
+is a small SSH client 
 .SH OPTIONS
 .TP
 .B \-p \fIport
@@ -98,7 +97,7 @@
 useful for working around firewalls or routers that drop connections after
 a certain period of inactivity. The trade-off is that a session may be
 closed if there is a temporary lapse of network connectivity. A setting
-if 0 disables keepalives.
+if 0 disables keepalives. If no response is received for 3 consecutive keepalives the connection will be closed.
 .TP
 .B \-I \fIidle_timeout
 Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
@@ -121,6 +120,9 @@
 .TP
 .B \-s 
 The specified command will be requested as a subsystem, used for sftp. Dropbear doesn't implement sftp itself but the OpenSSH sftp client can be used eg \fIsftp -S dbclient user@host\fR
+.TP
+.B \-V
+Print the version
 
 .SH MULTI-HOP
 Dropbear will also allow multiple "hops" to be specified, separated by commas. In
--- a/dbutil.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/dbutil.c	Fri Jan 23 21:38:47 2015 +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"
@@ -148,7 +161,7 @@
 	gettimeofday(&tv, NULL);
 
 	va_start(param, format);
-	fprintf(stderr, "TRACE  (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec);
+	fprintf(stderr, "TRACE  (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec);
 	vfprintf(stderr, format, param);
 	fprintf(stderr, "\n");
 	va_end(param);
@@ -170,7 +183,7 @@
 	gettimeofday(&tv, NULL);
 
 	va_start(param, format);
-	fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec);
+	fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec);
 	vfprintf(stderr, format, param);
 	fprintf(stderr, "\n");
 	va_end(param);
@@ -189,6 +202,9 @@
 
 	int iptos_val = 0, so_prio_val = 0, rc;
 
+	/* Don't log ENOTSOCK errors so that this can harmlessly be called
+	 * on a client '-J' proxy pipe */
+
 	/* set the TOS bit for either ipv4 or ipv6 */
 #ifdef IPTOS_LOWDELAY
 	if (prio == DROPBEAR_PRIO_LOWDELAY) {
@@ -198,12 +214,12 @@
 	}
 #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
 	rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val));
-	if (rc < 0) {
+	if (rc < 0 && errno != ENOTSOCK) {
 		TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno)));
 	}
 #endif
 	rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val));
-	if (rc < 0) {
+	if (rc < 0 && errno != ENOTSOCK) {
 		TRACE(("Couldn't set IP_TOS (%s)", strerror(errno)));
 	}
 #endif
@@ -216,7 +232,7 @@
 	}
 	/* linux specific, sets QoS class. see tc-prio(8) */
 	rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val));
-	if (rc < 0)
+	if (rc < 0 && errno != ENOTSOCK)
 		dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)",
 				strerror(errno));
 #endif
@@ -319,7 +335,7 @@
 			continue;
 		}
 
-		if (listen(sock, 20) < 0) {
+		if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) {
 			err = errno;
 			close(sock);
 			TRACE(("listen() failed"))
@@ -932,3 +948,56 @@
 	return c;
 }
 
+#if defined(__linux__) && defined(SYS_clock_gettime)
+/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to
+reach userspace include headers */
+#ifndef CLOCK_MONOTONIC_COARSE
+#define CLOCK_MONOTONIC_COARSE 6
+#endif
+static clockid_t get_linux_clock_source() {
+	struct timespec ts;
+	if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) {
+		return CLOCK_MONOTONIC_COARSE;
+	}
+
+	if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) {
+		return CLOCK_MONOTONIC;
+	}
+	return -1;
+}
+#endif 
+
+time_t monotonic_now() {
+#if defined(__linux__) && defined(SYS_clock_gettime)
+	static clockid_t clock_source = -2;
+
+	if (clock_source == -2) {
+		/* First run, find out which one works. 
+		-1 will fall back to time() */
+		clock_source = get_linux_clock_source();
+	}
+
+	if (clock_source >= 0) {
+		struct timespec ts;
+		if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) {
+			/* Intermittent clock failures should not happen */
+			dropbear_exit("Clock broke");
+		}
+		return ts.tv_sec;
+	}
+#endif /* linux clock_gettime */
+
+#if 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;
+#endif /* osx mach_absolute_time */
+
+	/* Fallback for everything else - this will sometimes go backwards */
+	return time(NULL);
+}
+
--- a/dbutil.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/dbutil.h	Fri Jan 23 21:38:47 2015 +0800
@@ -62,9 +62,9 @@
 #endif
 
 enum dropbear_prio {
-	DROPBEAR_PRIO_DEFAULT,
-	DROPBEAR_PRIO_LOWDELAY,
-	DROPBEAR_PRIO_BULK,
+	DROPBEAR_PRIO_DEFAULT = 10,
+	DROPBEAR_PRIO_LOWDELAY = 11,
+	DROPBEAR_PRIO_BULK = 12,
 };
 
 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/debian/changelog	Wed Mar 12 23:40:02 2014 +0800
+++ b/debian/changelog	Fri Jan 23 21:38:47 2015 +0800
@@ -1,3 +1,21 @@
+dropbear (2014.66-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Thu, 23 Oct 2014 22:54:00 +0800
+
+dropbear (2014.65-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Fri, 8 Aug 2014 22:54:00 +0800
+
+dropbear (2014.64-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Sun, 27 Jul 2014 22:54:00 +0800
+
 dropbear (2014.63-0.1) unstable; urgency=low
 
   * New upstream release.
--- a/dropbear.8	Wed Mar 12 23:40:02 2014 +0800
+++ b/dropbear.8	Fri Jan 23 21:38:47 2015 +0800
@@ -10,8 +10,7 @@
 .IR [address:]port ]
 .SH DESCRIPTION
 .B dropbear
-is a SSH server designed to be small enough to be used in small memory
-environments, while still being functional and secure enough for general use.
+is a small SSH server 
 .SH OPTIONS
 .TP
 .B \-b \fIbanner
@@ -88,10 +87,14 @@
 useful for working around firewalls or routers that drop connections after
 a certain period of inactivity. The trade-off is that a session may be
 closed if there is a temporary lapse of network connectivity. A setting
-if 0 disables keepalives.
+if 0 disables keepalives. If no response is received for 3 consecutive keepalives the connection will be closed.
 .TP
 .B \-I \fIidle_timeout
 Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
+.TP
+.B \-V
+Print the version
+
 .SH FILES
 
 .TP
--- a/gensignkey.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/gensignkey.c	Fri Jan 23 21:38:47 2015 +0800
@@ -41,6 +41,9 @@
 
 out:
 	if (fd >= 0) {
+		if (fsync(fd) != 0) {
+			dropbear_log(LOG_ERR, "fsync of %s failed: %s", filename, strerror(errno));
+		}
 		m_close(fd);
 	}
 	return ret;
--- a/keyimport.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/keyimport.c	Fri Jan 23 21:38:47 2015 +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/loginrec.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/loginrec.h	Fri Jan 23 21:38:47 2015 +0800
@@ -79,10 +79,10 @@
 #  if defined(HAVE_UTMP_H) && defined(UTMP_FILE) && !defined(DISABLE_UTMP)
 #    define USE_UTMP
 #  endif
-#  if defined(HAVE_WTMPX_H) && defined(WTMPX_FILE) && !defined(DISABLE_WTMPX)
+#  if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX)
 #    define USE_WTMPX
 #  endif
-#  if defined(HAVE_WTMP_H) && defined(WTMP_FILE) && !defined(DISABLE_WTMP)
+#  if defined(WTMP_FILE) && !defined(DISABLE_WTMP)
 #    define USE_WTMP
 #  endif
 
--- a/options.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/options.h	Fri Jan 23 21:38:47 2015 +0800
@@ -123,8 +123,8 @@
  * which are not the standard form. */
 #define DROPBEAR_SHA1_HMAC
 #define DROPBEAR_SHA1_96_HMAC
-/*#define DROPBEAR_SHA2_256_HMAC*/
-/*#define DROPBEAR_SHA2_512_HMAC*/
+#define DROPBEAR_SHA2_256_HMAC
+#define DROPBEAR_SHA2_512_HMAC
 #define DROPBEAR_MD5_HMAC
 
 /* You can also disable integrity. Don't bother disabling this if you're
@@ -257,7 +257,7 @@
 /* The command to invoke for xauth when using X11 forwarding.
  * "-q" for quiet */
 #ifndef XAUTH_COMMAND
-#define XAUTH_COMMAND "/usr/bin/X11/xauth -q"
+#define XAUTH_COMMAND "/usr/bin/xauth -q"
 #endif
 
 /* if you want to enable running an sftp server (such as the one included with
@@ -301,6 +301,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/packet.c	Fri Jan 23 21:38:47 2015 +0800
@@ -57,42 +57,55 @@
 
 	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);
+	/* This may return EAGAIN. The main loop sometimes
+	calls write_packet() without bothering to test with select() since
+	it's likely to be necessary */
+	written = writev(ses.sock_out, iov, iov_max_count);
 	if (written < 0) {
-		if (errno == EINTR) {
+		if (errno == EINTR || errno == EAGAIN) {
 			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 +126,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);
 
@@ -127,14 +139,13 @@
 	written = write(ses.sock_out, buf_getptr(writebuf, len), len);
 
 	if (written < 0) {
-		if (errno == EINTR) {
+		if (errno == EINTR || errno == EAGAIN) {
 			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 +160,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"))
 }
@@ -254,7 +258,7 @@
 		ses.remoteclosed();
 	}
 	if (slen < 0) {
-		if (errno == EINTR) {
+		if (errno == EINTR || errno == EAGAIN) {
 			TRACE2(("leave read_packet_init: EINTR"))
 			return DROPBEAR_FAILURE;
 		}
@@ -503,6 +507,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 +616,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/process-packet.c	Fri Jan 23 21:38:47 2015 +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) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/release.sh	Fri Jan 23 21:38:47 2015 +0800
@@ -0,0 +1,40 @@
+#!/bin/sh
+VERSION=$(echo '#include "sysoptions.h"\necho DROPBEAR_VERSION' | cpp - | sh)
+echo Releasing version "$VERSION" ...
+if ! head -n1 CHANGES | grep -q $VERSION ; then
+	echo "CHANGES needs updating"
+	exit 1
+fi
+
+if ! head -n1 debian/changelog | grep -q $VERSION ; then
+	echo "CHANGES needs updating"
+	exit 1
+fi
+
+head -n1 CHANGES
+
+#sleep 3
+
+RELDIR=$PWD/../dropbear-$VERSION
+ARCHIVE=${RELDIR}.tar.bz2
+if test -e $RELDIR; then
+	echo "$RELDIR exists"
+	exit 1
+fi
+
+if test -e $ARCHIVE; then
+	echo "$ARCHIVE exists"
+	exit 1
+fi
+
+hg archive "$RELDIR"  || exit 2
+
+(cd "$RELDIR" && autoconf && autoheader) || exit 2
+
+rm -r "$RELDIR/autom4te.cache" || exit 2
+
+(cd $RELDIR/.. && tar cjf $ARCHIVE `basename "$RELDIR"`) || exit 2
+
+ls -l $ARCHIVE
+openssl sha1 $ARCHIVE
+echo "Done to $ARCHIVE"
--- a/runopts.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/runopts.h	Fri Jan 23 21:38:47 2015 +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
@@ -164,4 +164,6 @@
 void parse_ciphers_macs();
 #endif
 
+void print_version(void);
+
 #endif /* _RUNOPTS_H_ */
--- a/scp.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/scp.c	Fri Jan 23 21:38:47 2015 +0800
@@ -1146,7 +1146,7 @@
 {
 	(void) fprintf(stderr,
 	    "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
-	    "           [-l limit] [-o ssh_option] [-P port] [-S program]\n"
+	    "           [-l limit] [-P port] [-S program]\n"
 	    "           [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
 	exit(1);
 }
--- a/session.h	Wed Mar 12 23:40:02 2014 +0800
+++ b/session.h	Fri Jan 23 21:38:47 2015 +0800
@@ -47,6 +47,9 @@
 void session_cleanup();
 void send_session_identification();
 void send_msg_ignore();
+void ignore_recv_response();
+
+void update_channel_prio();
 
 const char* get_user_shell();
 void fill_passwd(const char* username);
@@ -104,10 +107,6 @@
 	/* Is it a client or server? */
 	unsigned int 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;
 
@@ -150,11 +149,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 */
@@ -191,8 +193,11 @@
 	unsigned int chansize; /* the number of Channel*s allocated for channels */
 	unsigned int chancount; /* the number of Channel*s in use */
 	const struct ChanType **chantypes; /* The valid channel types */
+	int channel_signal_pending; /* Flag set by sigchld handler */
 
-	
+	/* 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;
@@ -222,6 +227,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/signkey.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-auth.c	Fri Jan 23 21:38:47 2015 +0800
@@ -401,8 +401,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-chansession.c	Fri Jan 23 21:38:47 2015 +0800
@@ -53,6 +53,7 @@
 static void closechansess(struct Channel *channel);
 static int newchansess(struct Channel *channel);
 static void chansessionrequest(struct Channel *channel);
+static int sesscheckclose(struct Channel *channel);
 
 static void send_exitsignalstatus(struct Channel *channel);
 static void send_msg_chansess_exitstatus(struct Channel * channel,
@@ -61,6 +62,14 @@
 		struct ChanSess * chansess);
 static void get_termmodes(struct ChanSess *chansess);
 
+const struct ChanType svrchansess = {
+	0, /* sepfds */
+	"session", /* name */
+	newchansess, /* inithandler */
+	sesscheckclose, /* checkclosehandler */
+	chansessionrequest, /* reqhandler */
+	closechansess, /* closehandler */
+};
 
 /* required to clear environment */
 extern char** environ;
@@ -89,6 +98,9 @@
 
 	const int saved_errno = errno;
 
+	/* Make channel handling code look for closed channels */
+	ses.channel_signal_pending = 1;
+
 	TRACE(("enter sigchld handler"))
 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
 		TRACE(("sigchld handler: pid %d", pid))
@@ -229,6 +241,7 @@
 	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
 	chansess->cmd = NULL;
 	chansess->connection_string = NULL;
+	chansess->client_string = NULL;
 	chansess->pid = 0;
 
 	/* pty details */
@@ -253,6 +266,8 @@
 	chansess->agentdir = NULL;
 #endif
 
+	channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
+
 	return 0;
 
 }
@@ -588,19 +603,26 @@
 	return DROPBEAR_SUCCESS;
 }
 
-static char* make_connection_string() {
+static void make_connection_string(struct ChanSess *chansess) {
 	char *local_ip, *local_port, *remote_ip, *remote_port;
 	size_t len;
-	char *ret;
 	get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0);
-	len = strlen(local_ip) + strlen(local_port) + strlen(remote_ip) + strlen(remote_port) + 4;
-	ret = m_malloc(len);
-	snprintf(ret, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port);
+
+	/* "remoteip remoteport localip localport" */
+	len = strlen(local_ip) + strlen(remote_ip) + 20;
+	chansess->connection_string = m_malloc(len);
+	snprintf(chansess->connection_string, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port);
+
+	/* deprecated but bash only loads .bashrc if SSH_CLIENT is set */ 
+	/* "remoteip remoteport localport" */
+	len = strlen(remote_ip) + 20;
+	chansess->client_string = m_malloc(len);
+	snprintf(chansess->client_string, len, "%s %s %s", remote_ip, remote_port, local_port);
+
 	m_free(local_ip);
 	m_free(local_port);
 	m_free(remote_ip);
 	m_free(remote_port);
-	return ret;
 }
 
 /* Handle a command request from the client. This is used for both shell
@@ -663,13 +685,16 @@
 	/* uClinux will vfork(), so there'll be a race as 
 	connection_string is freed below. */
 #ifndef USE_VFORK
-	chansess->connection_string = make_connection_string();
+	make_connection_string(chansess);
 #endif
 
 	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);
@@ -677,6 +702,7 @@
 
 #ifndef USE_VFORK
 	m_free(chansess->connection_string);
+	m_free(chansess->client_string);
 #endif
 
 	if (ret == DROPBEAR_FAILURE) {
@@ -932,6 +958,10 @@
 	if (chansess->connection_string) {
 		addnewvar("SSH_CONNECTION", chansess->connection_string);
 	}
+
+	if (chansess->client_string) {
+		addnewvar("SSH_CLIENT", chansess->client_string);
+	}
 	
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
 	if (chansess->original_command) {
@@ -960,16 +990,6 @@
 	dropbear_exit("Child failed");
 }
 
-const struct ChanType svrchansess = {
-	0, /* sepfds */
-	"session", /* name */
-	newchansess, /* inithandler */
-	sesscheckclose, /* checkclosehandler */
-	chansessionrequest, /* reqhandler */
-	closechansess, /* closehandler */
-};
-
-
 /* Set up the general chansession environment, in particular child-exit
  * handling */
 void svr_chansessinitialise() {
--- a/svr-kex.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-kex.c	Fri Jan 23 21:38:47 2015 +0800
@@ -84,7 +84,28 @@
 	TRACE(("leave recv_msg_kexdh_init"))
 }
 
+
 #ifdef DROPBEAR_DELAY_HOSTKEY
+
+static void fsync_parent_dir(const char* fn) {
+#ifdef HAVE_LIBGEN_H
+	char *fn_dir = m_strdup(fn);
+	char *dir = dirname(fn_dir);
+	int dirfd = open(dir, O_RDONLY);
+
+	if (dirfd != -1) {
+		if (fsync(dirfd) != 0) {
+			TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
+		}
+		m_close(dirfd);
+	} else {
+		TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
+	}
+
+	free(fn_dir);
+#endif
+}
+
 static void svr_ensure_hostkey() {
 
 	const char* fn = NULL;
@@ -142,6 +163,10 @@
 		}
 	}
 
+	/* ensure directory update is flushed to disk, otherwise we can end up
+	with zero-byte hostkey files if the power goes off */
+	fsync_parent_dir(fn);
+
 	ret = readhostkey(fn, svr_opts.hostkey, &type);
 
 	if (ret == DROPBEAR_SUCCESS) {
--- a/svr-main.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-main.c	Fri Jan 23 21:38:47 2015 +0800
@@ -409,7 +409,7 @@
 	size_t sockpos = 0;
 	int nsock;
 
-	TRACE(("listensockets: %d to try\n", svr_opts.portcount))
+	TRACE(("listensockets: %d to try", svr_opts.portcount))
 
 	for (i = 0; i < svr_opts.portcount; i++) {
 
--- a/svr-runopts.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-runopts.c	Fri Jan 23 21:38:47 2015 +0800
@@ -92,6 +92,7 @@
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d, in seconds)\n"
 					"-I <idle_timeout>  (0 is never, default %d, in seconds)\n"
+					"-V    Version\n"
 #ifdef DEBUG_TRACE
 					"-v		verbose (compiled with DEBUG_TRACE)\n"
 #endif
@@ -256,7 +257,7 @@
 #endif
 				case 'h':
 					printhelp(argv[0]);
-					exit(EXIT_FAILURE);
+					exit(EXIT_SUCCESS);
 					break;
 				case 'u':
 					/* backwards compatibility with old urandom option */
@@ -266,6 +267,10 @@
 					debug_trace = 1;
 					break;
 #endif
+				case 'V':
+					print_version();
+					exit(EXIT_SUCCESS);
+					break;
 				default:
 					fprintf(stderr, "Unknown argument %s\n", argv[i]);
 					printhelp(argv[0]);
@@ -405,7 +410,9 @@
 	sign_key * read_key = new_sign_key();
 	enum signkey_type type = DROPBEAR_SIGNKEY_ANY;
 	if (readhostkey(keyfile, read_key, &type) == DROPBEAR_FAILURE) {
-		dropbear_log(LOG_WARNING, "Failed loading %s", keyfile);
+		if (!svr_opts.delay_hostkey) {
+			dropbear_log(LOG_WARNING, "Failed loading %s", keyfile);
+		}
 	}
 
 #ifdef DROPBEAR_RSA
--- a/svr-session.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-session.c	Fri Jan 23 21:38:47 2015 +0800
@@ -58,6 +58,10 @@
 	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
 	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
 	{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
+	{SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response},
+	{SSH_MSG_CHANNEL_FAILURE, ignore_recv_response},
+	{SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, /* for keepalive */
+	{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, /* client */
 #ifdef USING_LISTENERS
 	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
@@ -83,12 +87,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
@@ -98,8 +112,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;
@@ -131,7 +143,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	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-tcpfwd.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/svr-x11fwd.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/sysoptions.h	Fri Jan 23 21:38:47 2015 +0800
@@ -4,7 +4,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "2014.63"
+#define DROPBEAR_VERSION "2014.66"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
@@ -153,8 +153,7 @@
 #define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
 							connection, so can't be _too_ small */
 
-#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
-							   is the max length for a password etc */
+#define MAX_STRING_LEN 2400 /* Sun SSH needs this long for algos */
 
 /* For a 4096 bit DSS key, empirically determined */
 #define MAX_PUBKEY_SIZE 1700
@@ -249,4 +248,13 @@
 #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
+
+/* Use this string since some implementations might special-case it */
+#define DROPBEAR_KEEPALIVE_STRING "[email protected]"
+
 /* no include guard for this file */
--- a/tcp-accept.c	Wed Mar 12 23:40:02 2014 +0800
+++ b/tcp-accept.c	Fri Jan 23 21:38:47 2015 +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	Wed Mar 12 23:40:02 2014 +0800
+++ b/tcpfwd.h	Fri Jan 23 21:38:47 2015 +0800
@@ -70,5 +70,9 @@
 
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);
+int tcp_prio_inithandler(struct Channel* chan);
+
+/* A random identifier */
+#define CHANNEL_ID_TCPFORWARDED 0x43612c67
 
 #endif