diff --git a/CHANGES b/CHANGES
index eb6855c096bda905b89a870b7a09d33b5ccf4d00..7e7d73f3174ae0603f4514d825446c8e504192b4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,56 @@
+0.52 - Wed 12 November 2008
+
+- Add "netcat-alike" option (-B) to dbclient, allowing Dropbear to tunnel
+  standard input/output to a TCP port-forwarded remote host.
+
+- Add "proxy command" support to dbclient, to allow using a spawned process for
+  IO rather than a direct TCP connection. eg
+	  dbclient remotehost
+  is equivalent to
+	  dbclient -J 'nc remotehost 22' remotehost
+  (the hostname is still provided purely for looking up saved host keys)
+
+- Combine netcat-alike and proxy support to allow "multihop" connections, with
+  comma-separated host syntax.  Allows running
+
+	  dbclient user1@host1,user2@host2,user3@host3
+
+  to end up at host3 via the other two, using SSH TCP forwarding. It's a bit
+  like onion-routing. All connections are established from the local machine.
+  The comma-separated syntax can also be used for scp/rsync, eg
+
+	  rsync -a -e dbclient m@gateway,m2@host,martello:/home/matt/ ~/backup/
+
+  to bounce through a few hosts.
+
+- Add -I "idle timeout" option (contributed by Farrell Aultman)
+
+- Allow restrictions on authorized_keys logins such as restricting commands
+  to be run etc. This is a subset of those allowed by OpenSSH, doesn't
+  yet allow restricting source host.
+
+- Use vfork() for scp on uClinux
+
+- Default to PATH=/usr/bin:/bin for shells.
+
+- Report errors if -R forwarding fails
+
+- Add counter mode cipher support, which avoids some security problems with the
+  standard CBC mode.
+
+- Support zlib@openssh.com delayed compression for client/server. It can be
+  required for the Dropbear server with the '-Z' option. This is useful for
+  security as it avoids exposing the server to attacks on zlib by
+  unauthenticated remote users, though requires client side support.
+
+- options.h has been split into options.h (user-changable) and sysoptions.h
+  (less commonly changed)
+
+- Support "dbclient -s sftp" to specify a subsystem
+
+- Fix a bug in replies to channel requests that could be triggered by recent
+  versions of PuTTY
+
 0.51 - Thu 27 March 2008
 
 - Make a copy of password fields rather erroneously relying on getwpnam()
diff --git a/LICENSE b/LICENSE
index ec93fa18381a70744ed313e95db3e4a4ceaafa51..ba11d4fc49f65cf60b8bcad44595825e161ebd49 100644
--- a/LICENSE
+++ b/LICENSE
@@ -8,7 +8,7 @@ The majority of code is written by Matt Johnston, under the license below.
 Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
 same license:
 
-Copyright (c) 2002-2006 Matt Johnston
+Copyright (c) 2002-2008 Matt Johnston
 Portions copyright (c) 2004 Mihnea Stoenescu
 All rights reserved.
 
diff --git a/Makefile.in b/Makefile.in
index 3e6c8551e3a6a6892eb06613dd24dae7b4bcf60a..4c8441f3c193d1d8522466d66286fd5282575647 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -25,7 +25,7 @@ COMMONOBJS=dbutil.o buffer.o \
 SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
 		svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
-		svr-tcpfwd.o svr-authpam.o
+		svr-tcpfwd.o svr-authpam.o @CRYPTLIB@
 
 CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
 		cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
diff --git a/algo.h b/algo.h
index 5ed01cc7ea6a6576e9e52ed6106eb2193f89dbc1..c83cfff42f45f9ca63a381d312893e0949f66a9d 100644
--- a/algo.h
+++ b/algo.h
@@ -29,13 +29,18 @@
 #include "includes.h"
 #include "buffer.h"
 
+#define DROPBEAR_MODE_UNUSED 0
+#define DROPBEAR_MODE_CBC 1
+#define DROPBEAR_MODE_CTR 2
+
 struct Algo_Type {
 
 	unsigned char *name; /* identifying name */
 	char val; /* a value for this cipher, or -1 for invalid */
-	void *data; /* algorithm specific data */
-	unsigned usable : 1; /* whether we can use this algorithm */
-
+	const void *data; /* algorithm specific data */
+	char usable; /* whether we can use this algorithm */
+	const void *mode; /* the mode, currently only used for ciphers,
+						 points to a 'struct dropbear_cipher_mode' */
 };
 
 typedef struct Algo_Type algo_type;
@@ -48,6 +53,7 @@ extern algo_type sshhashes[];
 extern algo_type sshcompress[];
 
 extern const struct dropbear_cipher dropbear_nocipher;
+extern const struct dropbear_cipher_mode dropbear_mode_none;
 extern const struct dropbear_hash dropbear_nohash;
 
 struct dropbear_cipher {
@@ -56,6 +62,16 @@ struct dropbear_cipher {
 	unsigned char blocksize;
 };
 
+struct dropbear_cipher_mode {
+	int (*start)(int cipher, const unsigned char *IV, 
+			const unsigned char *key, 
+			int keylen, int num_rounds, void *cipher_state);
+	int (*encrypt)(const unsigned char *pt, unsigned char *ct, 
+			unsigned long len, void *cipher_state);
+	int (*decrypt)(const unsigned char *ct, unsigned char *pt, 
+			unsigned long len, void *cipher_state);
+};
+
 struct dropbear_hash {
 	const struct ltc_hash_descriptor *hashdesc;
 	unsigned long keysize;
diff --git a/auth.h b/auth.h
index d6fe120c27415f37dc97ae7a3873496b67738188..55efeefcb034be83c3263845b3e2af7714216af6 100644
--- a/auth.h
+++ b/auth.h
@@ -135,7 +135,8 @@ struct SignKeyList {
 	int type; /* The type of key */
 	struct SignKeyList *next;
 	int source;
-	/* filename? or the buffer? for encrypted keys, so we can later get
+	char *filename;
+	/* the buffer? for encrypted keys, so we can later get
 	 * the private key portion */
 
 };
diff --git a/cli-auth.c b/cli-auth.c
index 10245387e5a353b649d6d899a19228a318396e74..8b6a440c30dd31b4095aeb5f7f34400cc218036c 100644
--- a/cli-auth.c
+++ b/cli-auth.c
@@ -91,7 +91,7 @@ void recv_msg_userauth_banner() {
 		}
 	}
 
-	printf("%s\n", banner);
+	fprintf(stderr, "%s\n", banner);
 
 out:
 	m_free(banner);
@@ -229,6 +229,8 @@ void recv_msg_userauth_failure() {
 
 void recv_msg_userauth_success() {
 	TRACE(("received msg_userauth_success"))
+	/* Note: in delayed-zlib mode, setting authdone here 
+	 * will enable compression in the transport layer */
 	ses.authstate.authdone = 1;
 	cli_ses.state = USERAUTH_SUCCESS_RCVD;
 	cli_ses.lastauthtype = AUTH_TYPE_NONE;
diff --git a/cli-authpubkey.c b/cli-authpubkey.c
index 3b89eae96f9b3dc8193a4ec6778b77ed9416e3b2..20277a1ae3a941e6320376efbf8b860fb1bd5e76 100644
--- a/cli-authpubkey.c
+++ b/cli-authpubkey.c
@@ -53,6 +53,7 @@ void cli_pubkeyfail() {
 	}
 
 	sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
+	m_free(cli_ses.lastprivkey->filename);
 	m_free(cli_ses.lastprivkey);
 
 	TRACE(("leave cli_pubkeyfail"))
diff --git a/cli-kex.c b/cli-kex.c
index 37de6e3c8b2fd7c4a60587c521e9d171d21a0fdc..c4048ecc8a6ea68b33b663a386330a8faf4a6339 100644
--- a/cli-kex.c
+++ b/cli-kex.c
@@ -327,4 +327,5 @@ out:
 	if (line != NULL) {
 		buf_free(line);
 	}
+	m_free(fingerprint);
 }
diff --git a/cli-main.c b/cli-main.c
index 4ba43c0ff8812a575f450eda846afc83ebeb2676..e7ddaa801f548945f2f1c7f9aa252dd3b591019c 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -32,7 +32,9 @@
 static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
 static void cli_dropbear_log(int priority, const char* format, va_list param);
 
+#ifdef ENABLE_CLI_PROXYCMD
 static void cli_proxy_cmd(int *sock_in, int *sock_out);
+#endif
 
 #if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
 #if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
@@ -63,6 +65,7 @@ int main(int argc, char ** argv) {
 #ifdef ENABLE_CLI_PROXYCMD
 	if (cli_opts.proxycmd) {
 		cli_proxy_cmd(&sock_in, &sock_out);
+		m_free(cli_opts.proxycmd);
 	} else
 #endif
 	{
@@ -132,6 +135,7 @@ static void exec_proxy_cmd(void *user_data_cmd) {
 	dropbear_exit("Failed to run '%s'\n", cmd);
 }
 
+#ifdef ENABLE_CLI_PROXYCMD
 static void cli_proxy_cmd(int *sock_in, int *sock_out) {
 	int ret;
 
@@ -144,3 +148,4 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out) {
 		*sock_in = *sock_out = -1;
 	}
 }
+#endif // ENABLE_CLI_PROXYCMD
diff --git a/cli-runopts.c b/cli-runopts.c
index d49caa24252f0bf1813f311804c025eb08e70dd8..a7c0f8210a6dc0209f49e6959224d19d258e2683 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -49,7 +49,11 @@ static void add_netcat(const char *str);
 static void printhelp() {
 
 	fprintf(stderr, "Dropbear client v%s\n"
+#ifdef ENABLE_CLI_MULTIHOP
+					"Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n"
+#else
 					"Usage: %s [options] [user@]host[/port] [command]\n"
+#endif
 					"Options are:\n"
 					"-p <remoteport>\n"
 					"-l <username>\n"
@@ -74,22 +78,22 @@ static void printhelp() {
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d)\n"
+					"-I <idle_timeout>  (0 is never, default %d)\n"
 #ifdef ENABLE_CLI_NETCAT
-					"-B <endhost:endport> Netcat-alike bouncing\n"
+					"-B <endhost:endport> Netcat-alike forwarding\n"
 #endif				
 #ifdef ENABLE_CLI_PROXYCMD
-					"-J <proxy_program> Use program rather than tcp connection\n"
+					"-J <proxy_program> Use program pipe rather than TCP connection\n"
 #endif
 #ifdef DEBUG_TRACE
-					"-v    verbose\n"
+					"-v    verbose (compiled with DEBUG_TRACE)\n"
 #endif
 					,DROPBEAR_VERSION, cli_opts.progname,
-					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
+					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
 					
 }
 
 void cli_getopts(int argc, char ** argv) {
-
 	unsigned int i, j;
 	char ** next = 0;
 	unsigned int cmdlen;
@@ -109,6 +113,8 @@ void cli_getopts(int argc, char ** argv) {
 
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
+	char* idle_timeout_arg = NULL;
+	char *host_arg = NULL;
 
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
@@ -264,6 +270,9 @@ void cli_getopts(int argc, char ** argv) {
 				case 'K':
 					next = &keepalive_arg;
 					break;
+				case 'I':
+					next = &idle_timeout_arg;
+					break;
 #ifdef ENABLE_CLI_AGENTFWD
 				case 'A':
 					cli_opts.agent_fwd = 1;
@@ -307,12 +316,8 @@ void cli_getopts(int argc, char ** argv) {
 
 			/* Either the hostname or commands */
 
-			if (cli_opts.remotehost == NULL) {
-#ifdef ENABLE_CLI_MULTIHOP
-				parse_multihop_hostname(argv[i], argv[0]);
-#else
-				parse_hostname(argv[i]);
-#endif
+			if (host_arg == NULL) {
+				host_arg = argv[i];
 			} else {
 
 				/* this is part of the commands to send - after this we
@@ -341,7 +346,7 @@ void cli_getopts(int argc, char ** argv) {
 
 	/* And now a few sanity checks and setup */
 
-	if (cli_opts.remotehost == NULL) {
+	if (host_arg == NULL) {
 		printhelp();
 		exit(EXIT_FAILURE);
 	}
@@ -377,12 +382,26 @@ void cli_getopts(int argc, char ** argv) {
 		}
 	}
 
+	if (idle_timeout_arg) {
+		if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
+			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
+		}
+	}
+
 #ifdef ENABLE_CLI_NETCAT
 	if (cli_opts.cmd && cli_opts.netcat_host) {
 		dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
 	}
 #endif
-	
+
+	/* The hostname gets set up last, since
+	 * in multi-hop mode it will require knowledge
+	 * of other flags such as -i */
+#ifdef ENABLE_CLI_MULTIHOP
+	parse_multihop_hostname(host_arg, argv[0]);
+#else
+	parse_hostname(host_arg);
+#endif
 }
 
 #ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -395,14 +414,12 @@ static void loadidentityfile(const char* filename) {
 	key = new_sign_key();
 	keytype = DROPBEAR_SIGNKEY_ANY;
 	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
-
 		fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
 		sign_key_free(key);
-
 	} else {
-
 		nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList));
 		nextkey->key = key;
+		nextkey->filename = m_strdup(filename);
 		nextkey->next = cli_opts.privkeys;
 		nextkey->type = keytype;
 		nextkey->source = SIGNKEY_SOURCE_RAW_FILE;
@@ -413,6 +430,39 @@ static void loadidentityfile(const char* filename) {
 
 #ifdef ENABLE_CLI_MULTIHOP
 
+static char*
+multihop_passthrough_args() {
+	char *ret;
+	int total;
+	unsigned int len = 0;
+	struct SignKeyList *nextkey;
+	/* Fill out -i and -W options that make sense for all
+	 * the intermediate processes */
+	for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
+	{
+		len += 3 + strlen(nextkey->filename);
+	}
+	len += 20; // space for -W <size>, terminator.
+	ret = m_malloc(len);
+	total = 0;
+
+	if (opts.recv_window != DEFAULT_RECV_WINDOW)
+	{
+		int written = snprintf(ret+total, len-total, "-W %d", opts.recv_window);
+		total += written;
+	}
+
+	for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
+	{
+		const size_t size = len - total;
+		int written = snprintf(ret+total, size, "-i %s", nextkey->filename);
+		dropbear_assert(written < size);
+		total += written;
+	}
+
+	return ret;
+}
+
 /* Sets up 'onion-forwarding' connections. This will spawn
  * a separate dbclient process for each hop.
  * As an example, if the cmdline is
@@ -427,6 +477,7 @@ static void loadidentityfile(const char* filename) {
  */
 static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
 	char *userhostarg = NULL;
+	char *hostbuf = NULL;
 	char *last_hop = NULL;;
 	char *remainder = NULL;
 
@@ -439,11 +490,12 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
 			&& strchr(cli_opts.username, ',') 
 			&& strchr(cli_opts.username, '@')) {
 		unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
-		userhostarg = m_malloc(len);
-		snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg);
+		hostbuf = m_malloc(len);
+		snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
 	} else {
-		userhostarg = m_strdup(orighostarg);
+		hostbuf = m_strdup(orighostarg);
 	}
+	userhostarg = hostbuf;
 
 	last_hop = strrchr(userhostarg, ',');
 	if (last_hop) {
@@ -461,19 +513,24 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
 	if (last_hop) {
 		/* Set up the proxycmd */
 		unsigned int cmd_len = 0;
+		char *passthrough_args = multihop_passthrough_args();
 		if (cli_opts.proxycmd) {
 			dropbear_exit("-J can't be used with multihop mode");
 		}
 		if (cli_opts.remoteport == NULL) {
 			cli_opts.remoteport = "22";
 		}
-		cmd_len = strlen(remainder) 
+		cmd_len = strlen(argv0) + strlen(remainder) 
 			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
-			+ strlen(argv0) + 30;
+			+ strlen(passthrough_args)
+			+ 30;
 		cli_opts.proxycmd = m_malloc(cmd_len);
-		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", 
-				argv0, cli_opts.remotehost, cli_opts.remoteport, remainder);
+		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", 
+				argv0, cli_opts.remotehost, cli_opts.remoteport, 
+				passthrough_args, remainder);
+		m_free(passthrough_args);
 	}
+	m_free(hostbuf);
 }
 #endif /* !ENABLE_CLI_MULTIHOP */
 
@@ -622,6 +679,7 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
 		goto badport;
 	}
 
+	newfwd->have_reply = 0;
 	newfwd->next = *fwdlist;
 	*fwdlist = newfwd;
 
diff --git a/cli-session.c b/cli-session.c
index 8c087d4f2ba8bae7c39b031a8c5a8a338be5b1ff..4aa2366b1b9f6b4f1352282540f325fff8a30055 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -64,6 +64,10 @@ static const packettype cli_packettypes[] = {
 	{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 */
+#ifdef  ENABLE_CLI_REMOTETCPFWD
+	{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
+	{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
+#endif
 	{0, 0} /* End */
 };
 
diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c
index c3bfd4dc4a88540f4b3d2739578df97c28838bdd..0e60090bb2a296ae8b2f45efee571a20d5184a60 100644
--- a/cli-tcpfwd.c
+++ b/cli-tcpfwd.c
@@ -128,7 +128,7 @@ static void send_msg_global_request_remotetcp(int port) {
 	CHECKCLEARTOWRITE();
 	buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
 	buf_putstring(ses.writepayload, "tcpip-forward", 13);
-	buf_putbyte(ses.writepayload, 0);
+	buf_putbyte(ses.writepayload, 1); /* want_reply */
 	if (opts.listen_fwd_all) {
 		listenspec = "";
 	} else {
@@ -143,6 +143,42 @@ static void send_msg_global_request_remotetcp(int port) {
 	TRACE(("leave send_msg_global_request_remotetcp"))
 }
 
+/* The only global success/failure messages are for remotetcp.
+ * Since there isn't any identifier in these messages, we have to rely on them
+ * being in the same order as we sent the requests. This is the ordering
+ * of the cli_opts.remotefwds list */
+void cli_recv_msg_request_success() {
+
+	/* Nothing in the packet. We just mark off that we have received the reply,
+	 * so that we can report failure for later ones. */
+	struct TCPFwdList * iter = NULL;
+
+	iter = cli_opts.remotefwds;
+	while (iter != NULL) {
+		if (!iter->have_reply)
+		{
+			iter->have_reply = 1;
+			return;
+		}
+		iter = iter->next;
+	}
+}
+
+void cli_recv_msg_request_failure() {
+	struct TCPFwdList * iter = NULL;
+
+	iter = cli_opts.remotefwds;
+	while (iter != NULL) {
+		if (!iter->have_reply)
+		{
+			iter->have_reply = 1;
+			dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport);
+			return;
+		}
+		iter = iter->next;
+	}
+}
+
 void setup_remotetcp() {
 
 	struct TCPFwdList * iter = NULL;
diff --git a/common-algo.c b/common-algo.c
index 21ac96a49a1fd4169480049205e7ceb2c944a607..892399fee191b1d97a5149e534962bdbcacc3cc3 100644
--- a/common-algo.c
+++ b/common-algo.c
@@ -29,32 +29,46 @@
 /* This file (algo.c) organises the ciphers which can be used, and is used to
  * decide which ciphers/hashes/compression/signing to use during key exchange*/
 
+static int void_cipher(const unsigned char* in, unsigned char* out,
+		unsigned long len, void *cipher_state) {
+	if (in != out) {
+		memmove(out, in, len);
+	}
+	return CRYPT_OK;
+}
+
+static int void_start(int cipher, const unsigned char *IV, 
+			const unsigned char *key, 
+			int keylen, int num_rounds, void *cipher_state) {
+	return CRYPT_OK;
+}
+
 /* Mappings for ciphers, parameters are
    {&cipher_desc, keysize, blocksize} */
 /* NOTE: if keysize > 2*SHA1_HASH_SIZE, code such as hashkeys()
    needs revisiting */
 
-#ifdef DROPBEAR_AES256_CBC
+#ifdef DROPBEAR_AES256
 static const struct dropbear_cipher dropbear_aes256 = 
 	{&aes_desc, 32, 16};
 #endif
-#ifdef DROPBEAR_AES128_CBC
+#ifdef DROPBEAR_AES128
 static const struct dropbear_cipher dropbear_aes128 = 
 	{&aes_desc, 16, 16};
 #endif
-#ifdef DROPBEAR_BLOWFISH_CBC
+#ifdef DROPBEAR_BLOWFISH
 static const struct dropbear_cipher dropbear_blowfish = 
 	{&blowfish_desc, 16, 8};
 #endif
-#ifdef DROPBEAR_TWOFISH256_CBC
+#ifdef DROPBEAR_TWOFISH256
 static const struct dropbear_cipher dropbear_twofish256 = 
 	{&twofish_desc, 32, 16};
 #endif
-#ifdef DROPBEAR_TWOFISH128_CBC
+#ifdef DROPBEAR_TWOFISH128
 static const struct dropbear_cipher dropbear_twofish128 = 
 	{&twofish_desc, 16, 16};
 #endif
-#ifdef DROPBEAR_3DES_CBC
+#ifdef DROPBEAR_3DES
 static const struct dropbear_cipher dropbear_3des = 
 	{&des3_desc, 24, 8};
 #endif
@@ -63,6 +77,24 @@ static const struct dropbear_cipher dropbear_3des =
 const struct dropbear_cipher dropbear_nocipher =
 	{NULL, 16, 8}; 
 
+/* A few void* s are required to silence warnings
+ * about the symmetric_CBC vs symmetric_CTR cipher_state pointer */
+const struct dropbear_cipher_mode dropbear_mode_cbc =
+	{(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt};
+const struct dropbear_cipher_mode dropbear_mode_none =
+	{void_start, void_cipher, void_cipher};
+#ifdef DROPBEAR_ENABLE_CTR_MODE
+/* a wrapper to make ctr_start and cbc_start look the same */
+static int dropbear_big_endian_ctr_start(int cipher, 
+		const unsigned char *IV, 
+		const unsigned char *key, int keylen, 
+		int num_rounds, symmetric_CTR *ctr) {
+	return ctr_start(cipher, IV, key, keylen, num_rounds, CTR_COUNTER_BIG_ENDIAN, ctr);
+}
+const struct dropbear_cipher_mode dropbear_mode_ctr =
+	{(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt};
+#endif
+
 /* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc.
    {&hash_desc, keysize, hashsize} */
 
@@ -83,65 +115,81 @@ const struct dropbear_hash dropbear_nohash =
 	{NULL, 16, 0}; /* used initially */
 	
 
-/* The following map ssh names to internal values */
+/* The following map ssh names to internal values.
+ * The ordering here is important for the client - the first mode
+ * that is also supported by the server will get used. */
 
 algo_type sshciphers[] = {
-#ifdef DROPBEAR_AES128_CBC
-	{"aes128-cbc", 0, (void*)&dropbear_aes128, 1},
+#ifdef DROPBEAR_ENABLE_CTR_MODE
+#ifdef DROPBEAR_AES128
+	{"aes128-ctr", 0, &dropbear_aes128, 1, &dropbear_mode_ctr},
+#endif
+#ifdef DROPBEAR_3DES
+	{"3des-ctr", 0, &dropbear_3des, 1, &dropbear_mode_ctr},
+#endif
+#ifdef DROPBEAR_AES256
+	{"aes256-ctr", 0, &dropbear_aes256, 1, &dropbear_mode_ctr},
+#endif
+#endif /* DROPBEAR_ENABLE_CTR_MODE */
+
+/* CBC modes are always enabled */
+#ifdef DROPBEAR_AES128
+	{"aes128-cbc", 0, &dropbear_aes128, 1, &dropbear_mode_cbc},
 #endif
-#ifdef DROPBEAR_3DES_CBC
-	{"3des-cbc", 0, (void*)&dropbear_3des, 1},
+#ifdef DROPBEAR_3DES
+	{"3des-cbc", 0, &dropbear_3des, 1, &dropbear_mode_cbc},
 #endif
-#ifdef DROPBEAR_AES256_CBC
-	{"aes256-cbc", 0, (void*)&dropbear_aes256, 1},
+#ifdef DROPBEAR_AES256
+	{"aes256-cbc", 0, &dropbear_aes256, 1, &dropbear_mode_cbc},
 #endif
-#ifdef DROPBEAR_TWOFISH256_CBC
-	{"twofish256-cbc", 0, (void*)&dropbear_twofish256, 1},
-	{"twofish-cbc", 0, (void*)&dropbear_twofish256, 1},
+#ifdef DROPBEAR_TWOFISH256
+	{"twofish256-cbc", 0, &dropbear_twofish256, 1, &dropbear_mode_cbc},
+	{"twofish-cbc", 0, &dropbear_twofish256, 1, &dropbear_mode_cbc},
 #endif
-#ifdef DROPBEAR_TWOFISH128_CBC
-	{"twofish128-cbc", 0, (void*)&dropbear_twofish128, 1},
+#ifdef DROPBEAR_TWOFISH128
+	{"twofish128-cbc", 0, &dropbear_twofish128, 1, &dropbear_mode_cbc},
 #endif
-#ifdef DROPBEAR_BLOWFISH_CBC
-	{"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
+#ifdef DROPBEAR_BLOWFISH
+	{"blowfish-cbc", 0, &dropbear_blowfish, 1, &dropbear_mode_cbc},
 #endif
-	{NULL, 0, NULL, 0}
+	{NULL, 0, NULL, 0, NULL}
 };
 
 algo_type sshhashes[] = {
 #ifdef DROPBEAR_SHA1_96_HMAC
-	{"hmac-sha1-96", 0, (void*)&dropbear_sha1_96, 1},
+	{"hmac-sha1-96", 0, &dropbear_sha1_96, 1, NULL},
 #endif
 #ifdef DROPBEAR_SHA1_HMAC
-	{"hmac-sha1", 0, (void*)&dropbear_sha1, 1},
+	{"hmac-sha1", 0, &dropbear_sha1, 1, NULL},
 #endif
 #ifdef DROPBEAR_MD5_HMAC
-	{"hmac-md5", 0, (void*)&dropbear_md5, 1},
+	{"hmac-md5", 0, &dropbear_md5, 1, NULL},
 #endif
-	{NULL, 0, NULL, 0}
+	{NULL, 0, NULL, 0, NULL}
 };
 
 algo_type sshcompress[] = {
 #ifndef DISABLE_ZLIB
-	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
+	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL},
+	{"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL},
 #endif
-	{"none", DROPBEAR_COMP_NONE, NULL, 1},
-	{NULL, 0, NULL, 0}
+	{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
+	{NULL, 0, NULL, 0, NULL}
 };
 
 algo_type sshhostkey[] = {
 #ifdef DROPBEAR_RSA
-	{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1},
+	{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL},
 #endif
 #ifdef DROPBEAR_DSS
-	{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1},
+	{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL},
 #endif
-	{NULL, 0, NULL, 0}
+	{NULL, 0, NULL, 0, NULL}
 };
 
 algo_type sshkex[] = {
-	{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1},
-	{NULL, 0, NULL, 0}
+	{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL},
+	{NULL, 0, NULL, 0, NULL}
 };
 
 
@@ -150,16 +198,16 @@ algo_type sshkex[] = {
 void crypto_init() {
 
 	const struct ltc_cipher_descriptor *regciphers[] = {
-#ifdef DROPBEAR_AES_CBC
+#ifdef DROPBEAR_AES
 		&aes_desc,
 #endif
-#ifdef DROPBEAR_BLOWFISH_CBC
+#ifdef DROPBEAR_BLOWFISH
 		&blowfish_desc,
 #endif
-#ifdef DROPBEAR_TWOFISH_CBC
+#ifdef DROPBEAR_TWOFISH
 		&twofish_desc,
 #endif
-#ifdef DROPBEAR_3DES_CBC
+#ifdef DROPBEAR_3DES
 		&des3_desc,
 #endif
 		NULL
@@ -215,7 +263,7 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
 	unsigned int donefirst = 0;
 	buffer *algolist = NULL;
 
-	algolist = buf_new(100);
+	algolist = buf_new(160);
 	for (i = 0; localalgos[i].name != NULL; i++) {
 		if (localalgos[i].usable) {
 			if (donefirst)
diff --git a/common-channel.c b/common-channel.c
index 21051841c62d8342b81a06beb2459ba4d134591b..95fce78454b1c19ed489d6bf36475c497fc393f8 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -276,10 +276,10 @@ static void check_close(struct Channel *channel) {
 		channel->flushing = 1;
 	}
 	
-	// if a type-specific check_close is defined we will only exit
-	// once that has been triggered. this is only used for a server "session"
-	// channel, to ensure that the shell has exited (and the exit status 
-	// retrieved) before we close things up.	
+	/* if a type-specific check_close is defined we will only exit
+	   once that has been triggered. this is only used for a server "session"
+	   channel, to ensure that the shell has exited (and the exit status 
+	   retrieved) before we close things up. */
 	if (!channel->type->check_close	
 			|| channel->type->check_close(channel)) {
 		close_allowed = 1;
@@ -691,7 +691,7 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd,
 		dropbear_exit("received data after eof");
 	}
 
- 	if (fd < 0) {
+	if (fd < 0) {
 		/* If we have encountered failed write, the far side might still
 		 * be sending data without having yet received our close notification.
 		 * We just drop the data. */
diff --git a/common-kex.c b/common-kex.c
index e9c655dfa52e8493e6377c7639f021ea356e9ddf..052324b499a3ed15403a2a80aabfe10f082300a8 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -272,8 +272,8 @@ void gen_new_keys() {
 	    recv_IV		= S2C_IV;
 	    trans_key	= C2S_key;
 	    recv_key	= S2C_key;
-	    C2S_keysize = ses.newkeys->trans_algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->recv_algo_crypt->keysize;
+	    C2S_keysize = ses.newkeys->trans.algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->recv.algo_crypt->keysize;
 		mactransletter = 'E';
 		macrecvletter = 'F';
 	} else {
@@ -281,8 +281,8 @@ void gen_new_keys() {
 	    recv_IV		= C2S_IV;
 	    trans_key	= S2C_key;
 	    recv_key	= C2S_key;
-	    C2S_keysize = ses.newkeys->recv_algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->trans_algo_crypt->keysize;
+	    C2S_keysize = ses.newkeys->recv.algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->trans.algo_crypt->keysize;
 		mactransletter = 'F';
 		macrecvletter = 'E';
 	}
@@ -292,30 +292,33 @@ void gen_new_keys() {
 	hashkeys(C2S_key, C2S_keysize, &hs, 'C');
 	hashkeys(S2C_key, S2C_keysize, &hs, 'D');
 
-	recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name);
+	recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
 	if (recv_cipher < 0)
 	    dropbear_exit("crypto error");
-		
-	if (cbc_start(recv_cipher, recv_IV, recv_key, 
-			ses.newkeys->recv_algo_crypt->keysize, 0, 
-			&ses.newkeys->recv_symmetric_struct) != CRYPT_OK) {
+	if (ses.newkeys->recv.crypt_mode->start(recv_cipher, 
+			recv_IV, recv_key, 
+			ses.newkeys->recv.algo_crypt->keysize, 0, 
+			&ses.newkeys->recv.cipher_state) != CRYPT_OK) {
 		dropbear_exit("crypto error");
 	}
-	trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name);
+
+	trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
 	if (trans_cipher < 0)
 	    dropbear_exit("crypto error");
-		
-	if (cbc_start(trans_cipher, trans_IV, trans_key, 
-			ses.newkeys->trans_algo_crypt->keysize, 0, 
-			&ses.newkeys->trans_symmetric_struct) != CRYPT_OK) {
+	if (ses.newkeys->trans.crypt_mode->start(trans_cipher, 
+			trans_IV, trans_key, 
+			ses.newkeys->trans.algo_crypt->keysize, 0, 
+			&ses.newkeys->trans.cipher_state) != CRYPT_OK) {
 		dropbear_exit("crypto error");
 	}
 	
 	/* MAC keys */
-	hashkeys(ses.newkeys->transmackey, 
-			ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter);
-	hashkeys(ses.newkeys->recvmackey, 
-			ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter);
+	hashkeys(ses.newkeys->trans.mackey, 
+			ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter);
+	hashkeys(ses.newkeys->recv.mackey, 
+			ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter);
+	ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name),
+	ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name),
 
 #ifndef DISABLE_ZLIB
 	gen_new_zstreams();
@@ -331,53 +334,68 @@ void gen_new_keys() {
 }
 
 #ifndef DISABLE_ZLIB
+
+int is_compress_trans() {
+	return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB
+		|| (ses.authstate.authdone
+			&& ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+}
+
+int is_compress_recv() {
+	return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB
+		|| (ses.authstate.authdone
+			&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+}
+
 /* Set up new zlib compression streams, close the old ones. Only
  * called from gen_new_keys() */
 static void gen_new_zstreams() {
 
 	/* create new zstreams */
-	if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
-		ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
-		ses.newkeys->recv_zstream->zalloc = Z_NULL;
-		ses.newkeys->recv_zstream->zfree = Z_NULL;
+	if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
+		ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->recv.zstream->zalloc = Z_NULL;
+		ses.newkeys->recv.zstream->zfree = Z_NULL;
 		
-		if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) {
+		if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
 			dropbear_exit("zlib error");
 		}
 	} else {
-		ses.newkeys->recv_zstream = NULL;
+		ses.newkeys->recv.zstream = NULL;
 	}
 
-	if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
-		ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
-		ses.newkeys->trans_zstream->zalloc = Z_NULL;
-		ses.newkeys->trans_zstream->zfree = Z_NULL;
+	if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
+		ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->trans.zstream->zalloc = Z_NULL;
+		ses.newkeys->trans.zstream->zfree = Z_NULL;
 	
-		if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) 
+		if (deflateInit(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION) 
 				!= Z_OK) {
 			dropbear_exit("zlib error");
 		}
 	} else {
-		ses.newkeys->trans_zstream = NULL;
+		ses.newkeys->trans.zstream = NULL;
 	}
-	
+
 	/* clean up old keys */
-	if (ses.keys->recv_zstream != NULL) {
-		if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
+	if (ses.keys->recv.zstream != NULL) {
+		if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
 			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
 			dropbear_exit("crypto error");
 		}
-		m_free(ses.keys->recv_zstream);
+		m_free(ses.keys->recv.zstream);
 	}
-	if (ses.keys->trans_zstream != NULL) {
-		if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) {
+	if (ses.keys->trans.zstream != NULL) {
+		if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) {
 			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
 			dropbear_exit("crypto error");
 		}
-		m_free(ses.keys->trans_zstream);
+		m_free(ses.keys->trans.zstream);
 	}
 }
-#endif
+#endif /* DISABLE_ZLIB */
 
 
 /* Executed upon receiving a kexinit message from the client to initiate
@@ -682,28 +700,36 @@ static void read_kex_algos() {
 
 	/* Handle the asymmetry */
 	if (IS_DROPBEAR_CLIENT) {
-		ses.newkeys->recv_algo_crypt = 
+		ses.newkeys->recv.algo_crypt = 
 			(struct dropbear_cipher*)s2c_cipher_algo->data;
-		ses.newkeys->trans_algo_crypt = 
+		ses.newkeys->trans.algo_crypt = 
 			(struct dropbear_cipher*)c2s_cipher_algo->data;
-		ses.newkeys->recv_algo_mac = 
+		ses.newkeys->recv.crypt_mode = 
+			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
+		ses.newkeys->trans.crypt_mode =
+			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
+		ses.newkeys->recv.algo_mac = 
 			(struct dropbear_hash*)s2c_hash_algo->data;
-		ses.newkeys->trans_algo_mac = 
+		ses.newkeys->trans.algo_mac = 
 			(struct dropbear_hash*)c2s_hash_algo->data;
-		ses.newkeys->recv_algo_comp = s2c_comp_algo->val;
-		ses.newkeys->trans_algo_comp = c2s_comp_algo->val;
+		ses.newkeys->recv.algo_comp = s2c_comp_algo->val;
+		ses.newkeys->trans.algo_comp = c2s_comp_algo->val;
 	} else {
 		/* SERVER */
-		ses.newkeys->recv_algo_crypt = 
+		ses.newkeys->recv.algo_crypt = 
 			(struct dropbear_cipher*)c2s_cipher_algo->data;
-		ses.newkeys->trans_algo_crypt = 
+		ses.newkeys->trans.algo_crypt = 
 			(struct dropbear_cipher*)s2c_cipher_algo->data;
-		ses.newkeys->recv_algo_mac = 
+		ses.newkeys->recv.crypt_mode =
+			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
+		ses.newkeys->trans.crypt_mode =
+			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
+		ses.newkeys->recv.algo_mac = 
 			(struct dropbear_hash*)c2s_hash_algo->data;
-		ses.newkeys->trans_algo_mac = 
+		ses.newkeys->trans.algo_mac = 
 			(struct dropbear_hash*)s2c_hash_algo->data;
-		ses.newkeys->recv_algo_comp = c2s_comp_algo->val;
-		ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
+		ses.newkeys->recv.algo_comp = c2s_comp_algo->val;
+		ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
 	}
 
 	/* reserved for future extensions */
diff --git a/common-session.c b/common-session.c
index 3d759b5768fa12f2d5a1176443824a0ab0766555..1348da2612f06fd1bf480e3cf27e97bc7da5743a 100644
--- a/common-session.c
+++ b/common-session.c
@@ -63,6 +63,7 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
 	ses.maxfd = MAX(sock_in, sock_out);
 
 	ses.connect_time = 0;
+	ses.last_trx_packet_time = 0;
 	ses.last_packet_time = 0;
 	
 	if (pipe(ses.signal_pipe) < 0) {
@@ -70,6 +71,9 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
 	}
 	setnonblocking(ses.signal_pipe[0]);
 	setnonblocking(ses.signal_pipe[1]);
+
+	ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
+	ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
 	
 	kexfirstinitialise(); /* initialise the kex state */
 
@@ -77,7 +81,6 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
 	ses.transseq = 0;
 
 	ses.readbuf = NULL;
-	ses.decryptreadbuf = NULL;
 	ses.payload = NULL;
 	ses.recvseq = 0;
 
@@ -94,20 +97,22 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
 	/* set all the algos to none */
 	ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
 	ses.newkeys = NULL;
-	ses.keys->recv_algo_crypt = &dropbear_nocipher;
-	ses.keys->trans_algo_crypt = &dropbear_nocipher;
+	ses.keys->recv.algo_crypt = &dropbear_nocipher;
+	ses.keys->trans.algo_crypt = &dropbear_nocipher;
+	ses.keys->recv.crypt_mode = &dropbear_mode_none;
+	ses.keys->trans.crypt_mode = &dropbear_mode_none;
 	
-	ses.keys->recv_algo_mac = &dropbear_nohash;
-	ses.keys->trans_algo_mac = &dropbear_nohash;
+	ses.keys->recv.algo_mac = &dropbear_nohash;
+	ses.keys->trans.algo_mac = &dropbear_nohash;
 
 	ses.keys->algo_kex = -1;
 	ses.keys->algo_hostkey = -1;
-	ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE;
-	ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE;
+	ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE;
+	ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE;
 
 #ifndef DISABLE_ZLIB
-	ses.keys->recv_zstream = NULL;
-	ses.keys->trans_zstream = NULL;
+	ses.keys->recv.zstream = NULL;
+	ses.keys->trans.zstream = NULL;
 #endif
 
 	/* key exchange buffers */
@@ -256,7 +261,7 @@ void session_identification() {
 		ses.remoteclosed();
 	}
 
-    /* If they send more than 50 lines, something is wrong */
+	/* If they send more than 50 lines, something is wrong */
 	for (i = 0; i < 50; i++) {
 		len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
 
@@ -281,11 +286,11 @@ void session_identification() {
 		memcpy(ses.remoteident, linebuf, len);
 	}
 
-    /* Shall assume that 2.x will be backwards compatible. */
-    if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
-            && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
-        dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
-    }
+	/* Shall assume that 2.x will be backwards compatible. */
+	if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
+			&& strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
+		dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
+	}
 
 	TRACE(("remoteident: %s", ses.remoteident))
 
@@ -397,9 +402,14 @@ static void checktimeouts() {
 	}
 	
 	if (opts.keepalive_secs > 0 
-		&& now - ses.last_packet_time >= opts.keepalive_secs) {
+			&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
 		send_msg_ignore();
 	}
+
+	if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
+			&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
+		dropbear_close("Idle timeout");
+	}
 }
 
 static long select_timeout() {
@@ -412,6 +422,8 @@ static long select_timeout() {
 		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);
 	return ret;
 }
 
diff --git a/configure.in b/configure.in
index 52a75e0b1dc8d5adb9e62e698cd6c236327da642..c7149e80f927a755b20f415db314a03845359e8e 100644
--- a/configure.in
+++ b/configure.in
@@ -82,7 +82,8 @@ AC_CHECK_DECL(__UCLIBC__,
 	],,,)
 
 # Checks for libraries.
-AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
+AC_CHECK_LIB(crypt, crypt, CRYPTLIB="-lcrypt")
+AC_SUBST(CRYPTLIB)	
 
 # Check if zlib is needed
 AC_ARG_WITH(zlib,
diff --git a/dbclient.1 b/dbclient.1
index 033672d0e5f1890cd68c6ca0c0357ca3385531f5..96e6c5c14f3d50950ec227721899a776ea73312c 100644
--- a/dbclient.1
+++ b/dbclient.1
@@ -10,6 +10,13 @@ dbclient \- lightweight SSH2 client
 .I l\fR:\fIh\fR:\fIr\fR] [\-l
 .IR user ]
 .I host
+.RI [ command ]
+
+.B dbclient
+[
+.I args ]
+.I [user1]@host1[/port1],[user2]@host2[/port2],...
+
 .SH DESCRIPTION
 .B dbclient
 is a SSH 2 client designed to be small enough to be used in small memory
@@ -86,22 +93,52 @@ 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.
+.TP
+.B \-I \fIidle_timeout
+Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
+.TP
+.B \-J \fIproxy_command
+Use the standard input/output of the program \fIproxy_command\fR rather than using
+a normal TCP connection. A hostname should be still be provided, as this is used for
+comparing saved hostkeys.
+.TP
+.B \-B \fIendhost:endport
+"Netcat-alike" mode, where Dropbear will connect to the given host, then create a
+forwarded connection to \fIendhost\fR. This will then be presented as dbclient's
+standard input/output.
+
+Dropbear will also allow multiple "hops" to be specified, separated by commas. In
+this case a connection will be made to the first host, then a TCP forwarded 
+connection will be made through that to the second host, and so on. Hosts other than
+the final destination will not see anything other than the encrypted SSH stream. 
+A port for a host can be specified with a slash (eg matt@martello/44 ).
+This syntax can also be used with scp or rsync (specifying dbclient as the 
+ssh/rsh command). A file can be "bounced" through multiple SSH hops, eg
+
+scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump .
+
+Note that hostnames are resolved by the prior hop (so "canyons" would be resolved by the host "wrt")
+in the example above, the same way as other -L TCP forwarded hosts are. Host keys are 
+checked locally based on the given hostname.
+
 .SH ENVIRONMENT
 .TP
+.B DROPBEAR_PASSWORD
+A password to use for remote authentication can be specified in the environment
+variable DROPBEAR_PASSWORD. Care should be taken that the password is not
+exposed to other users on a multi-user system, or stored in accessible files.
+.TP
 .B SSH_ASKPASS
 dbclient can use an external program to request a password from a user.
 SSH_ASKPASS should be set to the path of a program that will return a password
 on standard output. This program will only be used if either DISPLAY is set and
 standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is
 set.
-.TP
-.B DROPBEAR_PASSWORD
-A password to use for remote authentication can be specified in the environment
-variable DROPBEAR_PASSWORD. Care should be taken that the password is not
-exposed to other users on a multi-user system, or stored in accessible files.
 .SH AUTHOR
 Matt Johnston (matt@ucc.asn.au).
 .br
+Mihnea Stoenescu wrote initial Dropbear client support
+.br
 Gerrit Pape (pape@smarden.org) wrote this manual page.
 .SH SEE ALSO
 dropbear(8), dropbearkey(8)
diff --git a/debian/changelog b/debian/changelog
index f51769b5d5999273642912474057a7e2ddc2cedc..83639c91d09ca029c9139e12fa41b24311e4a68c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+dropbear (0.52-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Wed, 12 Nov 2008 22:54:00 +0900
+
 dropbear (0.51-0.1) unstable; urgency=low
 
   * New upstream release.
diff --git a/debian/dropbear.postinst b/debian/dropbear.postinst
index 7c95cfab8c2b3c6924e99f9d847a12cded3e64f4..2491e45dbad8c945070c7365c5a0ab1fd4f0a4f6 100644
--- a/debian/dropbear.postinst
+++ b/debian/dropbear.postinst
@@ -71,7 +71,7 @@ if test -x /etc/init.d/dropbear; then
 fi
 
 if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' &&
-update-service --check dropbear; then
+update-service --check dropbear 2>/dev/null; then
   update-service --remove /etc/dropbear 2>/dev/null || :
   sleep 6
   rm -rf /var/run/dropbear /var/run/dropbear.log
diff --git a/debug.h b/debug.h
index a9cc0bd71f684f8c1a06d20f69a90befff94629a..b8c2a575aa38599f65afc98f68fcfaef10eb329f 100644
--- a/debug.h
+++ b/debug.h
@@ -39,7 +39,7 @@
  * Caution: Don't use this in an unfriendly environment (ie unfirewalled),
  * since the printing may not sanitise strings etc. This will add a reasonable
  * amount to your executable size. */
-#define DEBUG_TRACE
+/*#define DEBUG_TRACE*/
 
 /* All functions writing to the cleartext payload buffer call
  * CHECKCLEARTOWRITE() before writing. This is only really useful if you're
diff --git a/dropbear.8 b/dropbear.8
index c9c2e7913ecc2aac7b6b49943c666a84783f5e23..ecbad311bb2fd6bd7040c0a6c92cced7f069c791 100644
--- a/dropbear.8
+++ b/dropbear.8
@@ -24,7 +24,10 @@ before user login (default: none).
 dsskeyfile.
 Use the contents of the file
 .I dsskey
-for the dss host key (default: /etc/dropbear/dropbear_dss_host_key).
+for the DSS host key (default: /etc/dropbear/dropbear_dss_host_key). 
+Note that 
+some SSH implementations
+use the term "DSA" rather than "DSS", they mean the same thing.
 This file is generated with
 .BR dropbearkey (8).
 .TP
@@ -94,6 +97,63 @@ 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.
+.TP
+.B \-I \fIidle_timeout
+Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
+.SH FILES
+
+.TP
+Authorized Keys
+
+~/.ssh/authorized_keys can be set up to allow remote login with a RSA or DSS
+key. Each line is of the form
+.TP
+[restrictions] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIgAsp... [comment]
+
+and can be extracted from a Dropbear private host key with "dropbearkey -y". This is the same format as used by OpenSSH, though the restrictions are a subset (keys with unknown restrictions are ignored).
+Restrictions are comma separated, with double quotes around spaces in arguments.
+Available restrictions are:
+
+.TP
+.B no-port-forwarding
+Don't allow port forwarding for this connection
+
+.TP
+.B no-agent-forwarding
+Don't allow agent forwarding for this connection
+
+.TP
+.B no-X11-forwarding
+Don't allow X11 forwarding for this connection
+
+.TP
+.B no-pty
+Disable PTY allocation. Note that a user can still obtain most of the
+same functionality with other means even if no-pty is set.
+
+.TP
+.B command="\fIforced_command\fR"
+Disregard the command provided by the user and always run \fIforced_command\fR.
+
+The authorized_keys file and its containing ~/.ssh directory must only be
+writable by the user, otherwise Dropbear will not allow a login using public
+key authentication.
+
+.TP
+Host Key Files
+
+Host key files are read at startup from a standard location, by default
+/etc/dropbear/dropbear_dss_host_key and /etc/dropbear/dropbear_rsa_host_key
+or specified on the commandline with -d or -r. These are of the form generated
+by dropbearkey.
+
+.TP
+Message Of The Day
+
+By default the file /etc/motd will be printed for any login shell (unless 
+disabled at compile-time). This can also be disabled per-user
+by creating a file ~/.hushlogin .
+
 .SH AUTHOR
 Matt Johnston (matt@ucc.asn.au).
 .br
diff --git a/dropbearkey.8 b/dropbearkey.8
index a093d85ea7f6b317a294e1950d5839f1139f80a0..b5745dd820c8c94fe6ee302947a2acffac8e99dc 100644
--- a/dropbearkey.8
+++ b/dropbearkey.8
@@ -11,13 +11,16 @@ dropbearkey \- create private keys for the use with dropbear(8)
 .IR bits ]
 .SH DESCRIPTION
 .B dropbearkey
-generates a type
-.I rsa
+generates a
+.I RSA
 or
-.I dss
-SSH private key, and saves it to a file for the use with the
+.I DSS
+format SSH private key, and saves it to a file for the use with the
 .BR dropbear (8)
 SSH 2 server.
+Note that 
+some SSH implementations
+use the term "DSA" rather than "DSS", they mean the same thing.
 .SH OPTIONS
 .TP
 .B \-t \fItype
diff --git a/kex.h b/kex.h
index d3dd1874929e5b92a6c9b7c5f1772ca8b9ffef34..a3bdc7a72f5725f6d92838912efc50f837bab4dc 100644
--- a/kex.h
+++ b/kex.h
@@ -37,6 +37,11 @@ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
 void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
 		sign_key *hostkey);
 
+#ifndef DISABLE_ZLIB
+int is_compress_trans();
+int is_compress_recv();
+#endif
+
 void recv_msg_kexdh_init(); /* server */
 
 void send_msg_kexdh_init(); /* client */
diff --git a/libtomcrypt/src/headers/tomcrypt_custom.h b/libtomcrypt/src/headers/tomcrypt_custom.h
index 440168bd3534495f051dc4ac63a4b179a0027eba..928f54e39468f0d65d599869ebe9704c6deaa43c 100644
--- a/libtomcrypt/src/headers/tomcrypt_custom.h
+++ b/libtomcrypt/src/headers/tomcrypt_custom.h
@@ -90,15 +90,15 @@
 /* #define LTC_NO_BSWAP */
 
 
-#ifdef DROPBEAR_BLOWFISH_CBC
+#ifdef DROPBEAR_BLOWFISH
 #define BLOWFISH
 #endif
 
-#ifdef DROPBEAR_AES_CBC
+#ifdef DROPBEAR_AES
 #define RIJNDAEL
 #endif
 
-#ifdef DROPBEAR_TWOFISH_CBC
+#ifdef DROPBEAR_TWOFISH
 #define TWOFISH
 
 /* enabling just TWOFISH_SMALL will make the binary ~1kB smaller, turning on
@@ -108,12 +108,16 @@
 /*#define TWOFISH_TABLES*/
 #endif
 
-#ifdef DROPBEAR_3DES_CBC
+#ifdef DROPBEAR_3DES
 #define DES
 #endif
 
 #define LTC_CBC_MODE
 
+#ifdef DROPBEAR_ENABLE_CTR_MODE
+#define LTC_CTR_MODE
+#endif
+
 #if defined(DROPBEAR_DSS) && defined(DSS_PROTOK)
 #define SHA512
 #endif
diff --git a/loginrec.c b/loginrec.c
index f084566ee88a2e4cb31d5d4f8ac94d8c771a7a0e..14b8090f7443694bad4ede9d7bae6a3343c34037 100644
--- a/loginrec.c
+++ b/loginrec.c
@@ -1334,7 +1334,7 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode)
 			return 0;
 	}
 
-	*fd = open(lastlog_file, filemode);
+	*fd = open(lastlog_file, filemode, 0600);
 	if ( *fd < 0) {
 		dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s",
 		    lastlog_file, strerror(errno));
diff --git a/options.h b/options.h
index 551562862de837956fd8cec54dcb30d823a8eb01..021457cf1b9651659f2ed53629f7dc472244a96a 100644
--- a/options.h
+++ b/options.h
@@ -46,9 +46,10 @@
 /*#define NO_FAST_EXPTMOD*/
 
 /* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
-several kB in binary size, however will make the symmetrical ciphers (AES, DES
-etc) slower (perhaps by 50%). Recommended for most small systems. */
-#define DROPBEAR_SMALL_CODE
+several kB in binary size however will make the symmetrical ciphers and hashes
+slower, perhaps by 50%. Recommended for small systems that aren't doing
+much traffic. */
+/*#define DROPBEAR_SMALL_CODE*/
 
 /* Enable X11 Forwarding - server only */
 #define ENABLE_X11FWD
@@ -60,10 +61,6 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
 #define ENABLE_CLI_LOCALTCPFWD
 #define ENABLE_CLI_REMOTETCPFWD
 
-/* Allow using -J <proxycommand> to run the connection through a 
-   pipe to a program, rather the normal TCP connection */
-#define ENABLE_CLI_PROXYCMD
-
 #define ENABLE_SVR_LOCALTCPFWD
 #define ENABLE_SVR_REMOTETCPFWD
 
@@ -71,23 +68,36 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
 #define ENABLE_SVR_AGENTFWD
 #define ENABLE_CLI_AGENTFWD
 
-/* Enable "Netcat mode". TODO describe here. */
-#define ENABLE_CLI_NETCAT
 
+/* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
+ * allow multihop dbclient connections */
+
+/* Allow using -J <proxycommand> to run the connection through a 
+   pipe to a program, rather the normal TCP connection */
+#define ENABLE_CLI_PROXYCMD
+
+/* Enable "Netcat mode" option. This will forward standard input/output
+ * to a remote TCP-forwarded connection */
+#define ENABLE_CLI_NETCAT
 
 /* Encryption - at least one required.
- * RFC Draft requires 3DES and recommends AES128 for interoperability.
+ * Protocol RFC requires 3DES and recommends AES128 for interoperability.
  * Including multiple keysize variants the same cipher 
  * (eg AES256 as well as AES128) will result in a minimal size increase.*/
-#define DROPBEAR_AES128_CBC
-#define DROPBEAR_3DES_CBC
-#define DROPBEAR_AES256_CBC
-#define DROPBEAR_BLOWFISH_CBC
-#define DROPBEAR_TWOFISH256_CBC
-#define DROPBEAR_TWOFISH128_CBC
+#define DROPBEAR_AES128
+#define DROPBEAR_3DES
+#define DROPBEAR_AES256
+#define DROPBEAR_BLOWFISH
+#define DROPBEAR_TWOFISH256
+#define DROPBEAR_TWOFISH128
+
+/* Enable "Counter Mode" for ciphers. This is more secure than normal
+ * CBC mode against certain attacks. This adds around 1kB to binary 
+ * size and is recommended for most cases */
+#define DROPBEAR_ENABLE_CTR_MODE
 
 /* Message Integrity - at least one required.
- * RFC Draft requires sha1 and recommends sha1-96.
+ * Protocol RFC requires sha1 and recommends sha1-96.
  * sha1-96 may be of use for slow links, as it has a smaller overhead.
  *
  * Note: there's no point disabling sha1 to save space, since it's used
@@ -143,7 +153,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
 
 #define ENABLE_SVR_PASSWORD_AUTH
 /* PAM requires ./configure --enable-pam */
-/* #define ENABLE_SVR_PAM_AUTH */
+/*#define ENABLE_SVR_PAM_AUTH*/
 #define ENABLE_SVR_PUBKEY_AUTH
 
 /* Wether to ake public key options in authorized_keys file into account */
@@ -250,6 +260,13 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
 be overridden at runtime with -K. 0 disables keepalives */
 #define DEFAULT_KEEPALIVE 0
 
+/* 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
+
+/* The default path. This will often get replaced by the shell */
+#define DEFAULT_PATH "/usr/bin:/bin"
+
 /* Some other defines (that mostly should be left alone) are defined
  * in sysoptions.h */
 #include "sysoptions.h"
diff --git a/packet.c b/packet.c
index 30f4758689bb1bc6f2b20fea01a0a15b2a8179fe..37ffdd2dd988e42b3467b35570c998b3dea4452b 100644
--- a/packet.c
+++ b/packet.c
@@ -35,9 +35,11 @@
 #include "auth.h"
 #include "channel.h"
 
-static void read_packet_init();
-static void writemac(buffer * outputbuffer, buffer * clearwritebuf);
-static int checkmac(buffer* hashbuf, buffer* readbuf);
+static int read_packet_init();
+static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
+		buffer * clear_buf, unsigned int clear_len, 
+		unsigned char *output_mac);
+static int checkmac();
 
 #define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
 #define ZLIB_DECOMPRESS_INCR 100
@@ -72,6 +74,7 @@ void write_packet() {
 		}
 	} 
 	
+	ses.last_trx_packet_time = time(NULL);
 	ses.last_packet_time = time(NULL);
 
 	if (written == 0) {
@@ -101,18 +104,18 @@ void read_packet() {
 	unsigned char blocksize;
 
 	TRACE(("enter read_packet"))
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
 	
 	if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
+		int ret;
 		/* In the first blocksize of a packet */
 
 		/* Read the first blocksize of the packet, so we can decrypt it and
 		 * find the length of the whole packet */
-		read_packet_init();
+		ret = read_packet_init();
 
-		/* If we don't have the length of decryptreadbuf, we didn't read
-		 * a whole blocksize and should exit */
-		if (ses.decryptreadbuf->len == 0) {
+		if (ret == DROPBEAR_FAILURE) {
+			/* didn't read enough to determine the length */
 			TRACE(("leave read_packet: packetinit done"))
 			return;
 		}
@@ -120,7 +123,6 @@ void read_packet() {
 
 	/* Attempt to read the remainder of the packet, note that there
 	 * mightn't be any available (EAGAIN) */
-	dropbear_assert(ses.readbuf != NULL);
 	maxlen = ses.readbuf->len - ses.readbuf->pos;
 	len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
 
@@ -150,7 +152,9 @@ void read_packet() {
 
 /* Function used to read the initial portion of a packet, and determine the
  * length. Only called during the first BLOCKSIZE of a packet. */
-static void read_packet_init() {
+/* Returns DROPBEAR_SUCCESS if the length is determined, 
+ * DROPBEAR_FAILURE otherwise */
+static int read_packet_init() {
 
 	unsigned int maxlen;
 	int len;
@@ -158,14 +162,12 @@ static void read_packet_init() {
 	unsigned char macsize;
 
 
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
-	macsize = ses.keys->recv_algo_mac->hashsize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
+	macsize = ses.keys->recv.algo_mac->hashsize;
 
 	if (ses.readbuf == NULL) {
 		/* start of a new packet */
 		ses.readbuf = buf_new(INIT_READBUF);
-		dropbear_assert(ses.decryptreadbuf == NULL);
-		ses.decryptreadbuf = buf_new(blocksize);
 	}
 
 	maxlen = blocksize - ses.readbuf->pos;
@@ -179,7 +181,7 @@ static void read_packet_init() {
 	if (len < 0) {
 		if (errno == EINTR) {
 			TRACE(("leave read_packet_init: EINTR"))
-			return;
+			return DROPBEAR_FAILURE;
 		}
 		dropbear_exit("error reading: %s", strerror(errno));
 	}
@@ -188,30 +190,22 @@ static void read_packet_init() {
 
 	if ((unsigned int)len != maxlen) {
 		/* don't have enough bytes to determine length, get next time */
-		return;
+		return DROPBEAR_FAILURE;
 	}
 
 	/* now we have the first block, need to get packet length, so we decrypt
 	 * the first block (only need first 4 bytes) */
 	buf_setpos(ses.readbuf, 0);
-	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
-		/* copy it */
-		memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize),
-				buf_getptr(ses.readbuf, blocksize),
-				blocksize);
-	} else {
-		/* decrypt it */
-		if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
-					buf_getwriteptr(ses.decryptreadbuf,blocksize),
-					blocksize,
-					&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
-			dropbear_exit("error decrypting");
-		}
+	if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
+				buf_getwriteptr(ses.readbuf, blocksize),
+				blocksize,
+				&ses.keys->recv.cipher_state) != CRYPT_OK) {
+		dropbear_exit("error decrypting");
 	}
-	buf_setlen(ses.decryptreadbuf, blocksize);
-	len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
+	len = buf_getint(ses.readbuf) + 4 + macsize;
+
+	TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
 
-	buf_setpos(ses.readbuf, blocksize);
 
 	/* check packet length */
 	if ((len > RECV_MAX_PACKET_LEN) ||
@@ -220,9 +214,12 @@ static void read_packet_init() {
 		dropbear_exit("bad packet size %d", len);
 	}
 
-	buf_resize(ses.readbuf, len);
+	if (len > ses.readbuf->size) {
+		buf_resize(ses.readbuf, len);		
+	}
 	buf_setlen(ses.readbuf, len);
-
+	buf_setpos(ses.readbuf, blocksize);
+	return DROPBEAR_SUCCESS;
 }
 
 /* handle the received packet */
@@ -234,77 +231,58 @@ void decrypt_packet() {
 	unsigned int len;
 
 	TRACE(("enter decrypt_packet"))
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
-	macsize = ses.keys->recv_algo_mac->hashsize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
+	macsize = ses.keys->recv.algo_mac->hashsize;
 
 	ses.kexstate.datarecv += ses.readbuf->len;
 
 	/* we've already decrypted the first blocksize in read_packet_init */
 	buf_setpos(ses.readbuf, blocksize);
 
-	buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
-	buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
-	buf_setpos(ses.decryptreadbuf, blocksize);
-
-	/* decrypt if encryption is set, memcpy otherwise */
-	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
-		/* copy it */
-		len = ses.readbuf->len - macsize - blocksize;
-		memcpy(buf_getwriteptr(ses.decryptreadbuf, len),
-				buf_getptr(ses.readbuf, len), len);
-	} else {
-		/* decrypt */
-		while (ses.readbuf->pos < ses.readbuf->len - macsize) {
-			if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
-						buf_getwriteptr(ses.decryptreadbuf, blocksize),
-						blocksize,
-						&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
-				dropbear_exit("error decrypting");
-			}
-			buf_incrpos(ses.readbuf, blocksize);
-			buf_incrwritepos(ses.decryptreadbuf, blocksize);
-		}
+	/* decrypt it in-place */
+	len = ses.readbuf->len - macsize - ses.readbuf->pos;
+	if (ses.keys->recv.crypt_mode->decrypt(
+				buf_getptr(ses.readbuf, len), 
+				buf_getwriteptr(ses.readbuf, len),
+				len,
+				&ses.keys->recv.cipher_state) != CRYPT_OK) {
+		dropbear_exit("error decrypting");
 	}
+	buf_incrpos(ses.readbuf, len);
 
 	/* check the hmac */
-	buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
-	if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
+	if (checkmac() != DROPBEAR_SUCCESS) {
 		dropbear_exit("Integrity error");
 	}
 
-	/* readbuf no longer required */
-	buf_free(ses.readbuf);
-	ses.readbuf = NULL;
-
 	/* get padding length */
-	buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
-	padlen = buf_getbyte(ses.decryptreadbuf);
+	buf_setpos(ses.readbuf, PACKET_PADDING_OFF);
+	padlen = buf_getbyte(ses.readbuf);
 		
 	/* payload length */
 	/* - 4 - 1 is for LEN and PADLEN values */
-	len = ses.decryptreadbuf->len - padlen - 4 - 1;
+	len = ses.readbuf->len - padlen - 4 - 1;
 	if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) {
 		dropbear_exit("bad packet size");
 	}
 
-	buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
+	buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF);
 
 #ifndef DISABLE_ZLIB
-	if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+	if (is_compress_recv()) {
 		/* decompress */
-		ses.payload = buf_decompress(ses.decryptreadbuf, len);
-
+		ses.payload = buf_decompress(ses.readbuf, len);
 	} else 
 #endif
 	{
 		/* copy payload */
 		ses.payload = buf_new(len);
-		memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
+		memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
 		buf_incrlen(ses.payload, len);
 	}
 
-	buf_free(ses.decryptreadbuf);
-	ses.decryptreadbuf = NULL;
+	buf_free(ses.readbuf);
+	ses.readbuf = NULL;
 	buf_setpos(ses.payload, 0);
 
 	ses.recvseq++;
@@ -312,49 +290,22 @@ void decrypt_packet() {
 	TRACE(("leave decrypt_packet"))
 }
 
-/* Checks the mac in hashbuf, for the data in readbuf.
+/* Checks the mac at the end of a decrypted readbuf.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-static int checkmac(buffer* macbuf, buffer* sourcebuf) {
-
-	unsigned int macsize;
-	hmac_state hmac;
-	unsigned char tempbuf[MAX_MAC_LEN];
-	unsigned long bufsize;
-	unsigned int len;
-
-	macsize = ses.keys->recv_algo_mac->hashsize;
-	if (macsize == 0) {
-		return DROPBEAR_SUCCESS;
-	}
+static int checkmac() {
 
-	/* calculate the mac */
-	if (hmac_init(&hmac, 
-				find_hash(ses.keys->recv_algo_mac->hashdesc->name), 
-				ses.keys->recvmackey, 
-				ses.keys->recv_algo_mac->keysize) 
-				!= CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	unsigned char mac_bytes[MAX_MAC_LEN];
+	unsigned int mac_size, contents_len;
 	
-	/* sequence number */
-	STORE32H(ses.recvseq, tempbuf);
-	if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	mac_size = ses.keys->trans.algo_mac->hashsize;
+	contents_len = ses.readbuf->len - mac_size;
 
-	buf_setpos(sourcebuf, 0);
-	len = sourcebuf->len;
-	if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
-
-	bufsize = sizeof(tempbuf);
-	if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	buf_setpos(ses.readbuf, 0);
+	make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
 
 	/* compare the hash */
-	if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
+	buf_setpos(ses.readbuf, contents_len);
+	if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
 		return DROPBEAR_FAILURE;
 	} else {
 		return DROPBEAR_SUCCESS;
@@ -369,7 +320,7 @@ static buffer* buf_decompress(buffer* buf, unsigned int len) {
 	buffer * ret;
 	z_streamp zstream;
 
-	zstream = ses.keys->recv_zstream;
+	zstream = ses.keys->recv.zstream;
 	ret = buf_new(len);
 
 	zstream->avail_in = len;
@@ -465,10 +416,12 @@ void maybe_flush_reply_queue() {
 void encrypt_packet() {
 
 	unsigned char padlen;
-	unsigned char blocksize, macsize;
-	buffer * writebuf; /* the packet which will go on the wire */
-	buffer * clearwritebuf; /* unencrypted, possibly compressed */
+	unsigned char blocksize, mac_size;
+	buffer * writebuf; /* the packet which will go on the wire. This is 
+	                      encrypted in-place. */
 	unsigned char type;
+	unsigned int len, encrypt_buf_size;
+	unsigned char mac_bytes[MAX_MAC_LEN];
 	
 	type = ses.writepayload->data[0];
 	TRACE(("enter encrypt_packet()"))
@@ -482,33 +435,36 @@ void encrypt_packet() {
 		return;
 	}
 		
-	blocksize = ses.keys->trans_algo_crypt->blocksize;
-	macsize = ses.keys->trans_algo_mac->hashsize;
+	blocksize = ses.keys->trans.algo_crypt->blocksize;
+	mac_size = ses.keys->trans.algo_mac->hashsize;
 
 	/* Encrypted packet len is payload+5, then worst case is if we are 3 away
 	 * from a blocksize multiple. In which case we need to pad to the
 	 * multiple, then add another blocksize (or MIN_PACKET_LEN) */
-	clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
+	encrypt_buf_size = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3;
+	/* add space for the MAC at the end */
+	encrypt_buf_size += mac_size;
+
 #ifndef DISABLE_ZLIB
-			+ ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
+	encrypt_buf_size += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/
 #endif
-			);
-	buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
-	buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
+	writebuf = buf_new(encrypt_buf_size);
+	buf_setlen(writebuf, PACKET_PAYLOAD_OFF);
+	buf_setpos(writebuf, PACKET_PAYLOAD_OFF);
 
 	buf_setpos(ses.writepayload, 0);
 
 #ifndef DISABLE_ZLIB
 	/* compression */
-	if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
-		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
+	if (is_compress_trans()) {
+		buf_compress(writebuf, ses.writepayload, ses.writepayload->len);
 	} else
 #endif
 	{
-		memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
+		memcpy(buf_getwriteptr(writebuf, ses.writepayload->len),
 				buf_getptr(ses.writepayload, ses.writepayload->len),
 				ses.writepayload->len);
-		buf_incrwritepos(clearwritebuf, ses.writepayload->len);
+		buf_incrwritepos(writebuf, ses.writepayload->len);
 	}
 
 	/* finished with payload */
@@ -517,60 +473,45 @@ void encrypt_packet() {
 
 	/* length of padding - packet length must be a multiple of blocksize,
 	 * with a minimum of 4 bytes of padding */
-	padlen = blocksize - (clearwritebuf->len) % blocksize;
+	padlen = blocksize - (writebuf->len) % blocksize;
 	if (padlen < 4) {
 		padlen += blocksize;
 	}
 	/* check for min packet length */
-	if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
+	if (writebuf->len + padlen < MIN_PACKET_LEN) {
 		padlen += blocksize;
 	}
 
-	buf_setpos(clearwritebuf, 0);
+	buf_setpos(writebuf, 0);
 	/* packet length excluding the packetlength uint32 */
-	buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);
+	buf_putint(writebuf, writebuf->len + padlen - 4);
 
 	/* padding len */
-	buf_putbyte(clearwritebuf, padlen);
+	buf_putbyte(writebuf, padlen);
 	/* actual padding */
-	buf_setpos(clearwritebuf, clearwritebuf->len);
-	buf_incrlen(clearwritebuf, padlen);
-	genrandom(buf_getptr(clearwritebuf, padlen), padlen);
-
-	/* do the actual encryption */
-	buf_setpos(clearwritebuf, 0);
-	/* create a new writebuffer, this is freed when it has been put on the 
-	 * wire by writepacket() */
-	writebuf = buf_new(clearwritebuf->len + macsize);
-
-	if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
-		/* copy it */
-		memcpy(buf_getwriteptr(writebuf, clearwritebuf->len),
-				buf_getptr(clearwritebuf, clearwritebuf->len),
-				clearwritebuf->len);
-		buf_incrwritepos(writebuf, clearwritebuf->len);
-	} else {
-		/* encrypt it */
-		while (clearwritebuf->pos < clearwritebuf->len) {
-			if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize),
-						buf_getwriteptr(writebuf, blocksize),
-						blocksize,
-						&ses.keys->trans_symmetric_struct) != CRYPT_OK) {
-				dropbear_exit("error encrypting");
-			}
-			buf_incrpos(clearwritebuf, blocksize);
-			buf_incrwritepos(writebuf, blocksize);
-		}
-	}
+	buf_setpos(writebuf, writebuf->len);
+	buf_incrlen(writebuf, padlen);
+	genrandom(buf_getptr(writebuf, padlen), padlen);
 
-	/* now add a hmac and we're done */
-	writemac(writebuf, clearwritebuf);
+	make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
 
-	/* clearwritebuf is finished with */
-	buf_free(clearwritebuf);
-	clearwritebuf = NULL;
+	/* do the actual encryption, in-place */
+	buf_setpos(writebuf, 0);
+	/* encrypt it in-place*/
+	len = writebuf->len;
+	if (ses.keys->trans.crypt_mode->encrypt(
+				buf_getptr(writebuf, len),
+				buf_getwriteptr(writebuf, len),
+				len,
+				&ses.keys->trans.cipher_state) != CRYPT_OK) {
+		dropbear_exit("error encrypting");
+	}
+	buf_incrpos(writebuf, len);
 
-	/* enqueue the packet for sending */
+    /* stick the MAC on it */
+    buf_putbytes(writebuf, mac_bytes, mac_size);
+
+	/* enqueue the packet for sending. It will get freed after transmission. */
 	buf_setpos(writebuf, 0);
 	enqueue(&ses.writequeue, (void*)writebuf);
 
@@ -583,47 +524,43 @@ void encrypt_packet() {
 
 
 /* Create the packet mac, and append H(seqno|clearbuf) to the output */
-static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
-
-	unsigned int macsize;
+/* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */
+static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
+		buffer * clear_buf, unsigned int clear_len, 
+		unsigned char *output_mac) {
 	unsigned char seqbuf[4];
-	unsigned char tempbuf[MAX_MAC_LEN];
 	unsigned long bufsize;
 	hmac_state hmac;
 
 	TRACE(("enter writemac"))
 
-	macsize = ses.keys->trans_algo_mac->hashsize;
-	if (macsize > 0) {
+	if (key_state->algo_mac->hashsize > 0) {
 		/* calculate the mac */
 		if (hmac_init(&hmac, 
-					find_hash(ses.keys->trans_algo_mac->hashdesc->name), 
-					ses.keys->transmackey, 
-					ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
+					key_state->hash_index,
+					key_state->mackey,
+					key_state->algo_mac->keysize) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
 		/* sequence number */
-		STORE32H(ses.transseq, seqbuf);
+		STORE32H(seqno, seqbuf);
 		if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
 		/* the actual contents */
-		buf_setpos(clearwritebuf, 0);
+		buf_setpos(clear_buf, 0);
 		if (hmac_process(&hmac, 
-					buf_getptr(clearwritebuf, 
-						clearwritebuf->len),
-					clearwritebuf->len) != CRYPT_OK) {
+					buf_getptr(clear_buf, clear_len),
+					clear_len) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
-		bufsize = sizeof(tempbuf);
-		if (hmac_done(&hmac, tempbuf, &bufsize) 
-				!= CRYPT_OK) {
+        bufsize = MAX_MAC_LEN;
+		if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
-		buf_putbytes(outputbuffer, tempbuf, macsize);
 	}
 	TRACE(("leave writemac"))
 }
@@ -640,29 +577,29 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
 
 	while (1) {
 
-		ses.keys->trans_zstream->avail_in = endpos - src->pos;
-		ses.keys->trans_zstream->next_in = 
-			buf_getptr(src, ses.keys->trans_zstream->avail_in);
+		ses.keys->trans.zstream->avail_in = endpos - src->pos;
+		ses.keys->trans.zstream->next_in = 
+			buf_getptr(src, ses.keys->trans.zstream->avail_in);
 
-		ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
-		ses.keys->trans_zstream->next_out =
-			buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);
+		ses.keys->trans.zstream->avail_out = dest->size - dest->pos;
+		ses.keys->trans.zstream->next_out =
+			buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out);
 
-		result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);
+		result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH);
 
-		buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
-		buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
+		buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in);
+		buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out);
 		buf_setpos(dest, dest->len);
 
 		if (result != Z_OK) {
 			dropbear_exit("zlib error");
 		}
 
-		if (ses.keys->trans_zstream->avail_in == 0) {
+		if (ses.keys->trans.zstream->avail_in == 0) {
 			break;
 		}
 
-		dropbear_assert(ses.keys->trans_zstream->avail_out == 0);
+		dropbear_assert(ses.keys->trans.zstream->avail_out == 0);
 
 		/* the buffer has been filled, we must extend. This only happens in
 		 * unusual circumstances where the data grows in size after deflate(),
diff --git a/packet.h b/packet.h
index 8fadeb38a0de75e2e2f34effece96873dcb63281..4645b146390656f31a92df88bf35fbac35308fb9 100644
--- a/packet.h
+++ b/packet.h
@@ -44,6 +44,6 @@ typedef struct PacketType {
 #define PACKET_PADDING_OFF 4
 #define PACKET_PAYLOAD_OFF 5
 
-#define INIT_READBUF 200
+#define INIT_READBUF 128
 
 #endif /* _PACKET_H_ */
diff --git a/process-packet.c b/process-packet.c
index d96c1cb6f7f6e03daef62434169b711ad0fd6161..fe4a543dc0ac53a1c039ec8dbb42f043c44d4249 100644
--- a/process-packet.c
+++ b/process-packet.c
@@ -70,6 +70,7 @@ void process_packet() {
 			dropbear_close("Disconnect received");
 	}
 
+    ses.last_packet_time = time(NULL);
 
 	/* This applies for KEX, where the spec says the next packet MUST be
 	 * NEWKEYS */
diff --git a/runopts.h b/runopts.h
index 6cbdd2652fc6bfbc22a1c18f9fbc7af39971443f..bc35435b34c9ad34b2ec44927bb650cfc34483fc 100644
--- a/runopts.h
+++ b/runopts.h
@@ -38,6 +38,7 @@ typedef struct runopts {
 #endif
 	unsigned int recv_window;
 	unsigned int keepalive_secs;
+	unsigned int idle_timeout_secs;
 
 } runopts;
 
diff --git a/scp.c b/scp.c
index 1d788c90fb5d89e49dc280518231ad116f907bb2..da64dd16939c8083a07f63be99ef6a61692a374a 100644
--- a/scp.c
+++ b/scp.c
@@ -130,13 +130,22 @@ do_local_cmd(arglist *a)
 			fprintf(stderr, " %s", a->list[i]);
 		fprintf(stderr, "\n");
 	}
-	if ((pid = fork()) == -1)
+#ifdef __uClinux__
+	pid = vfork();
+#else
+	pid = fork();
+#endif /* __uClinux__ */
+	if (pid == -1)
 		fatal("do_local_cmd: fork: %s", strerror(errno));
 
 	if (pid == 0) {
 		execvp(a->list[0], a->list);
 		perror(a->list[0]);
+#ifdef __uClinux__
+		_exit(1);
+#else
 		exit(1);
+#endif /* __uClinux__ */
 	}
 
 	do_cmd_pid = pid;
@@ -225,7 +234,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
 
 		execvp(ssh_program, args.list);
 		perror(ssh_program);
+#ifndef __uClinux__
 		exit(1);
+#else
+		_exit(1);
+#endif /* __uClinux__ */
 	} else if (do_cmd_pid == -1) {
 		fatal("fork: %s", strerror(errno));
 	}
diff --git a/session.h b/session.h
index b63a258c009d1f44663a6c258fb5315b6eb1a232..5a4569e39902e111240d3ea8ce6356294faf021a 100644
--- a/session.h
+++ b/session.h
@@ -60,28 +60,36 @@ void cli_session(int sock_in, int sock_out, char *remotehost);
 void cli_session_cleanup();
 void cleantext(unsigned char* dirtytext);
 
+/* crypto parameters that are stored individually for transmit and receive */
+struct key_context_directional {
+	const struct dropbear_cipher *algo_crypt; /* NULL for none */
+	const struct dropbear_cipher_mode *crypt_mode;
+	const struct dropbear_hash *algo_mac; /* NULL for none */
+	int hash_index; /* lookup for libtomcrypt */
+	char algo_comp; /* compression */
+#ifndef DISABLE_ZLIB
+	z_streamp zstream;
+#endif
+	/* actual keys */
+	union {
+		symmetric_CBC cbc;
+#ifdef DROPBEAR_ENABLE_CTR_MODE
+		symmetric_CTR ctr;
+#endif
+	} cipher_state;
+	unsigned char mackey[MAX_MAC_KEY];
+};
+
 struct key_context {
 
-	const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */
-	const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */
-	const struct dropbear_hash *recv_algo_mac; /* NULL for none */
-	const struct dropbear_hash *trans_algo_mac; /* NULL for none */
+	struct key_context_directional recv;
+	struct key_context_directional trans;
+
 	char algo_kex;
 	char algo_hostkey;
 
-	char recv_algo_comp; /* compression */
-	char trans_algo_comp;
-#ifndef DISABLE_ZLIB
-	z_streamp recv_zstream;
-	z_streamp trans_zstream;
-#endif
-
-	/* actual keys */
-	symmetric_CBC recv_symmetric_struct;
-	symmetric_CBC trans_symmetric_struct;
-	unsigned char recvmackey[MAX_MAC_KEY];
-	unsigned char transmackey[MAX_MAC_KEY];
-
+	int allow_compress; /* whether compression has started (useful in 
+							zlib@openssh.com delayed compression case) */
 };
 
 struct packetlist;
@@ -114,8 +122,7 @@ struct sshsession {
 							 throughout the code, as handlers fill out this
 							 buffer with the packet to send. */
 	struct Queue writequeue; /* A queue of encrypted packets to send */
-	buffer *readbuf; /* Encrypted */
-	buffer *decryptreadbuf; /* Post-decryption */
+	buffer *readbuf; /* From the wire, decrypted in-place */
 	buffer *payload; /* Post-decompression, the actual SSH packet */
 	unsigned int transseq, recvseq; /* Sequence IDs */
 
@@ -134,12 +141,16 @@ struct sshsession {
 
 	unsigned char lastpacket; /* What the last received packet type was */
 	
-    int signal_pipe[2]; /* stores endpoints of a self-pipe used for
+	int signal_pipe[2]; /* stores endpoints of a self-pipe used for
 						   race-free signal handling */
 						
-	time_t last_packet_time; /* time of the last packet transmission, for
+	time_t last_trx_packet_time; /* time of the last packet transmission, for
 							keepalive purposes */
 
+	time_t last_packet_time; /* time of the last packet transmission or receive, for
+								idle timeout purposes */
+
+
 	/* KEX/encryption related */
 	struct KEXState kexstate;
 	struct key_context *keys;
diff --git a/svr-auth.c b/svr-auth.c
index 4adf80989bb163a6ea457505163a9de4eea390dd..5da0aa70c2110b1571c3d0989de49f1a70f6cee9 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -368,6 +368,8 @@ void send_msg_userauth_success() {
 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
 	encrypt_packet();
 
+	/* authdone must be set after encrypt_packet() for 
+	 * delayed-zlib mode */
 	ses.authstate.authdone = 1;
 	ses.connect_time = 0;
 
diff --git a/svr-authpubkeyoptions.c b/svr-authpubkeyoptions.c
index 8a92d62d80c70bb1623c29343c7761dad3e6bcc4..13a179def597bd95aae734abee0e78d7dcfbf69a 100644
--- a/svr-authpubkeyoptions.c
+++ b/svr-authpubkeyoptions.c
@@ -105,7 +105,7 @@ void svr_pubkey_options_cleanup() {
 /* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched,
    and increments the options_buf */
 static int match_option(buffer *options_buf, const char *opt_name) {
-	const int len = strlen(opt_name);
+	const unsigned int len = strlen(opt_name);
 	if (options_buf->len - options_buf->pos < len) {
 		return DROPBEAR_FAILURE;
 	}
diff --git a/svr-chansession.c b/svr-chansession.c
index 9b2a41225fe849622a9582fa5afc7a6c060eced8..23dad8c1bbb151b39f4964ad8079dd529c9ce56a 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -663,11 +663,11 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
 	addchildpid(chansess, chansess->pid);
 
 	if (svr_ses.lastexit.exitpid != -1) {
+		unsigned int i;
 		TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
 		/* The child probably exited and the signal handler triggered
 		 * possibly before we got around to adding the childpid. So we fill
 		 * out its data manually */
-		int i;
 		for (i = 0; i < svr_ses.childpidsize; i++) {
 			if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
 				TRACE(("found match for lastexitpid"))
@@ -878,6 +878,7 @@ static void execchild(void *user_data) {
 	addnewvar("LOGNAME", ses.authstate.pw_name);
 	addnewvar("HOME", ses.authstate.pw_dir);
 	addnewvar("SHELL", get_user_shell());
+	addnewvar("PATH", DEFAULT_PATH);
 	if (chansess->term != NULL) {
 		addnewvar("TERM", chansess->term);
 	}
diff --git a/svr-main.c b/svr-main.c
index 8d57084267b4402f1eff6c62dad5d78ce3d901e8..45ea4be733f88699ae315d1b4d74ab0165e37c2b 100644
--- a/svr-main.c
+++ b/svr-main.c
@@ -133,7 +133,7 @@ void main_noinetd() {
 	for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
 		childpipes[i] = -1;
 	}
-	bzero(preauth_addrs, sizeof(preauth_addrs));
+	memset(preauth_addrs, 0x0, sizeof(preauth_addrs));
 	
 	/* Set up the listening sockets */
 	listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
diff --git a/svr-runopts.c b/svr-runopts.c
index c8b6585e342a9a61b13f2fc3dafe36f93f241903..4f1355a6b9e1f88ba6737d284b44cc1f684c67c3 100644
--- a/svr-runopts.c
+++ b/svr-runopts.c
@@ -82,8 +82,9 @@ static void printhelp(const char * progname) {
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d)\n"
+					"-I <idle_timeout>  (0 is never, default %d)\n"
 #ifdef DEBUG_TRACE
-					"-v		verbose\n"
+					"-v		verbose (compiled with DEBUG_TRACE)\n"
 #endif
 					,DROPBEAR_VERSION, progname,
 #ifdef DROPBEAR_DSS
@@ -93,7 +94,7 @@ static void printhelp(const char * progname) {
 					RSA_PRIV_FILENAME,
 #endif
 					DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE,
-					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
+					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
 }
 
 void svr_getopts(int argc, char ** argv) {
@@ -103,6 +104,7 @@ void svr_getopts(int argc, char ** argv) {
 	int nextisport = 0;
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
+	char* idle_timeout_arg = NULL;
 
 	/* see printhelp() for options */
 	svr_opts.rsakeyfile = NULL;
@@ -134,7 +136,8 @@ void svr_getopts(int argc, char ** argv) {
 	svr_opts.usingsyslog = 1;
 #endif
 	opts.recv_window = DEFAULT_RECV_WINDOW;
-	opts.keepalive_secs = DEFAULT_KEEPALIVE;	
+	opts.keepalive_secs = DEFAULT_KEEPALIVE;
+	opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
 	
 #ifdef ENABLE_SVR_REMOTETCPFWD
 	opts.listen_fwd_all = 0;
@@ -218,6 +221,9 @@ void svr_getopts(int argc, char ** argv) {
 				case 'K':
 					next = &keepalive_arg;
 					break;
+				case 'I':
+					next = &idle_timeout_arg;
+					break;
 #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
 				case 's':
 					svr_opts.noauthpass = 1;
@@ -253,7 +259,7 @@ void svr_getopts(int argc, char ** argv) {
 		svr_opts.addresses[0] = m_strdup(DROPBEAR_DEFADDRESS);
 		svr_opts.portcount = 1;
 	}
-        
+
 	if (svr_opts.dsskeyfile == NULL) {
 		svr_opts.dsskeyfile = DSS_PRIV_FILENAME;
 	}
@@ -294,6 +300,12 @@ void svr_getopts(int argc, char ** argv) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
 	}
+
+	if (idle_timeout_arg) {
+		if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
+			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
+		}
+	}
 }
 
 static void addportandaddress(char* spec) {
diff --git a/sysoptions.h b/sysoptions.h
index 6b171515819fa80115879cdfb882d8e609aa2d04..2de118408eb7258cd272a2f49a10c9e43f472314 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -4,7 +4,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "0.51"
+#define DROPBEAR_VERSION "0.52"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
@@ -68,6 +68,7 @@
 
 #define DROPBEAR_COMP_NONE 0
 #define DROPBEAR_COMP_ZLIB 1
+#define DROPBEAR_COMP_ZLIB_DELAY 2
 
 /* Required for pubkey auth */
 #if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
@@ -133,12 +134,12 @@
 												accept for keyb-interactive
 												auth */
 
-#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
-#define DROPBEAR_AES_CBC
+#if defined(DROPBEAR_AES256) || defined(DROPBEAR_AES128)
+#define DROPBEAR_AES
 #endif
 
-#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
-#define DROPBEAR_TWOFISH_CBC
+#if defined(DROPBEAR_TWOFISH256) || defined(DROPBEAR_TWOFISH128)
+#define DROPBEAR_TWOFISH
 #endif
 
 #ifndef ENABLE_X11FWD
@@ -201,5 +202,8 @@
 #define IS_DROPBEAR_CLIENT 1
 
 #else
-#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
+/* Just building key utils? */
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 0
+
 #endif
diff --git a/tcpfwd.h b/tcpfwd.h
index f50515cebff8ec157af9519e0186defcd80742ef..251612edaf92e53c7ab95649efb72a3d8af0f9a1 100644
--- a/tcpfwd.h
+++ b/tcpfwd.h
@@ -49,6 +49,8 @@ struct TCPFwdList {
 	const unsigned char* connectaddr;
 	unsigned int connectport;
 	unsigned int listenport;
+	unsigned int have_reply; /* is set to 1 after a reply has been received
+								when setting up the forwarding */
 	struct TCPFwdList * next;
 
 };
@@ -62,6 +64,8 @@ extern const struct ChanType svr_chan_tcpdirect;
 void setup_localtcp();
 void setup_remotetcp();
 extern const struct ChanType cli_chan_tcpremote;
+void cli_recv_msg_request_success();
+void cli_recv_msg_request_failure();
 
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);