changeset 579:8c737cd7c1af

merge of '48fdaa8706d1acda35e9d564adc9a1fbc96c18c8' and '658fd03abd21e0da7c4c89b9fff9dc693c72daae'
author Matt Johnston <matt@ucc.asn.au>
date Sat, 27 Feb 2010 11:53:18 +0000
parents 69e98c45db7c (current diff) 44f486b72427 (diff)
children dd9947170fc8
files cli-runopts.c cli-tcpfwd.c tcpfwd.h
diffstat 48 files changed, 1339 insertions(+), 663 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Wed Feb 24 16:13:15 2010 +0000
+++ b/Makefile.in	Sat Feb 27 11:53:18 2010 +0000
@@ -20,16 +20,17 @@
 		dss.o bignum.o \
 		signkey.o rsa.o random.o \
 		queue.o \
-		atomicio.o compat.o  fake-rfc2553.o
+		atomicio.o compat.o  fake-rfc2553.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 \
-		cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o
+		cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \
+		cli-agentfwd.o list.o
 
 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
@@ -40,7 +41,7 @@
 
 CONVERTOBJS=dropbearconvert.o keyimport.o
 
-SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o
+SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
 
 HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
 		dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \
--- a/agentfwd.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/agentfwd.h	Sat Feb 27 11:53:18 2010 +0000
@@ -23,21 +23,34 @@
  * SOFTWARE. */
 #ifndef _AGENTFWD_H_
 #define _AGENTFWD_H_
-#ifndef DISABLE_AGENTFWD
 
 #include "includes.h"
 #include "chansession.h"
 #include "channel.h"
+#include "auth.h"
+#include "list.h"
 
-int agentreq(struct ChanSess * chansess);
-void agentsetauth(struct ChanSess *chansess);
-void agentcleanup(struct ChanSess * chansess);
-void agentset(struct ChanSess *chansess);
+/* An agent reply can be reasonably large, as it can
+ * contain a list of all public keys held by the agent.
+ * 10000 is arbitrary */
+#define MAX_AGENT_REPLY  10000
+
+int svr_agentreq(struct ChanSess * chansess);
+void svr_agentcleanup(struct ChanSess * chansess);
+void svr_agentset(struct ChanSess *chansess);
+
+/* client functions */
+void cli_load_agent_keys(m_list * ret_list);
+void agent_buf_sign(buffer *sigblob, sign_key *key, 
+    const unsigned char *data, unsigned int len);
+void cli_setup_agent(struct Channel *channel);
+
 
 #ifdef __hpux
 #define seteuid(a)       setresuid(-1, (a), -1)
 #define setegid(a)       setresgid(-1, (a), -1)
 #endif
 
-#endif /* DROPBEAR_AGENTFWD */
+extern const struct ChanType cli_chan_agent;
+
 #endif /* _AGENTFWD_H_ */
--- a/algo.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/algo.h	Sat Feb 27 11:53:18 2010 +0000
@@ -50,7 +50,8 @@
 extern algo_type sshhostkey[];
 extern algo_type sshciphers[];
 extern algo_type sshhashes[];
-extern algo_type sshcompress[];
+extern algo_type ssh_compress[];
+extern algo_type ssh_nocompress[];
 
 extern const struct dropbear_cipher dropbear_nocipher;
 extern const struct dropbear_cipher_mode dropbear_mode_none;
--- a/auth.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/auth.h	Sat Feb 27 11:53:18 2010 +0000
@@ -26,6 +26,7 @@
 #define _AUTH_H_
 
 #include "includes.h"
+#include "signkey.h"
 #include "chansession.h"
 
 void svr_authinitialise();
@@ -73,6 +74,7 @@
 int cli_auth_pubkey();
 void cli_auth_interactive();
 char* getpass_or_cancel(char* prompt);
+void cli_auth_pubkey_cleanup();
 
 
 #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
@@ -97,7 +99,6 @@
  * relatively little extraneous bits when used for the client rather than the
  * server */
 struct AuthState {
-
 	char *username; /* This is the username the client presents to check. It
 					   is updated each run through, used for auth checking */
 	unsigned char authtypes; /* Flags indicating which auth types are still 
@@ -120,19 +121,6 @@
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
 	struct PubKeyOptions* pubkey_options;
 #endif
-
-};
-
-struct SignKeyList;
-/* A singly linked list of signing keys */
-struct SignKeyList {
-
-	sign_key *key;
-	int type; /* The type of key */
-	struct SignKeyList *next;
-	/* filename? or the buffer? for encrypted keys, so we can later get
-	 * the private key portion */
-
 };
 
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
@@ -145,7 +133,7 @@
 	int no_pty_flag;
 	/* "command=" option. */
 	unsigned char * forced_command;
-
+	unsigned char * original_command;
 };
 #endif
 
--- a/buffer.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/buffer.c	Sat Feb 27 11:53:18 2010 +0000
@@ -223,6 +223,20 @@
 	return ret;
 }
 
+/* Return a string as a newly allocated buffer */
+buffer * buf_getstringbuf(buffer *buf) {
+	buffer *ret;
+	unsigned char* str;
+	unsigned int len;
+	str = buf_getstring(buf, &len);
+	ret = m_malloc(sizeof(*ret));
+	ret->data = str;
+	ret->len = len;
+	ret->size = len;
+	ret->pos = 0;
+	return ret;
+}
+
 /* Just increment the buffer position the same as if we'd used buf_getstring,
  * but don't bother copying/malloc()ing for it */
 void buf_eatstring(buffer *buf) {
--- a/buffer.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/buffer.h	Sat Feb 27 11:53:18 2010 +0000
@@ -55,6 +55,7 @@
 unsigned char* buf_getptr(buffer* buf, unsigned int len);
 unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
 unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
+buffer * buf_getstringbuf(buffer *buf);
 void buf_eatstring(buffer *buf);
 void buf_putint(buffer* buf, unsigned int val);
 void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len);
--- a/channel.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/channel.h	Sat Feb 27 11:53:18 2010 +0000
@@ -58,7 +58,7 @@
 	unsigned int recvmaxpacket, transmaxpacket;
 	void* typedata; /* a pointer to type specific data */
 	int writefd; /* read from wire, written to insecure side */
-	int readfd; /* read from insecure size, written to wire */
+	int readfd; /* read from insecure side, written to wire */
 	int errfd; /* used like writefd or readfd, depending if it's client or server.
 				  Doesn't exactly belong here, but is cleaner here */
 	circbuffer *writebuf; /* data from the wire, for local consumption */
--- a/chansession.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/chansession.h	Sat Feb 27 11:53:18 2010 +0000
@@ -50,6 +50,10 @@
 
 	/* exit details */
 	struct exitinfo exit;
+
+	/* Used to set $SSH_CONNECTION in the child session. 
+	Is only set temporarily before forking */
+	char *connection_string;
 	
 #ifndef DISABLE_X11FWD
 	struct Listener * x11listener;
@@ -60,7 +64,7 @@
 	unsigned char x11singleconn;
 #endif
 
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	struct Listener * agentlistener;
 	char * agentfile;
 	char * agentdir;
@@ -81,6 +85,7 @@
 #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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-agentfwd.c	Sat Feb 27 11:53:18 2010 +0000
@@ -0,0 +1,311 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2005 Matt Johnston
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+
+#ifdef ENABLE_CLI_AGENTFWD
+
+#include "agentfwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+#include "random.h"
+#include "listener.h"
+#include "runopts.h"
+#include "atomicio.h"
+#include "signkey.h"
+#include "auth.h"
+
+/* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in
+   PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */
+
+static int new_agent_chan(struct Channel * channel);
+
+const struct ChanType cli_chan_agent = {
+	0, /* sepfds */
+	"[email protected]",
+	new_agent_chan,
+	NULL,
+	NULL,
+	NULL
+};
+
+static int connect_agent() {
+
+	int fd = -1;
+	char* agent_sock = NULL;
+
+	agent_sock = getenv("SSH_AUTH_SOCK");
+	if (agent_sock == NULL)
+		return -1;
+
+	fd = connect_unix(agent_sock);
+
+	if (fd < 0) {
+		dropbear_log(LOG_INFO, "Failed to connect to agent");
+	}
+
+	return fd;
+}
+
+// handle a request for a connection to the locally running ssh-agent
+// or forward.
+static int new_agent_chan(struct Channel * channel) {
+
+	int fd = -1;
+
+	if (!cli_opts.agent_fwd)
+		return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+	fd = connect_agent();
+	if (cli_opts.agent_fd < 0) {
+		return SSH_OPEN_CONNECT_FAILED;
+	}
+
+	setnonblocking(fd);
+
+	ses.maxfd = MAX(ses.maxfd, fd);
+
+	channel->readfd = fd;
+	channel->writefd = fd;
+
+	// success
+	return 0;
+}
+
+/* Sends a request to the agent, returning a newly allocated buffer
+ * with the response */
+/* This function will block waiting for a response - it will
+ * only be used by client authentication (not for forwarded requests)
+ * won't cause problems for interactivity. */
+/* Packet format (from draft-ylonen)
+   4 bytes     Length, msb first.  Does not include length itself.
+   1 byte      Packet type.  The value 255 is reserved for future extensions.
+   data        Any data, depending on packet type.  Encoding as in the ssh packet
+               protocol.
+*/
+static buffer * agent_request(unsigned char type, buffer *data) {
+
+	buffer * payload = NULL;
+	buffer * inbuf = NULL;
+	size_t readlen = 0;
+	ssize_t ret;
+	const int fd = cli_opts.agent_fd;
+	unsigned int data_len = 0;
+	if (data)
+	{
+		data_len = data->len;
+	}
+
+	payload = buf_new(4 + 1 + data_len);
+
+	buf_putint(payload, 1 + data_len);
+	buf_putbyte(payload, type);
+	if (data) {
+		buf_putbytes(payload, data->data, data->len);
+	}
+	buf_setpos(payload, 0);
+
+	ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len);
+	if ((size_t)ret != payload->len) {
+		TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno)))
+		goto out;
+	}
+
+	buf_free(payload);
+	payload = NULL;
+	TRACE(("Wrote out bytes for agent_request"))
+	/* Now we read the response */
+	inbuf = buf_new(4);
+	ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4);
+	if (ret != 4) {
+		TRACE(("read of length failed for agent_request"))
+		goto out;
+	}
+	buf_setpos(inbuf, 0);
+	buf_setlen(inbuf, ret);
+
+	readlen = buf_getint(inbuf);
+	if (readlen > MAX_AGENT_REPLY) {
+		TRACE(("agent reply is too big"));
+		goto out;
+	}
+	
+	TRACE(("agent_request readlen is %d", readlen))
+
+	buf_resize(inbuf, readlen);
+	buf_setpos(inbuf, 0);
+	ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen);
+	if ((size_t)ret != readlen) {
+		TRACE(("read of data failed for agent_request"))
+		goto out;
+	}
+	buf_incrwritepos(inbuf, readlen);
+	buf_setpos(inbuf, 0);
+	TRACE(("agent_request success, length %d", readlen))
+
+out:
+	if (payload)
+		buf_free(payload);
+
+	return inbuf;
+}
+
+static void agent_get_key_list(m_list * ret_list)
+{
+	buffer * inbuf = NULL;
+	unsigned int num = 0;
+	unsigned char packet_type;
+	unsigned int i;
+	int ret;
+
+	inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL);
+	if (!inbuf) {
+		TRACE(("agent_request failed returning identities"))
+		goto out;
+	}
+
+	/* The reply has a format of:
+		byte			SSH2_AGENT_IDENTITIES_ANSWER
+		uint32			num_keys
+  	   Followed by zero or more consecutive keys, encoded as:
+       	 string			key_blob
+    	 string			key_comment
+	 */
+	packet_type = buf_getbyte(inbuf);
+	if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) {
+		goto out;
+	}
+
+	num = buf_getint(inbuf);
+	for (i = 0; i < num; i++) {
+		sign_key * pubkey = NULL;
+		int key_type = DROPBEAR_SIGNKEY_ANY;
+		buffer * key_buf;
+
+		/* each public key is encoded as a string */
+		key_buf = buf_getstringbuf(inbuf);
+		pubkey = new_sign_key();
+		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");
+		}
+		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);
+	}
+
+out:
+	if (inbuf) {
+		buf_free(inbuf);
+		inbuf = NULL;
+	}
+}
+
+void cli_setup_agent(struct Channel *channel) {
+	if (!getenv("SSH_AUTH_SOCK")) {
+		return;
+	}
+	
+	cli_start_send_channel_request(channel, "[email protected]");
+	/* Don't want replies */
+	buf_putbyte(ses.writepayload, 0);
+	encrypt_packet();
+}
+
+/* Returned keys are prepended to ret_list, which will
+   be updated. */
+void cli_load_agent_keys(m_list *ret_list) {
+	/* agent_fd will be closed after successful auth */
+	cli_opts.agent_fd = connect_agent();
+	if (cli_opts.agent_fd < 0) {
+		return;
+	}
+
+	agent_get_key_list(ret_list);
+}
+
+void agent_buf_sign(buffer *sigblob, sign_key *key, 
+		const unsigned char *data, unsigned int len) {
+	buffer *request_data = buf_new(MAX_PUBKEY_SIZE + len + 12);
+	buffer *response;
+	unsigned int keylen, siglen;
+	int packet_type;
+	
+	/* Request format
+	byte			SSH2_AGENTC_SIGN_REQUEST
+	string			key_blob
+	string			data
+	uint32			flags
+	*/
+	/* We write the key, then figure how long it was and write that */
+	//buf_putint(request_data, 0);
+	buf_put_pub_key(request_data, key, key->type);
+	keylen = request_data->len - 4;
+	//buf_setpos(request_data, 0);
+	//buf_putint(request_data, keylen);
+	
+	//buf_setpos(request_data, request_data->len);
+	buf_putstring(request_data, data, len);
+	buf_putint(request_data, 0);
+	
+	response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
+	buf_free(request_data);
+	
+	if (!response) {
+		goto fail;
+	}
+	
+	packet_type = buf_getbyte(response);
+	if (packet_type != SSH2_AGENT_SIGN_RESPONSE) {
+		goto fail;
+	}
+	
+	/* Response format
+	byte			SSH2_AGENT_SIGN_RESPONSE
+	string			signature_blob
+	*/
+	siglen = buf_getint(response);
+	buf_putbytes(sigblob, buf_getptr(response, siglen), siglen);
+	buf_free(response);
+	
+	return;
+fail:
+	/* XXX don't fail badly here. instead propagate a failure code back up to
+	   the cli auth pubkey code, and just remove this key from the list of 
+	   ones to try. */
+	dropbear_exit("Agent failed signing key");
+}
+
+#endif
--- a/cli-auth.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-auth.c	Sat Feb 27 11:53:18 2010 +0000
@@ -91,7 +91,7 @@
 		}
 	}
 
-	printf("%s\n", banner);
+	fprintf(stderr, "%s\n", banner);
 
 out:
 	m_free(banner);
@@ -234,6 +234,10 @@
 	ses.authstate.authdone = 1;
 	cli_ses.state = USERAUTH_SUCCESS_RCVD;
 	cli_ses.lastauthtype = AUTH_TYPE_NONE;
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	cli_auth_pubkey_cleanup();
+#endif
 }
 
 void cli_auth_try() {
--- a/cli-authpubkey.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-authpubkey.c	Sat Feb 27 11:53:18 2010 +0000
@@ -30,6 +30,7 @@
 #include "ssh.h"
 #include "runopts.h"
 #include "auth.h"
+#include "agentfwd.h"
 
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
@@ -37,30 +38,23 @@
 /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
  * We use it to remove the key we tried from the list */
 void cli_pubkeyfail() {
-
-	struct SignKeyList *keyitem;
-	struct SignKeyList **previtem;
-
-	TRACE(("enter cli_pubkeyfail"))
-	previtem = &cli_opts.privkeys;
-
-	/* Find the key we failed with, and remove it */
-	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
-		if (keyitem == cli_ses.lastprivkey) {
-			*previtem = keyitem->next;
+	m_list_elem *iter;
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
+		sign_key *iter_key = (sign_key*)iter->item;
+		
+		if (iter_key == cli_ses.lastprivkey)
+		{
+			/* found the failing key */
+			list_remove(iter);
+			sign_key_free(iter_key);
+			cli_ses.lastprivkey = NULL;
+			return;
 		}
-		previtem = &keyitem;
 	}
-
-	sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
-	m_free(cli_ses.lastprivkey);
-
-	TRACE(("leave cli_pubkeyfail"))
 }
 
 void recv_msg_userauth_pk_ok() {
-
-	struct SignKeyList *keyitem = NULL;
+	m_list_elem *iter;
 	buffer* keybuf = NULL;
 	char* algotype = NULL;
 	unsigned int algolen;
@@ -80,9 +74,9 @@
 
 	/* Iterate through our keys, find which one it was that matched, and
 	 * send a real request with that key */
-	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
-
-		if (keyitem->type != keytype) {
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
+		sign_key *key = (sign_key*)iter->item;
+		if (key->type != keytype) {
 			/* Types differed */
 			TRACE(("types differed"))
 			continue;
@@ -90,7 +84,7 @@
 
 		/* Now we compare the contents of the key */
 		keybuf->pos = keybuf->len = 0;
-		buf_put_pub_key(keybuf, keyitem->key, keytype);
+		buf_put_pub_key(keybuf, key, keytype);
 		buf_setpos(keybuf, 0);
 		buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
 								   remotelen) which has already been taken from
@@ -114,11 +108,11 @@
 	}
 	buf_free(keybuf);
 
-	if (keyitem != NULL) {
+	if (iter != NULL) {
 		TRACE(("matching key"))
 		/* XXX TODO: if it's an encrypted key, here we ask for their
 		 * password */
-		send_msg_userauth_pubkey(keyitem->key, keytype, 1);
+		send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1);
 	} else {
 		TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
 	}
@@ -126,6 +120,25 @@
 	TRACE(("leave recv_msg_userauth_pk_ok"))
 }
 
+void cli_buf_put_sign(buffer* buf, sign_key *key, int type, 
+			const unsigned char *data, unsigned int len)
+{
+	if (key->source == SIGNKEY_SOURCE_AGENT) {
+		/* Format the agent signature ourselves, as buf_put_sign would. */
+		buffer *sigblob;
+		sigblob = buf_new(MAX_PUBKEY_SIZE);
+		agent_buf_sign(sigblob, key, data, len);
+		buf_setpos(sigblob, 0);
+		buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
+				sigblob->len);
+
+		buf_free(sigblob);
+	} else {
+		buf_put_sign(buf, key, type, data, len);
+	}
+	
+}
+
 /* TODO: make it take an agent reference to use as well */
 static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
 
@@ -161,7 +174,7 @@
 		sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len);
 		buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE);
 		buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
-		buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
+		cli_buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
 		buf_free(sigbuf); /* Nothing confidential in the buffer */
 	}
 
@@ -169,20 +182,41 @@
 	TRACE(("leave send_msg_userauth_pubkey"))
 }
 
+/* Returns 1 if a key was tried */
 int cli_auth_pubkey() {
 
 	TRACE(("enter cli_auth_pubkey"))
 
-	if (cli_opts.privkeys != NULL) {
+	if (!cli_opts.agent_keys_loaded) {
+		/* get the list of available keys from the agent */
+		cli_load_agent_keys(cli_opts.privkeys);
+		cli_opts.agent_keys_loaded = 1;
+	}
+
+	if (cli_opts.privkeys->first) {
+		sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
 		/* Send a trial request */
-		send_msg_userauth_pubkey(cli_opts.privkeys->key,
-				cli_opts.privkeys->type, 0);
-		cli_ses.lastprivkey = cli_opts.privkeys;
+		send_msg_userauth_pubkey(key, key->type, 0);
+		cli_ses.lastprivkey = key;
 		TRACE(("leave cli_auth_pubkey-success"))
 		return 1;
 	} else {
+		/* no more keys left */
 		TRACE(("leave cli_auth_pubkey-failure"))
 		return 0;
 	}
 }
+
+void cli_auth_pubkey_cleanup() {
+
+#ifdef ENABLE_CLI_AGENTFWD
+	m_close(cli_opts.agent_fd);
+	cli_opts.agent_fd = -1;
+#endif
+
+	while (cli_opts.privkeys->first) {
+		sign_key * key = list_remove(cli_opts.privkeys->first);
+		sign_key_free(key);
+	}
+}
 #endif /* Pubkey auth */
--- a/cli-chansession.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-chansession.c	Sat Feb 27 11:53:18 2010 +0000
@@ -33,13 +33,12 @@
 #include "runopts.h"
 #include "termcodes.h"
 #include "chansession.h"
+#include "agentfwd.h"
 
 static void cli_closechansess(struct Channel *channel);
 static int cli_initchansess(struct Channel *channel);
 static void cli_chansessreq(struct Channel *channel);
 
-static void start_channel_request(struct Channel *channel, unsigned char *type);
-
 static void send_chansess_pty_req(struct Channel *channel);
 static void send_chansess_shell_req(struct Channel *channel);
 
@@ -92,7 +91,7 @@
 
 }
 
-static void start_channel_request(struct Channel *channel, 
+void cli_start_send_channel_request(struct Channel *channel, 
 		unsigned char *type) {
 
 	CHECKCLEARTOWRITE();
@@ -287,7 +286,7 @@
 
 	TRACE(("enter send_chansess_pty_req"))
 
-	start_channel_request(channel, "pty-req");
+	cli_start_send_channel_request(channel, "pty-req");
 
 	/* Don't want replies */
 	buf_putbyte(ses.writepayload, 0);
@@ -330,7 +329,7 @@
 		reqtype = "shell";
 	}
 
-	start_channel_request(channel, reqtype);
+	cli_start_send_channel_request(channel, reqtype);
 
 	/* XXX TODO */
 	buf_putbyte(ses.writepayload, 0); /* Don't want replies */
@@ -361,6 +360,12 @@
 
 	cli_init_stdpipe_sess(channel);
 
+#ifdef ENABLE_CLI_AGENTFWD
+	if (cli_opts.agent_fwd) {
+		cli_setup_agent(channel);
+	}
+#endif
+
 	if (cli_opts.wantpty) {
 		send_chansess_pty_req(channel);
 	}
@@ -376,20 +381,20 @@
 
 #ifdef ENABLE_CLI_NETCAT
 
+const struct ChanType cli_chan_netcat = {
+	0, /* sepfds */
+	"direct-tcpip",
+	cli_init_stdpipe_sess, /* inithandler */
+	NULL,
+	NULL,
+	cli_closechansess
+};
+
 void cli_send_netcat_request() {
 
 	const unsigned char* source_host = "127.0.0.1";
 	const int source_port = 22;
 
-	const struct ChanType cli_chan_netcat = {
-		0, /* sepfds */
-		"direct-tcpip",
-		cli_init_stdpipe_sess, /* inithandler */
-		NULL,
-		NULL,
-		cli_closechansess
-	};
-
 	cli_opts.wantpty = 0;
 
 	if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat) 
@@ -424,16 +429,3 @@
 	TRACE(("leave cli_send_chansess_request"))
 
 }
-
-
-#if 0
-	while (cli_opts.localfwds != NULL) {
-		ret = cli_localtcp(cli_opts.localfwds->listenport,
-				cli_opts.localfwds->connectaddr,
-				cli_opts.localfwds->connectport);
-		if (ret == DROPBEAR_FAILURE) {
-			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
-					cli_opts.localfwds->listenport,
-					cli_opts.localfwds->connectaddr,
-					cli_opts.localfwds->connectport);
-#endif
--- a/cli-kex.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-kex.c	Sat Feb 27 11:53:18 2010 +0000
@@ -304,7 +304,7 @@
 		fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */
 		buf_setpos(line, 0);
 		buf_setlen(line, 0);
-		buf_putbytes(line, ses.remotehost, hostlen);
+		buf_putbytes(line, cli_opts.remotehost, hostlen);
 		buf_putbyte(line, ' ');
 		buf_putbytes(line, algoname, algolen);
 		buf_putbyte(line, ' ');
@@ -327,4 +327,5 @@
 	if (line != NULL) {
 		buf_free(line);
 	}
+	m_free(fingerprint);
 }
--- a/cli-main.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-main.c	Sat Feb 27 11:53:18 2010 +0000
@@ -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)
@@ -43,8 +45,6 @@
 
 	int sock_in, sock_out;
 	char* error = NULL;
-	char* hostandport;
-	int len;
 
 	_dropbear_exit = cli_dropbear_exit;
 	_dropbear_log = cli_dropbear_log;
@@ -63,6 +63,7 @@
 #ifdef ENABLE_CLI_PROXYCMD
 	if (cli_opts.proxycmd) {
 		cli_proxy_cmd(&sock_in, &sock_out);
+		m_free(cli_opts.proxycmd);
 	} else
 #endif
 	{
@@ -75,14 +76,7 @@
 		dropbear_exit("%s", error);
 	}
 
-	/* Set up the host:port log */
-	len = strlen(cli_opts.remotehost);
-	len += 10; /* 16 bit port and leeway*/
-	hostandport = (char*)m_malloc(len);
-	snprintf(hostandport, len, "%s:%s", 
-			cli_opts.remotehost, cli_opts.remoteport);
-
-	cli_session(sock_in, sock_out, hostandport);
+	cli_session(sock_in, sock_out);
 
 	/* not reached */
 	return -1;
@@ -132,6 +126,7 @@
 	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 +139,4 @@
 		*sock_in = *sock_out = -1;
 	}
 }
+#endif // ENABLE_CLI_PROXYCMD
--- a/cli-runopts.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-runopts.c	Sat Feb 27 11:53:18 2010 +0000
@@ -29,6 +29,7 @@
 #include "dbutil.h"
 #include "algo.h"
 #include "tcpfwd.h"
+#include "list.h"
 
 cli_runopts cli_opts; /* GLOBAL */
 
@@ -40,7 +41,7 @@
 static void loadidentityfile(const char* filename);
 #endif
 #ifdef ENABLE_CLI_ANYTCPFWD
-static void addforward(const char* str, struct TCPFwdList** fwdlist);
+static void addforward(const char* str, m_list *fwdlist);
 #endif
 #ifdef ENABLE_CLI_NETCAT
 static void add_netcat(const char *str);
@@ -66,6 +67,9 @@
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 					"-i <identityfile>   (multiple allowed)\n"
 #endif
+#ifdef ENABLE_CLI_AGENTFWD
+					"-A    Enable agent auth forwarding\n"
+#endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
 					"-L <listenport:remotehost:remoteport> Local port forwarding\n"
 					"-g    Allow remote hosts to connect to forwarded ports\n"
@@ -91,7 +95,6 @@
 }
 
 void cli_getopts(int argc, char ** argv) {
-
 	unsigned int i, j;
 	char ** next = 0;
 	unsigned int cmdlen;
@@ -112,6 +115,7 @@
 	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];
@@ -125,18 +129,25 @@
 	cli_opts.always_accept_key = 0;
 	cli_opts.is_subsystem = 0;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
-	cli_opts.privkeys = NULL;
+	cli_opts.privkeys = list_new();
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-	cli_opts.localfwds = NULL;
+	cli_opts.localfwds = list_new();
 	opts.listen_fwd_all = 0;
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
-	cli_opts.remotefwds = NULL;
+	cli_opts.remotefwds = list_new();
+#endif
+#ifdef ENABLE_CLI_AGENTFWD
+	cli_opts.agent_fwd = 0;
+	cli_opts.agent_keys_loaded = 0;
 #endif
 #ifdef ENABLE_CLI_PROXYCMD
 	cli_opts.proxycmd = NULL;
 #endif
+#ifndef DISABLE_ZLIB
+	opts.enable_compress = 1;
+#endif
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -158,7 +169,7 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 		if (nextisremote) {
 			TRACE(("nextisremote true"))
-			addforward(argv[i], &cli_opts.remotefwds);
+			addforward(argv[i], cli_opts.remotefwds);
 			nextisremote = 0;
 			continue;
 		}
@@ -166,7 +177,7 @@
 #ifdef ENABLE_CLI_LOCALTCPFWD
 		if (nextislocal) {
 			TRACE(("nextislocal true"))
-			addforward(argv[i], &cli_opts.localfwds);
+			addforward(argv[i], cli_opts.localfwds);
 			nextislocal = 0;
 			continue;
 		}
@@ -266,6 +277,11 @@
 				case 'I':
 					next = &idle_timeout_arg;
 					break;
+#ifdef ENABLE_CLI_AGENTFWD
+				case 'A':
+					cli_opts.agent_fwd = 1;
+					break;
+#endif
 #ifdef DEBUG_TRACE
 				case 'v':
 					debug_trace = 1;
@@ -304,12 +320,8 @@
 
 			/* 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
@@ -338,7 +350,7 @@
 
 	/* And now a few sanity checks and setup */
 
-	if (cli_opts.remotehost == NULL) {
+	if (host_arg == NULL) {
 		printhelp();
 		exit(EXIT_FAILURE);
 	}
@@ -369,15 +381,19 @@
 		}
 	}
 	if (keepalive_arg) {
-		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
+		opts.keepalive_secs = val;
 	}
 
 	if (idle_timeout_arg) {
-		if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
 		}
+		opts.idle_timeout_secs = val;
 	}
 
 #ifdef ENABLE_CLI_NETCAT
@@ -385,36 +401,73 @@
 		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
 static void loadidentityfile(const char* filename) {
-
-	struct SignKeyList * nextkey;
 	sign_key *key;
 	int keytype;
 
 	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->next = cli_opts.privkeys;
-		nextkey->type = keytype;
-		cli_opts.privkeys = nextkey;
+		key->type = keytype;
+		key->source = SIGNKEY_SOURCE_RAW_FILE;
+		key->filename = m_strdup(filename);
+		list_append(cli_opts.privkeys, key);
 	}
 }
 #endif
 
 #ifdef ENABLE_CLI_MULTIHOP
 
+static char*
+multihop_passthrough_args() {
+	char *ret;
+	int total;
+	unsigned int len = 0;
+	m_list_elem *iter;
+	/* Fill out -i and -W options that make sense for all
+	 * the intermediate processes */
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+	{
+		sign_key * key = (sign_key*)iter->item;
+		len += 3 + strlen(key->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 (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+	{
+		sign_key * key = (sign_key*)iter->item;
+		const size_t size = len - total;
+		int written = snprintf(ret+total, size, "-i %s", key->filename);
+		dropbear_assert((unsigned int)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
@@ -429,7 +482,8 @@
  */
 static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
 	char *userhostarg = NULL;
-	char *last_hop = NULL;;
+	char *hostbuf = NULL;
+	char *last_hop = NULL;
 	char *remainder = NULL;
 
 	/* both scp and rsync parse a user@host argument
@@ -441,11 +495,12 @@
 			&& 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) {
@@ -463,19 +518,28 @@
 	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);
+#ifndef DISABLE_ZLIB
+		/* The stream will be incompressible since it's encrypted. */
+		opts.enable_compress = 0;
+#endif
+		m_free(passthrough_args);
 	}
+	m_free(hostbuf);
 }
 #endif /* !ENABLE_CLI_MULTIHOP */
 
@@ -566,14 +630,14 @@
 #ifdef ENABLE_CLI_ANYTCPFWD
 /* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
  * set, and add it to the forwarding list */
-static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
+static void addforward(const char* origstr, m_list *fwdlist) {
 
 	char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
 	char * listenaddr = NULL;
 	char * listenport = NULL;
 	char * connectaddr = NULL;
 	char * connectport = NULL;
-	struct TCPFwdList* newfwd = NULL;
+	struct TCPFwdEntry* newfwd = NULL;
 	char * str = NULL;
 
 	TRACE(("enter addforward"))
@@ -618,7 +682,9 @@
 		connectport = part3;
 	}
 
-	newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList));
+	}
+
+	newfwd = m_malloc(sizeof(struct TCPFwdEntry));
 
 	/* Now we check the ports - note that the port ints are unsigned,
 	 * the check later only checks for >= MAX_PORT */
@@ -646,8 +712,7 @@
 	}
 
 	newfwd->have_reply = 0;
-	newfwd->next = *fwdlist;
-	*fwdlist = newfwd;
+	list_append(fwdlist, newfwd);
 
 	TRACE(("leave addforward: done"))
 	return;
--- a/cli-session.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-session.c	Sat Feb 27 11:53:18 2010 +0000
@@ -35,6 +35,7 @@
 #include "service.h"
 #include "runopts.h"
 #include "chansession.h"
+#include "agentfwd.h"
 
 static void cli_remoteclosed();
 static void cli_sessionloop();
@@ -75,16 +76,19 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	&cli_chan_tcpremote,
 #endif
+#ifdef ENABLE_CLI_AGENTFWD
+	&cli_chan_agent,
+#endif
 	NULL /* Null termination */
 };
 
-void cli_session(int sock_in, int sock_out, char* remotehost) {
+void cli_session(int sock_in, int sock_out) {
 
 	seedrandom();
 
 	crypto_init();
 
-	common_session_init(sock_in, sock_out, remotehost);
+	common_session_init(sock_in, sock_out);
 
 	chaninitialise(cli_chantypes);
 
@@ -231,7 +235,7 @@
 				cli_send_netcat_request();
 			} else 
 #endif
-				if (!cli_opts.no_cmd) {
+			if (!cli_opts.no_cmd) {
 				cli_send_chansess_request();
 			}
 			TRACE(("leave cli_sessionloop: running"))
--- a/cli-tcpfwd.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/cli-tcpfwd.c	Sat Feb 27 11:53:18 2010 +0000
@@ -61,30 +61,25 @@
 
 #ifdef ENABLE_CLI_LOCALTCPFWD
 void setup_localtcp() {
-
+	m_list_elem *iter;
 	int ret;
 
 	TRACE(("enter setup_localtcp"))
 
-	if (cli_opts.localfwds == NULL) {
-		TRACE(("cli_opts.localfwds == NULL"))
-	}
-
-	while (cli_opts.localfwds != NULL) {
+	for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
 		ret = cli_localtcp(
-                cli_opts.localfwds->listenaddr,
-                cli_opts.localfwds->listenport,
-				cli_opts.localfwds->connectaddr,
-				cli_opts.localfwds->connectport);
+				fwd->listenaddr,
+				fwd->listenport,
+				fwd->connectaddr,
+				fwd->connectport);
 		if (ret == DROPBEAR_FAILURE) {
-			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
-					cli_opts.localfwds->listenaddr,
-					cli_opts.localfwds->listenport,
-					cli_opts.localfwds->connectaddr,
-					cli_opts.localfwds->connectport);
-		}
-
-		cli_opts.localfwds = cli_opts.localfwds->next;
+			dropbear_log(LOG_WARNING, "Failed local port forward %s:%d:%s:%d",
+					fwd->listenaddr,
+					fwd->listenport,
+					fwd->connectaddr,
+					fwd->connectport);
+		}		
 	}
 	TRACE(("leave setup_localtcp"))
 
@@ -156,63 +151,49 @@
  * 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;
+	m_list_elem * iter = NULL;
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->have_reply) {
+			fwd->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);
+	m_list_elem *iter;
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->have_reply) {
+			fwd->have_reply = 1;
+			dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", fwd->listenport, fwd->connectaddr, fwd->connectport);
 			return;
 		}
-		iter = iter->next;
 	}
 }
 
 void setup_remotetcp() {
-
-	struct TCPFwdList * iter = NULL;
-
+	m_list_elem *iter;
 	TRACE(("enter setup_remotetcp"))
 
-	if (cli_opts.remotefwds == NULL) {
-		TRACE(("cli_opts.remotefwds == NULL"))
-	}
-
-	iter = cli_opts.remotefwds;
-
-	while (iter != NULL) {
-		if (!iter->listenaddr)
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->listenaddr)
 		{
 			// we store the addresses so that we can compare them
 			// when the server sends them back
 			if (opts.listen_fwd_all) {
-				iter->listenaddr = m_strdup("");
+				fwd->listenaddr = m_strdup("");
 			} else {
-				iter->listenaddr = m_strdup("localhost");
+				fwd->listenaddr = m_strdup("localhost");
 			}
 		}
-		send_msg_global_request_remotetcp(iter->listenaddr, iter->listenport);
-		iter = iter->next;
+		send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
 	}
+
 	TRACE(("leave setup_remotetcp"))
 }
 
@@ -220,7 +201,8 @@
 
     char *origaddr = NULL;
 	unsigned int origport;
-	struct TCPFwdList * iter = NULL;
+	m_list_elem * iter = NULL;
+	struct TCPFwdEntry *fwd;
 	char portstring[NI_MAXSERV];
 	int sock;
 	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
@@ -229,14 +211,12 @@
 	origport = buf_getint(ses.payload);
 
 	/* Find which port corresponds */
-	iter = cli_opts.remotefwds;
-
-	while (iter != NULL) {
-		if (origport == iter->listenport
-            && (strcmp(origaddr, iter->listenaddr) == 0)) {
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		fwd = (struct TCPFwdEntry*)iter->item;
+		if (origport == fwd->listenport
+				&& (strcmp(origaddr, fwd->listenaddr) == 0)) {
 			break;
 		}
-		iter = iter->next;
 	}
 
 	if (iter == NULL) {
@@ -247,8 +227,8 @@
 		goto out;
 	}
 	
-	snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
-	sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
+	snprintf(portstring, sizeof(portstring), "%d", fwd->connectport);
+	sock = connect_remote(fwd->connectaddr, portstring, 1, NULL);
 	if (sock < 0) {
 		TRACE(("leave newtcpdirect: sock failed"))
 		err = SSH_OPEN_CONNECT_FAILED;
@@ -265,7 +245,7 @@
 	err = SSH_OPEN_IN_PROGRESS;
 
 out:
-    m_free(origaddr);
+	m_free(origaddr);
 	TRACE(("leave newtcpdirect: err %d", err))
 	return err;
 }
--- a/common-algo.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/common-algo.c	Sat Feb 27 11:53:18 2010 +0000
@@ -31,7 +31,9 @@
 
 static int void_cipher(const unsigned char* in, unsigned char* out,
 		unsigned long len, void *cipher_state) {
-	memcpy(out, in, len);
+	if (in != out) {
+		memmove(out, in, len);
+	}
 	return CRYPT_OK;
 }
 
@@ -166,11 +168,16 @@
 	{NULL, 0, NULL, 0, NULL}
 };
 
-algo_type sshcompress[] = {
 #ifndef DISABLE_ZLIB
+algo_type ssh_compress[] = {
 	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL},
 	{"[email protected]", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL},
+	{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
+	{NULL, 0, NULL, 0, NULL}
+};
 #endif
+
+algo_type ssh_nocompress[] = {
 	{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
 	{NULL, 0, NULL, 0, NULL}
 };
--- a/common-kex.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/common-kex.c	Sat Feb 27 11:53:18 2010 +0000
@@ -33,6 +33,7 @@
 #include "packet.h"
 #include "bignum.h"
 #include "random.h"
+#include "runopts.h"
 
 /* diffie-hellman-group1-sha1 value for p */
 static const unsigned char dh_p_val[] = {
@@ -91,10 +92,10 @@
 	buf_put_algolist(ses.writepayload, sshhashes);
 
 	/* compression_algorithms_client_to_server */
-	buf_put_algolist(ses.writepayload, sshcompress);
+	buf_put_algolist(ses.writepayload, ses.compress_algos);
 
 	/* compression_algorithms_server_to_client */
-	buf_put_algolist(ses.writepayload, sshcompress);
+	buf_put_algolist(ses.writepayload, ses.compress_algos);
 
 	/* languages_client_to_server */
 	buf_putstring(ses.writepayload, "", 0);
@@ -180,8 +181,16 @@
 
 /* Set up the kex for the first time */
 void kexfirstinitialise() {
+	ses.kexstate.donefirstkex = 0;
 
-	ses.kexstate.donefirstkex = 0;
+#ifndef DISABLE_ZLIB
+	if (opts.enable_compress) {
+		ses.compress_algos = ssh_compress;
+	} else
+#endif
+	{
+		ses.compress_algos = ssh_nocompress;
+	}
 	kexinitialise();
 }
 
@@ -272,8 +281,8 @@
 	    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 +290,8 @@
 	    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,31 +301,33 @@
 	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 (ses.newkeys->recv_crypt_mode->start(recv_cipher, 
+	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) {
+			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 (ses.newkeys->trans_crypt_mode->start(trans_cipher, 
+	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) {
+			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();
@@ -334,15 +345,15 @@
 #ifndef DISABLE_ZLIB
 
 int is_compress_trans() {
-	return ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB
+	return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB
 		|| (ses.authstate.authdone
-			&& ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+			&& ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
 }
 
 int is_compress_recv() {
-	return ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB
+	return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB
 		|| (ses.authstate.authdone
-			&& ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+			&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
 }
 
 /* Set up new zlib compression streams, close the old ones. Only
@@ -350,47 +361,49 @@
 static void gen_new_zstreams() {
 
 	/* create new zstreams */
-	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 (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_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 (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 (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION,
+					Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS, 
+					DROPBEAR_ZLIB_MEM_LEVEL, Z_DEFAULT_STRATEGY)
 				!= 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 /* DISABLE_ZLIB */
@@ -666,7 +679,7 @@
 	TRACE(("hash s2c is  %s", s2c_hash_algo->name))
 
 	/* compression_algorithms_client_to_server */
-	c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+	c2s_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
 	if (c2s_comp_algo == NULL) {
 		erralgo = "comp c->s";
 		goto error;
@@ -674,7 +687,7 @@
 	TRACE(("hash c2s is  %s", c2s_comp_algo->name))
 
 	/* compression_algorithms_server_to_client */
-	s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+	s2c_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
 	if (s2c_comp_algo == NULL) {
 		erralgo = "comp s->c";
 		goto error;
@@ -698,36 +711,36 @@
 
 	/* 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_crypt_mode = 
+		ses.newkeys->recv.crypt_mode = 
 			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
-		ses.newkeys->trans_crypt_mode =
+		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
-		ses.newkeys->recv_algo_mac = 
+		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_crypt_mode =
+		ses.newkeys->recv.crypt_mode =
 			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
-		ses.newkeys->trans_crypt_mode =
+		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
-		ses.newkeys->recv_algo_mac = 
+		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 */
--- a/common-session.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/common-session.c	Sat Feb 27 11:53:18 2010 +0000
@@ -52,12 +52,10 @@
 
 
 /* called only at the start of a session, set up initial state */
-void common_session_init(int sock_in, int sock_out, char* remotehost) {
+void common_session_init(int sock_in, int sock_out) {
 
 	TRACE(("enter session_init"))
 
-	ses.remotehost = remotehost;
-
 	ses.sock_in = sock_in;
 	ses.sock_out = sock_out;
 	ses.maxfd = MAX(sock_in, sock_out);
@@ -71,6 +69,9 @@
 	}
 	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 */
 
@@ -78,7 +79,6 @@
 	ses.transseq = 0;
 
 	ses.readbuf = NULL;
-	ses.decryptreadbuf = NULL;
 	ses.payload = NULL;
 	ses.recvseq = 0;
 
@@ -95,22 +95,22 @@
 	/* 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_crypt_mode = &dropbear_mode_none;
-	ses.keys->trans_crypt_mode = &dropbear_mode_none;
+	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 */
--- a/configure.in	Wed Feb 24 16:13:15 2010 +0000
+++ b/configure.in	Sat Feb 27 11:53:18 2010 +0000
@@ -82,7 +82,8 @@
 	],,,)
 
 # 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,
@@ -145,6 +146,7 @@
 		if test "x$enableval" = "xyes"; then
 			AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***]))
 			AC_MSG_NOTICE(Enabling PAM)
+			AC_CHECK_FUNCS(pam_fail_delay)
 		else
 			AC_DEFINE(DISABLE_PAM,, Use PAM)
 			AC_MSG_NOTICE(Disabling PAM)
--- a/dbclient.1	Wed Feb 24 16:13:15 2010 +0000
+++ b/dbclient.1	Sat Feb 27 11:53:18 2010 +0000
@@ -82,6 +82,11 @@
 Always accept hostkeys if they are unknown. If a hostkey mismatch occurs the
 connection will abort as normal.
 .TP
+.B \-A
+Forward agent connections to the remote host. dbclient will use any
+OpenSSH-style agent program if available ($SSH_AUTH_SOCK will be set) for
+public key authentication.  Forwarding is only enabled if -A is specified.
+.TP
 .B \-W \fIwindowsize
 Specify the per-channel receive window buffer size. Increasing this 
 may improve network performance at the expense of memory use. Use -h to see the
--- a/dbutil.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/dbutil.c	Sat Feb 27 11:53:18 2010 +0000
@@ -295,6 +295,28 @@
 	return nsock;
 }
 
+/* Connect to a given unix socket. The socket is blocking */
+#ifdef ENABLE_CONNECT_UNIX
+int connect_unix(const char* path) {
+	struct sockaddr_un addr;
+	int fd = -1;
+
+	memset((void*)&addr, 0x0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		TRACE(("Failed to open unix socket"))
+		return -1;
+	}
+	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+		TRACE(("Failed to connect to '%s' socket", path))
+		return -1;
+	}
+	return fd;
+}
+#endif
+
 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
  * return immediately if nonblocking is set. On failure, if errstring
  * wasn't null, it will be a newly malloced error message */
@@ -341,15 +363,7 @@
 		}
 
 		if (nonblocking) {
-			if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
-				close(sock);
-				sock = -1;
-				if (errstring != NULL && *errstring == NULL) {
-					*errstring = m_strdup("Failed non-blocking");
-				}
-				TRACE(("Failed non-blocking: %s", strerror(errno)))
-				continue;
-			}
+			setnonblocking(sock);
 		}
 
 		if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
@@ -525,14 +539,47 @@
 	execv(usershell, argv);
 }
 
+void get_socket_address(int fd, char **local_host, char **local_port,
+						char **remote_host, char **remote_port, int host_lookup)
+{
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	
+	if (local_host || local_port) {
+		addrlen = sizeof(addr);
+		if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
+			dropbear_exit("Failed socket address: %s", strerror(errno));
+		}
+		getaddrstring(&addr, local_host, local_port, host_lookup);		
+	}
+	if (remote_host || remote_port) {
+		addrlen = sizeof(addr);
+		if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
+			dropbear_exit("Failed socket address: %s", strerror(errno));
+		}
+		getaddrstring(&addr, remote_host, remote_port, host_lookup);		
+	}
+}
+
 /* Return a string representation of the socket address passed. The return
  * value is allocated with malloc() */
-unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
+void getaddrstring(struct sockaddr_storage* addr, 
+			char **ret_host, char **ret_port,
+			int host_lookup) {
 
-	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
-	char *retstring = NULL;
+	char host[NI_MAXHOST+1], serv[NI_MAXSERV+1];
+	unsigned int len;
 	int ret;
-	unsigned int len;
+	
+	int flags = NI_NUMERICSERV | NI_NUMERICHOST;
+
+#ifndef DO_HOST_LOOKUP
+	host_lookup = 0;
+#endif
+	
+	if (host_lookup) {
+		flags = NI_NUMERICSERV;
+	}
 
 	len = sizeof(struct sockaddr_storage);
 	/* Some platforms such as Solaris 8 require that len is the length
@@ -550,67 +597,28 @@
 #endif
 #endif
 
-	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), 
-			sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
+	ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, 
+			serv, sizeof(serv)-1, flags);
 
 	if (ret != 0) {
-		/* This is a fairly bad failure - it'll fallback to IP if it
-		 * just can't resolve */
-		dropbear_exit("failed lookup (%d, %d)", ret, errno);
-	}
-
-	if (withport) {
-		len = strlen(hbuf) + 2 + strlen(sbuf);
-		retstring = (char*)m_malloc(len);
-		snprintf(retstring, len, "%s:%s", hbuf, sbuf);
-	} else {
-		retstring = m_strdup(hbuf);
+		if (host_lookup) {
+			/* On some systems (Darwin does it) we get EINTR from getnameinfo
+			 * somehow. Eew. So we'll just return the IP, since that doesn't seem
+			 * to exhibit that behaviour. */
+			getaddrstring(addr, ret_host, ret_port, 0);
+			return;
+		} else {
+			/* if we can't do a numeric lookup, something's gone terribly wrong */
+			dropbear_exit("Failed lookup: %s", gai_strerror(ret));
+		}
 	}
 
-	return retstring;
-
-}
-
-/* Get the hostname corresponding to the address addr. On failure, the IP
- * address is returned. The return value is allocated with strdup() */
-char* getaddrhostname(struct sockaddr_storage * addr) {
-
-	char hbuf[NI_MAXHOST];
-	char sbuf[NI_MAXSERV];
-	int ret;
-	unsigned int len;
-#ifdef DO_HOST_LOOKUP
-	const int flags = NI_NUMERICSERV;
-#else
-	const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
-#endif
-
-	len = sizeof(struct sockaddr_storage);
-	/* Some platforms such as Solaris 8 require that len is the length
-	 * of the specific structure. */
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
-	if (addr->ss_family == AF_INET) {
-		len = sizeof(struct sockaddr_in);
+	if (ret_host) {
+		*ret_host = m_strdup(host);
 	}
-#ifdef AF_INET6
-	if (addr->ss_family == AF_INET6) {
-		len = sizeof(struct sockaddr_in6);
+	if (ret_port) {
+		*ret_port = m_strdup(serv);
 	}
-#endif
-#endif
-
-
-	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
-			sbuf, sizeof(sbuf), flags);
-
-	if (ret != 0) {
-		/* On some systems (Darwin does it) we get EINTR from getnameinfo
-		 * somehow. Eew. So we'll just return the IP, since that doesn't seem
-		 * to exhibit that behaviour. */
-		return getaddrstring(addr, 0);
-	}
-
-	return m_strdup(hbuf);
 }
 
 #ifdef DEBUG_TRACE
--- a/dbutil.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/dbutil.h	Sat Feb 27 11:53:18 2010 +0000
@@ -46,15 +46,20 @@
 extern int debug_trace;
 #endif
 char * stripcontrol(const char * text);
-unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
+void get_socket_address(int fd, char **local_host, char **local_port,
+		char **remote_host, char **remote_port, int host_lookup);
+void getaddrstring(struct sockaddr_storage* addr, 
+		char **ret_host, char **ret_port, int host_lookup);
 int dropbear_listen(const char* address, const char* port,
 		int *socks, unsigned int sockcount, char **errstring, int *maxfd);
 int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
 		int *writefd, int *readfd, int *errfd, pid_t *pid);
 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
+#ifdef ENABLE_CONNECT_UNIX
+int connect_unix(const char* addr);
+#endif
 int connect_remote(const char* remotehost, const char* remoteport,
 		int nonblocking, char ** errstring);
-char* getaddrhostname(struct sockaddr_storage * addr);
 int buf_readfile(buffer* buf, const char* filename);
 int buf_getline(buffer * line, FILE * authfile);
 
--- a/debug.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/debug.h	Sat Feb 27 11:53:18 2010 +0000
@@ -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
--- a/dropbear.8	Wed Feb 24 16:13:15 2010 +0000
+++ b/dropbear.8	Sat Feb 27 11:53:18 2010 +0000
@@ -7,7 +7,7 @@
 .I banner\fR] [\-d
 .I dsskey\fR] [\-r
 .I rsakey\fR] [\-p
-.IR port ]
+.IR [address:]port ]
 .SH DESCRIPTION
 .B dropbear
 is a SSH 2 server designed to be small enough to be used in small memory
@@ -154,6 +154,34 @@
 disabled at compile-time). This can also be disabled per-user
 by creating a file ~/.hushlogin .
 
+.SH ENVIRONMENT VARIABLES
+Dropbear sets the standard variables USER, LOGNAME, HOME, SHELL, PATH, and TERM.
+
+The variables below are set for sessions as appropriate. 
+
+.TP
+.B SSH_TTY
+This is set to the allocated TTY if a PTY was used.
+
+.TP
+.B SSH_CONNECTION
+Contains "<remote_ip> <remote_port> <local_ip> <local_port>".
+
+.TP
+.B DISPLAY
+Set X11 forwarding is used.
+
+.TP
+.B SSH_ORIGINAL_COMMAND
+If a 'command=' authorized_keys option was used, the original command is specified
+in this variable. If a shell was requested this is set to an empty value.
+
+.TP
+.B SSH_AUTH_SOCK
+Set to a forwarded ssh-agent connection.
+
+
+
 .SH AUTHOR
 Matt Johnston ([email protected]).
 .br
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.c	Sat Feb 27 11:53:18 2010 +0000
@@ -0,0 +1,49 @@
+#include "options.h"
+#include "dbutil.h"
+#include "list.h"
+
+void list_append(m_list *list, void *item) {
+	m_list_elem *elem;
+	
+	elem = m_malloc(sizeof(*elem));
+	elem->item = item;
+	elem->list = list;
+	elem->next = NULL;
+	if (!list->first) {
+		list->first = elem;
+		elem->prev = NULL;
+	} else {
+		elem->prev = list->last;
+		list->last->next = elem;
+	}
+	list->last = elem;
+}
+
+m_list * list_new() {
+	m_list *ret = m_malloc(sizeof(m_list));
+	ret->first = ret->last = NULL;
+	return ret;
+}
+
+void * list_remove(m_list_elem *elem) {
+	void *item = elem->item;
+	m_list *list = elem->list;
+	if (list->first == elem)
+	{
+		list->first = elem->next;
+	}
+	if (list->last == elem)
+	{
+		list->last = elem->prev;
+	}
+	if (elem->prev)
+	{
+		elem->prev->next = elem->next;
+	}
+	if (elem->next)
+	{
+		elem->next->prev = elem->prev;
+	}
+	m_free(elem);
+	return item;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.h	Sat Feb 27 11:53:18 2010 +0000
@@ -0,0 +1,28 @@
+#ifndef _DROPBEAR_LIST_H
+#define _DROPBEAR_LIST_H
+
+struct _m_list;
+
+struct _m_list_elem {
+    void *item;
+	struct _m_list_elem *next;
+	struct _m_list_elem *prev;
+    struct _m_list *list;
+};
+	
+typedef struct _m_list_elem m_list_elem;
+
+struct _m_list {
+    m_list_elem *first;
+    m_list_elem *last;
+};
+
+typedef struct _m_list m_list;
+
+m_list * list_new();
+void list_append(m_list *list, void *item);
+/* returns the item for the element removed */
+void * list_remove(m_list_elem *elem);
+
+
+#endif /* _DROPBEAR_LIST_H */
\ No newline at end of file
--- a/options.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/options.h	Sat Feb 27 11:53:18 2010 +0000
@@ -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
@@ -64,7 +65,8 @@
 #define ENABLE_SVR_REMOTETCPFWD
 
 /* Enable Authentication Agent Forwarding - server only for now */
-#define ENABLE_AGENTFWD
+#define ENABLE_SVR_AGENTFWD
+#define ENABLE_CLI_AGENTFWD
 
 
 /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
@@ -85,7 +87,8 @@
 #define DROPBEAR_AES128
 #define DROPBEAR_3DES
 #define DROPBEAR_AES256
-#define DROPBEAR_BLOWFISH
+/* Compiling in Blowfish will add ~6kB to runtime heap memory usage */
+/*#define DROPBEAR_BLOWFISH*/
 #define DROPBEAR_TWOFISH256
 #define DROPBEAR_TWOFISH128
 
@@ -128,6 +131,21 @@
  * if the random number source isn't good. In general this isn't required */
 /* #define DSS_PROTOK */
 
+/* Control the memory/performance/compression tradeoff for zlib.
+ * Set windowBits=8, memLevel=1 for least memory usage, see your system's
+ * zlib.h for full details.
+ * Default settings (windowBits=15, memLevel=8) will use 
+ * 256kB for compression + 32kB for decompression.
+ * windowBits=8, memLevel=1 will use 10kB compression + 32kB decompression.
+ * Note that windowBits is only set for deflate() - inflate() always uses the
+ * default of 15 so as to interoperate with other clients. */
+#ifndef DROPBEAR_ZLIB_WINDOW_BITS
+#define DROPBEAR_ZLIB_WINDOW_BITS 15 
+#endif
+#ifndef DROPBEAR_ZLIB_MEM_LEVEL
+#define DROPBEAR_ZLIB_MEM_LEVEL 8
+#endif
+
 /* Whether to do reverse DNS lookups. */
 #define DO_HOST_LOOKUP
 
@@ -154,7 +172,8 @@
 /*#define ENABLE_SVR_PAM_AUTH*/
 #define ENABLE_SVR_PUBKEY_AUTH
 
-/* Wether to ake public key options in authorized_keys file into account */
+/* Whether to take public key options in 
+ * authorized_keys file into account */
 #ifdef ENABLE_SVR_PUBKEY_AUTH
 #define ENABLE_SVR_PUBKEY_OPTIONS
 #endif
@@ -220,7 +239,7 @@
 /* The command to invoke for xauth when using X11 forwarding.
  * "-q" for quiet */
 #ifndef XAUTH_COMMAND
-#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q"
+#define XAUTH_COMMAND "/usr/bin/X11/xauth -q"
 #endif
 
 /* if you want to enable running an sftp server (such as the one included with
@@ -246,13 +265,19 @@
    significant difference to network performance. 24kB was empirically
    chosen for a 100mbit ethernet network. The value can be altered at
    runtime with the -W argument. */
+#ifndef DEFAULT_RECV_WINDOW
 #define DEFAULT_RECV_WINDOW 24576
+#endif
 /* Maximum size of a received SSH data packet - this _MUST_ be >= 32768
    in order to interoperate with other implementations */
+#ifndef RECV_MAX_PAYLOAD_LEN
 #define RECV_MAX_PAYLOAD_LEN 32768
+#endif
 /* Maximum size of a transmitted data packet - this can be any value,
    though increasing it may not make a significant difference. */
+#ifndef TRANS_MAX_PAYLOAD_LEN
 #define TRANS_MAX_PAYLOAD_LEN 16384
+#endif
 
 /* Ensure that data is transmitted every KEEPALIVE seconds. This can
 be overridden at runtime with -K. 0 disables keepalives */
--- a/packet.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/packet.c	Sat Feb 27 11:53:18 2010 +0000
@@ -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
@@ -102,18 +104,18 @@
 	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;
 		}
@@ -121,7 +123,6 @@
 
 	/* 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);
 
@@ -151,60 +152,61 @@
 
 /* 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;
-	unsigned char blocksize;
-	unsigned char macsize;
+	int slen;
+	unsigned int len;
+	unsigned int blocksize;
+	unsigned int 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;
 			
 	/* read the rest of the packet if possible */
-	len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
+	slen = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
 			maxlen);
-	if (len == 0) {
+	if (slen == 0) {
 		ses.remoteclosed();
 	}
-	if (len < 0) {
+	if (slen < 0) {
 		if (errno == EINTR) {
 			TRACE(("leave read_packet_init: EINTR"))
-			return;
+			return DROPBEAR_FAILURE;
 		}
 		dropbear_exit("error reading: %s", strerror(errno));
 	}
 
-	buf_incrwritepos(ses.readbuf, len);
+	buf_incrwritepos(ses.readbuf, slen);
 
-	if ((unsigned int)len != maxlen) {
+	if ((unsigned int)slen != 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_crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
-				buf_getwriteptr(ses.decryptreadbuf,blocksize),
+	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) {
+				&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;
 
-	buf_setpos(ses.readbuf, blocksize);
+	TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
+
 
 	/* check packet length */
 	if ((len > RECV_MAX_PACKET_LEN) ||
@@ -213,9 +215,12 @@
 		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 */
@@ -227,69 +232,58 @@
 	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 it */
-	while (ses.readbuf->pos < ses.readbuf->len - macsize) {
-		if (ses.keys->recv_crypt_mode->decrypt(
-					buf_getptr(ses.readbuf, blocksize), 
-					buf_getwriteptr(ses.decryptreadbuf, blocksize),
-					blocksize,
-					&ses.keys->recv_cipher_state) != 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 - macsize;
 	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 (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++;
@@ -297,49 +291,22 @@
 	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;
@@ -354,7 +321,7 @@
 	buffer * ret;
 	z_streamp zstream;
 
-	zstream = ses.keys->recv_zstream;
+	zstream = ses.keys->recv.zstream;
 	ret = buf_new(len);
 
 	zstream->avail_in = len;
@@ -450,11 +417,12 @@
 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 clear_len;
+	unsigned int len, encrypt_buf_size;
+	unsigned char mac_bytes[MAX_MAC_LEN];
 	
 	type = ses.writepayload->data[0];
 	TRACE(("enter encrypt_packet()"))
@@ -468,34 +436,36 @@
 		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) */
-	clear_len = (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
-	clear_len += 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
-	clearwritebuf = buf_new(clear_len);
-	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 (is_compress_trans()) {
-		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
+		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 */
@@ -504,53 +474,45 @@
 
 	/* 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);
+	buf_setpos(writebuf, writebuf->len);
+	buf_incrlen(writebuf, padlen);
+	genrandom(buf_getptr(writebuf, 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);
+	make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
 
-	/* encrypt it */
-	while (clearwritebuf->pos < clearwritebuf->len) {
-		if (ses.keys->trans_crypt_mode->encrypt(
-					buf_getptr(clearwritebuf, blocksize),
-					buf_getwriteptr(writebuf, blocksize),
-					blocksize,
-					&ses.keys->trans_cipher_state) != CRYPT_OK) {
-			dropbear_exit("error encrypting");
-		}
-		buf_incrpos(clearwritebuf, blocksize);
-		buf_incrwritepos(writebuf, blocksize);
+	/* 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");
 	}
-
-	/* now add a hmac and we're done */
-	writemac(writebuf, clearwritebuf);
+	buf_incrpos(writebuf, len);
 
-	/* clearwritebuf is finished with */
-	buf_free(clearwritebuf);
-	clearwritebuf = NULL;
+    /* stick the MAC on it */
+    buf_putbytes(writebuf, mac_bytes, mac_size);
 
-	/* enqueue the packet for sending */
+	/* enqueue the packet for sending. It will get freed after transmission. */
 	buf_setpos(writebuf, 0);
 	enqueue(&ses.writequeue, (void*)writebuf);
 
@@ -563,47 +525,43 @@
 
 
 /* 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"))
 }
@@ -620,29 +578,29 @@
 
 	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(),
--- a/packet.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/packet.h	Sat Feb 27 11:53:18 2010 +0000
@@ -44,6 +44,6 @@
 #define PACKET_PADDING_OFF 4
 #define PACKET_PAYLOAD_OFF 5
 
-#define INIT_READBUF 200
+#define INIT_READBUF 128
 
 #endif /* _PACKET_H_ */
--- a/random.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/random.c	Sat Feb 27 11:53:18 2010 +0000
@@ -69,12 +69,8 @@
 #endif
 
 #ifdef DROPBEAR_PRNGD_SOCKET
-	memset((void*)&egdsock, 0x0, sizeof(egdsock));
-	egdsock.sun_family = AF_UNIX;
-	strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
-			sizeof(egdsock.sun_path));
+	readfd = connect_unix(DROPBEAR_PRNGD_SOCKET);
 
-	readfd = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (readfd < 0) {
 		dropbear_exit("couldn't open random device");
 	}
--- a/runopts.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/runopts.h	Sat Feb 27 11:53:18 2010 +0000
@@ -37,8 +37,16 @@
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	unsigned int keepalive_secs;
-	unsigned int idle_timeout_secs;
+	time_t keepalive_secs;
+	time_t idle_timeout_secs;
+
+#ifndef DISABLE_ZLIB
+	/* TODO: add a commandline flag. Currently this is on by default if compression
+	 * is compiled in, but disabled for a client's non-final multihop stages. (The
+	 * intermediate stages are compressed streams, so are uncompressible. */
+	int enable_compress;
+#endif
+
 
 } runopts;
 
@@ -112,13 +120,20 @@
 	int backgrounded;
 	int is_subsystem;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
-	struct SignKeyList *privkeys; /* Keys to use for public-key auth */
+	m_list *privkeys; /* Keys to use for public-key auth */
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
-	struct TCPFwdList * remotefwds;
+	m_list * remotefwds;
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-	struct TCPFwdList * localfwds;
+	m_list * localfwds;
+#endif
+#ifdef ENABLE_CLI_AGENTFWD
+	int agent_fwd;
+	int agent_keys_loaded; /* whether pubkeys has been populated with a 
+							  list of keys held by the agent */
+	int agent_fd; /* The agent fd is only set during authentication. Forwarded
+	                 agent sessions have their own file descriptors */
 #endif
 
 #ifdef ENABLE_CLI_NETCAT
@@ -128,7 +143,6 @@
 #ifdef ENABLE_CLI_PROXYCMD
 	char *proxycmd;
 #endif
-
 } cli_runopts;
 
 extern cli_runopts cli_opts;
--- a/scp.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/scp.c	Sat Feb 27 11:53:18 2010 +0000
@@ -343,7 +343,7 @@
 			addargs(&args, "-p%s", optarg);
 			break;
 		case 'B':
-			addargs(&args, "-oBatchmode yes");
+			fprintf(stderr, "Note: -B option is disabled in this version of scp");
 			break;
 		case 'l':
 			speed = strtod(optarg, &endp);
@@ -492,9 +492,13 @@
 			addargs(&alist, "%s", ssh_program);
 			if (verbose_mode)
 				addargs(&alist, "-v");
+#if 0
+			// Disabled since dbclient won't understand them
+			// and scp works fine without them.
 			addargs(&alist, "-x");
 			addargs(&alist, "-oClearAllForwardings yes");
 			addargs(&alist, "-n");
+#endif
 
 			*src++ = 0;
 			if (*src == 0)
--- a/session.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/session.h	Sat Feb 27 11:53:18 2010 +0000
@@ -41,7 +41,7 @@
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
 
-void common_session_init(int sock_in, int sock_out, char* remotehost);
+void common_session_init(int sock_in, int sock_out);
 void session_loop(void(*loophandler)());
 void common_session_cleanup();
 void session_identification();
@@ -51,51 +51,45 @@
 void fill_passwd(const char* username);
 
 /* Server */
-void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
+void svr_session(int sock, int childpipe);
 void svr_dropbear_exit(int exitcode, const char* format, va_list param);
 void svr_dropbear_log(int priority, const char* format, va_list param);
 
 /* Client */
-void cli_session(int sock_in, int sock_out, char *remotehost);
+void cli_session(int sock_in, int sock_out);
 void cli_session_cleanup();
 void cleantext(unsigned char* dirtytext);
 
-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_cipher_mode *recv_crypt_mode;
-	const struct dropbear_cipher_mode *trans_crypt_mode;
-	const struct dropbear_hash *recv_algo_mac; /* NULL for none */
-	const struct dropbear_hash *trans_algo_mac; /* NULL for none */
-	char algo_kex;
-	char algo_hostkey;
-
-	char recv_algo_comp; /* compression */
-	char trans_algo_comp;
-	int allow_compress; /* whether compression has started (useful in 
-							[email protected] delayed compression case) */
+/* 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 recv_zstream;
-	z_streamp trans_zstream;
+	z_streamp zstream;
 #endif
-
 	/* actual keys */
 	union {
 		symmetric_CBC cbc;
 #ifdef DROPBEAR_ENABLE_CTR_MODE
 		symmetric_CTR ctr;
 #endif
-	} recv_cipher_state;
-	union {
-		symmetric_CBC cbc;
-#ifdef DROPBEAR_ENABLE_CTR_MODE
-		symmetric_CTR ctr;
-#endif
-	} trans_cipher_state;
-	unsigned char recvmackey[MAX_MAC_KEY];
-	unsigned char transmackey[MAX_MAC_KEY];
+	} cipher_state;
+	unsigned char mackey[MAX_MAC_KEY];
+};
+
+struct key_context {
 
+	struct key_context_directional recv;
+	struct key_context_directional trans;
+
+	char algo_kex;
+	char algo_hostkey;
+
+	int allow_compress; /* whether compression has started (useful in 
+							[email protected] delayed compression case) */
 };
 
 struct packetlist;
@@ -116,8 +110,6 @@
 	int sock_in;
 	int sock_out;
 
-	unsigned char *remotehost; /* the peer hostname */
-
 	unsigned char *remoteident;
 
 	int maxfd; /* the maximum file descriptor to check with select() */
@@ -128,8 +120,7 @@
 							 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 */
 
@@ -169,6 +160,9 @@
 	buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
 	buffer* transkexinit; /* the kexinit packet we send should be kept so we
 							 can add it to the hash when generating keys */
+
+	/* Enables/disables compression */
+	algo_type *compress_algos;
 							
 	/* a list of queued replies that should be sent after a KEX has
 	   concluded (ie, while dataallowed was unset)*/
@@ -220,6 +214,13 @@
 	/* The numeric address they connected from, used for logging */
 	char * addrstring;
 
+	/* The resolved remote address, used for lastlog etc */
+	char *remotehost;
+
+#ifdef __uClinux__
+	pid_t server_pid;
+#endif
+
 };
 
 typedef enum {
@@ -268,7 +269,7 @@
 									  info request from the server for
 									  interactive auth.*/
 #endif
-	struct SignKeyList *lastprivkey;
+	sign_key *lastprivkey;
 
 	int retval; /* What the command exit status was - we emulate it */
 #if 0
--- a/signkey.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/signkey.c	Sat Feb 27 11:53:18 2010 +0000
@@ -40,8 +40,10 @@
 #ifdef DROPBEAR_RSA
 	ret->rsakey = NULL;
 #endif
+	ret->filename = NULL;
+	ret->type = DROPBEAR_SIGNKEY_NONE;
+	ret->source = SIGNKEY_SOURCE_INVALID;
 	return ret;
-
 }
 
 /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
@@ -81,6 +83,8 @@
 	}
 #endif
 
+	TRACE(("signkey_type_from_name unexpected key type."))
+
 	return DROPBEAR_SIGNKEY_NONE;
 }
 
@@ -101,8 +105,11 @@
 	m_free(ident);
 
 	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
+		TRACE(("buf_get_pub_key bad type - got %d, expected %d", keytype, type))
 		return DROPBEAR_FAILURE;
 	}
+	
+	TRACE(("buf_get_pub_key keytype is %d"))
 
 	*type = keytype;
 
@@ -255,6 +262,8 @@
 	key->rsakey = NULL;
 #endif
 
+	m_free(key->filename);
+
 	m_free(key);
 	TRACE(("leave sign_key_free"))
 }
@@ -358,7 +367,6 @@
 		const unsigned char *data, unsigned int len) {
 
 	buffer *sigblob;
-
 	sigblob = buf_new(MAX_PUBKEY_SIZE);
 
 #ifdef DROPBEAR_DSS
@@ -374,7 +382,6 @@
 	if (sigblob->len == 0) {
 		dropbear_exit("non-matching signing type");
 	}
-
 	buf_setpos(sigblob, 0);
 	buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
 			sigblob->len);
--- a/signkey.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/signkey.h	Sat Feb 27 11:53:18 2010 +0000
@@ -29,8 +29,22 @@
 #include "dss.h"
 #include "rsa.h"
 
+
+/* Sources for signing keys */
+typedef enum {
+	SIGNKEY_SOURCE_RAW_FILE,
+	SIGNKEY_SOURCE_AGENT,
+	SIGNKEY_SOURCE_INVALID,	
+} signkey_source;
+
 struct SIGN_key {
 
+	int type; /* The type of key (dss or rsa) */
+	signkey_source source;
+	char *filename;
+	/* the buffer? for encrypted keys, so we can later get
+	 * the private key portion */
+
 #ifdef DROPBEAR_DSS
 	dss_key * dsskey;
 #endif
--- a/ssh.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/ssh.h	Sat Feb 27 11:53:18 2010 +0000
@@ -105,3 +105,14 @@
 #define SSH_SIGNKEY_DSS_LEN 7
 #define SSH_SIGNKEY_RSA "ssh-rsa"
 #define SSH_SIGNKEY_RSA_LEN 7
+
+/* Agent commands. These aren't part of the spec, and are defined
+ * only on the openssh implementation. */
+#define SSH_AGENT_FAILURE			5
+#define SSH_AGENT_SUCCESS			6
+#define SSH2_AGENTC_REQUEST_IDENTITIES		11
+#define SSH2_AGENT_IDENTITIES_ANSWER		12
+#define SSH2_AGENTC_SIGN_REQUEST		13
+#define SSH2_AGENT_SIGN_RESPONSE		14
+
+#define SSH2_AGENT_FAILURE			30
--- a/svr-agentfwd.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-agentfwd.c	Sat Feb 27 11:53:18 2010 +0000
@@ -49,10 +49,12 @@
 
 /* Handles client requests to start agent forwarding, sets up listening socket.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int agentreq(struct ChanSess * chansess) {
+int svr_agentreq(struct ChanSess * chansess) {
 
 	int fd;
 
+	TRACE(("enter svr_agentreq"))
+
 	if (!svr_pubkey_allows_agentfwd()) {
 		return DROPBEAR_FAILURE;
 	}
@@ -89,10 +91,12 @@
 	}
 
 	return DROPBEAR_SUCCESS;
+	TRACE(("success"))
 
 fail:
+	TRACE(("fail"))
 	/* cleanup */
-	agentcleanup(chansess);
+	svr_agentcleanup(chansess);
 
 	return DROPBEAR_FAILURE;
 }
@@ -118,7 +122,7 @@
 
 /* set up the environment variable pointing to the socket. This is called
  * just before command/shell execution, after dropping priveleges */
-void agentset(struct ChanSess * chansess) {
+void svr_agentset(struct ChanSess * chansess) {
 
 	char *path = NULL;
 	int len;
@@ -137,7 +141,7 @@
 }
 
 /* close the socket, remove the socket-file */
-void agentcleanup(struct ChanSess * chansess) {
+void svr_agentcleanup(struct ChanSess * chansess) {
 
 	char *path = NULL;
 	uid_t uid;
@@ -181,7 +185,7 @@
 
 }
 
-static const struct ChanType chan_agent = {
+static const struct ChanType chan_svr_agent = {
 	0, /* sepfds */
 	"[email protected]",
 	NULL,
@@ -194,7 +198,7 @@
 /* helper for accepting an agent request */
 static int send_msg_channel_open_agent(int fd) {
 
-	if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) {
+	if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) {
 		encrypt_packet();
 		return DROPBEAR_SUCCESS;
 	} else {
--- a/svr-auth.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-auth.c	Sat Feb 27 11:53:18 2010 +0000
@@ -33,6 +33,7 @@
 #include "packet.h"
 #include "auth.h"
 #include "runopts.h"
+#include "random.h"
 
 static void authclear();
 static int checkusername(unsigned char *username, unsigned int userlen);
@@ -337,7 +338,12 @@
 	encrypt_packet();
 
 	if (incrfail) {
-		usleep(300000); /* XXX improve this */
+		unsigned int delay;
+		genrandom((unsigned char*)&delay, sizeof(delay));
+		/* We delay for 300ms +- 50ms, 0.1ms granularity */
+		delay = 250000 + (delay % 1000)*100;
+		usleep(delay);
+		dropbear_log(LOG_INFO, "delay is %d", delay);
 		ses.authstate.failcount++;
 	}
 
--- a/svr-authpam.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-authpam.c	Sat Feb 27 11:53:18 2010 +0000
@@ -102,7 +102,7 @@
 				/* We don't recognise the prompt as asking for a password,
 				   so can't handle it. Add more above as required for
 				   different pam modules/implementations */
-				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
 				break;
@@ -123,12 +123,15 @@
 
 		case PAM_PROMPT_ECHO_ON:
 
-			if (!((strcmp(compare_message, "login:" ) == 0) 
-				|| (strcmp(compare_message, "please enter username:") == 0))) {
+			if (!(
+				(strcmp(compare_message, "login:" ) == 0) 
+				|| (strcmp(compare_message, "please enter username:") == 0)
+				|| (strcmp(compare_message, "username:") == 0)
+				)) {
 				/* We don't recognise the prompt as asking for a username,
 				   so can't handle it. Add more above as required for
 				   different pam modules/implementations */
-				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
 				break;
@@ -212,7 +215,10 @@
 		goto cleanup;
 	}
 
+#ifdef HAVE_PAM_FAIL_DELAY
+	/* We have our own random delay code already, disable PAM's */
 	(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
+#endif
 
 	/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */
 
--- a/svr-authpubkeyoptions.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-authpubkeyoptions.c	Sat Feb 27 11:53:18 2010 +0000
@@ -88,10 +88,20 @@
 	return 1;
 }
 
-/* Set chansession command to the one forced by 'command' public key option */
+/* Set chansession command to the one forced 
+ * by any 'command' public key option. */
 void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
-	if (ses.authstate.pubkey_options)
+	if (ses.authstate.pubkey_options) {
+		ses.authstate.pubkey_options->original_command = chansess->cmd;
+		if (!chansess->cmd)
+		{
+			ses.authstate.pubkey_options->original_command = m_strdup("");
+		}
 		chansess->cmd = ses.authstate.pubkey_options->forced_command;
+#ifdef LOG_COMMANDS
+		dropbear_log(LOG_INFO, "command forced to '%s'", ses.authstate.pubkey_options->original_command);
+#endif
+	}
 }
 
 /* Free potential public key options */
@@ -124,7 +134,6 @@
 	TRACE(("enter addpubkeyoptions"))
 
 	ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
-	memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options));
 
 	buf_setpos(options_buf, 0);
 	while (options_buf->pos < options_buf->len) {
--- a/svr-chansession.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-chansession.c	Sat Feb 27 11:53:18 2010 +0000
@@ -222,6 +222,7 @@
 
 	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
 	chansess->cmd = NULL;
+	chansess->connection_string = NULL;
 	chansess->pid = 0;
 
 	/* pty details */
@@ -250,6 +251,14 @@
 
 }
 
+static struct logininfo* 
+chansess_login_alloc(struct ChanSess *chansess) {
+	struct logininfo * li;
+	li = login_alloc_entry(chansess->pid, ses.authstate.username,
+			svr_ses.remotehost, chansess->tty);
+	return li;
+}
+
 /* clean a session channel */
 static void closechansess(struct Channel *channel) {
 
@@ -273,8 +282,7 @@
 
 	if (chansess->tty) {
 		/* write the utmp/wtmp login record */
-		li = login_alloc_entry(chansess->pid, ses.authstate.username,
-				ses.remotehost, chansess->tty);
+		li = chansess_login_alloc(chansess);
 		login_logout(li);
 		login_free_entry(li);
 
@@ -287,7 +295,7 @@
 #endif
 
 #ifndef DISABLE_AGENTFWD
-	agentcleanup(chansess);
+	svr_agentcleanup(chansess);
 #endif
 
 	/* clear child pid entries */
@@ -346,7 +354,7 @@
 #endif
 #ifndef DISABLE_AGENTFWD
 	} else if (strcmp(type, "[email protected]") == 0) {
-		ret = agentreq(chansess);
+		ret = svr_agentreq(chansess);
 #endif
 	} else if (strcmp(type, "signal") == 0) {
 		ret = sessionsignal(chansess);
@@ -570,6 +578,21 @@
 	return DROPBEAR_SUCCESS;
 }
 
+static char* make_connection_string() {
+	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);
+	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
  * and command-execution requests, and passes the command to
  * noptycommand or ptycommand as appropriate.
@@ -589,9 +612,6 @@
 		return DROPBEAR_FAILURE;
 	}
 
-	/* take public key option 'command' into account */
-	svr_pubkey_set_forced_command(chansess);
-
 	if (iscmd) {
 		/* "exec" */
 		if (chansess->cmd == NULL) {
@@ -616,6 +636,9 @@
 			}
 		}
 	}
+	
+	/* take public key option 'command' into account */
+	svr_pubkey_set_forced_command(chansess);
 
 #ifdef LOG_COMMANDS
 	if (chansess->cmd) {
@@ -627,6 +650,12 @@
 	}
 #endif
 
+	/* uClinux will vfork(), so there'll be a race as 
+	connection_string is freed below. */
+#ifndef __uClinux__
+	chansess->connection_string = make_connection_string();
+#endif
+
 	if (chansess->term == NULL) {
 		/* no pty */
 		ret = noptycommand(channel, chansess);
@@ -635,6 +664,10 @@
 		ret = ptycommand(channel, chansess);
 	}
 
+#ifndef __uClinux__	
+	m_free(chansess->connection_string);
+#endif
+
 	if (ret == DROPBEAR_FAILURE) {
 		m_free(chansess->cmd);
 	}
@@ -736,13 +769,10 @@
 
 		/* write the utmp/wtmp login record - must be after changing the
 		 * terminal used for stdout with the dup2 above */
-		li= login_alloc_entry(getpid(), ses.authstate.username,
-				ses.remotehost, chansess->tty);
+		li = chansess_login_alloc(chansess);
 		login_login(li);
 		login_free_entry(li);
 
-		m_free(chansess->tty);
-
 #ifdef DO_MOTD
 		if (svr_opts.domotd) {
 			/* don't show the motd if ~/.hushlogin exists */
@@ -883,6 +913,22 @@
 		addnewvar("TERM", chansess->term);
 	}
 
+	if (chansess->tty) {
+		addnewvar("SSH_TTY", chansess->tty);
+	}
+	
+	if (chansess->connection_string) {
+		addnewvar("SSH_CONNECTION", chansess->connection_string);
+	}
+	
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+	if (ses.authstate.pubkey_options &&
+			ses.authstate.pubkey_options->original_command) {
+		addnewvar("SSH_ORIGINAL_COMMAND", 
+			ses.authstate.pubkey_options->original_command);
+	}
+#endif
+
 	/* change directory */
 	if (chdir(ses.authstate.pw_dir) < 0) {
 		dropbear_exit("error changing directory");
@@ -894,7 +940,7 @@
 #endif
 #ifndef DISABLE_AGENTFWD
 	/* set up agent env variable */
-	agentset(chansess);
+	svr_agentset(chansess);
 #endif
 
 	usershell = m_strdup(get_user_shell());
--- a/svr-main.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-main.c	Sat Feb 27 11:53:18 2010 +0000
@@ -77,22 +77,16 @@
 
 #ifdef INETD_MODE
 static void main_inetd() {
-
-	struct sockaddr_storage remoteaddr;
-	socklen_t remoteaddrlen;
-	char * addrstring = NULL;
+	char *host, *port = NULL;
 
 	/* Set up handlers, syslog, seed random */
 	commonsetup();
 
-	remoteaddrlen = sizeof(remoteaddr);
-	if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
-		dropbear_exit("Unable to getpeername: %s", strerror(errno));
-	}
-
 	/* In case our inetd was lax in logging source addresses */
-	addrstring = getaddrstring(&remoteaddr, 1);
-	dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+	get_socket_address(0, NULL, NULL, &host, &port, 0);
+	dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port);
+	m_free(host);
+	m_free(port);
 
 	/* Don't check the return value - it may just fail since inetd has
 	 * already done setsid() after forking (xinetd on Darwin appears to do
@@ -102,7 +96,7 @@
 	/* Start service program 
 	 * -1 is a dummy childpipe, just something we can close() without 
 	 * mattering. */
-	svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
+	svr_session(0, -1);
 
 	/* notreached */
 }
@@ -133,7 +127,7 @@
 	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);
@@ -218,14 +212,13 @@
 
 		/* handle each socket which has something to say */
 		for (i = 0; i < listensockcount; i++) {
-
-			struct sockaddr_storage remoteaddr;
-			socklen_t remoteaddrlen = 0;
 			size_t num_unauthed_for_addr = 0;
 			size_t num_unauthed_total = 0;
-			char * remote_addr_str = NULL;
+			char *remote_host = NULL, *remote_port = NULL;
 			pid_t fork_ret = 0;
 			size_t conn_idx = 0;
+			struct sockaddr_storage remoteaddr;
+			socklen_t remoteaddrlen;
 
 			if (!FD_ISSET(listensocks[i], &fds)) 
 				continue;
@@ -240,14 +233,14 @@
 			}
 
 			/* Limit the number of unauthenticated connections per IP */
-			remote_addr_str = getaddrstring(&remoteaddr, 0);
+			getaddrstring(&remoteaddr, &remote_host, NULL, 0);
 
 			num_unauthed_for_addr = 0;
 			num_unauthed_total = 0;
 			for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
 				if (childpipes[j] >= 0) {
 					num_unauthed_total++;
-					if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
+					if (strcmp(remote_host, preauth_addrs[j]) == 0) {
 						num_unauthed_for_addr++;
 					}
 				} else {
@@ -280,21 +273,21 @@
 				/* parent */
 				childpipes[conn_idx] = childpipe[0];
 				m_close(childpipe[1]);
-				preauth_addrs[conn_idx] = remote_addr_str;
-				remote_addr_str = NULL;
+				preauth_addrs[conn_idx] = remote_host;
+				remote_host = NULL;
 
 			} else {
 
 				/* child */
-				char * addrstring = NULL;
 #ifdef DEBUG_FORKGPROF
 				extern void _start(void), etext(void);
 				monstartup((u_long)&_start, (u_long)&etext);
 #endif /* DEBUG_FORKGPROF */
 
-				m_free(remote_addr_str);
-				addrstring = getaddrstring(&remoteaddr, 1);
-				dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+				getaddrstring(&remoteaddr, NULL, &remote_port, 0);
+				dropbear_log(LOG_INFO, "Child connection from %s:%s", remote_host, remote_port);
+				m_free(remote_host);
+				m_free(remote_port);
 
 #ifndef DEBUG_NOFORK
 				if (setsid() < 0) {
@@ -310,9 +303,7 @@
 				m_close(childpipe[0]);
 
 				/* start the session */
-				svr_session(childsock, childpipe[1], 
-								getaddrhostname(&remoteaddr),
-								addrstring);
+				svr_session(childsock, childpipe[1]);
 				/* don't return */
 				dropbear_assert(0);
 			}
@@ -320,8 +311,8 @@
 out:
 			/* This section is important for the parent too */
 			m_close(childsock);
-			if (remote_addr_str) {
-				m_free(remote_addr_str);
+			if (remote_host) {
+				m_free(remote_host);
 			}
 		}
 	} /* for(;;) loop */
--- a/svr-runopts.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-runopts.c	Sat Feb 27 11:53:18 2010 +0000
@@ -125,6 +125,9 @@
 #ifdef ENABLE_SVR_REMOTETCPFWD
 	svr_opts.noremotetcp = 0;
 #endif
+#ifndef DISABLE_ZLIB
+	opts.enable_compress = 1;
+#endif
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -296,15 +299,19 @@
 	}
 	
 	if (keepalive_arg) {
-		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
+		opts.keepalive_secs = val;
 	}
 
 	if (idle_timeout_arg) {
-		if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
 		}
+		opts.idle_timeout_secs = val;
 	}
 }
 
--- a/svr-session.c	Wed Feb 24 16:13:15 2010 +0000
+++ b/svr-session.c	Sat Feb 27 11:53:18 2010 +0000
@@ -74,23 +74,36 @@
 	NULL /* Null termination is mandatory. */
 };
 
-void svr_session(int sock, int childpipe, 
-		char* remotehost, char *addrstring) {
-
+void svr_session(int sock, int childpipe) {
+	char *host, *port;
+	size_t len;
     reseedrandom();
 
 	crypto_init();
-	common_session_init(sock, sock, remotehost);
+	common_session_init(sock, sock);
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
-	svr_ses.addrstring = addrstring;
+#ifdef __uClinux__
+	svr_ses.server_pid = getpid();
+#endif
 	svr_authinitialise();
 	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;
+	svr_ses.addrstring = m_malloc(len);
+	snprintf(svr_ses.addrstring, len, "%s:%s", host, port);
+	m_free(host);
+	m_free(port);
+
+	get_socket_address(ses.sock_in, NULL, NULL, 
+			&svr_ses.remotehost, NULL, 1);
+
 	/* set up messages etc */
 	ses.remoteclosed = svr_remoteclosed;
 
@@ -144,11 +157,20 @@
 
 	_dropbear_log(LOG_INFO, fmtbuf, param);
 
-	/* free potential public key options */
-	svr_pubkey_options_cleanup();
+#ifdef __uClinux__
+	/* only the main server process should cleanup - we don't want
+	 * forked children doing that */
+	if (svr_ses.server_pid == getpid())
+#else
+	if (1)
+#endif
+	{
+		/* free potential public key options */
+		svr_pubkey_options_cleanup();
 
-	/* must be after we've done with username etc */
-	common_session_cleanup();
+		/* must be after we've done with username etc */
+		common_session_cleanup();
+	}
 
 	exit(exitcode);
 
--- a/sysoptions.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/sysoptions.h	Sat Feb 27 11:53:18 2010 +0000
@@ -146,10 +146,6 @@
 #define DISABLE_X11FWD
 #endif
 
-#ifndef ENABLE_AGENTFWD
-#define DISABLE_AGENTFWD
-#endif
-
 #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
 #define ENABLE_CLI_ANYTCPFWD 
 #endif
@@ -160,7 +156,7 @@
 
 #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
 	defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
-	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+	defined(ENABLE_SVR_AGENTFWD) || defined(ENABLE_X11FWD)
 #define USING_LISTENERS
 #endif
 
@@ -168,6 +164,10 @@
 #define ENABLE_CLI_MULTIHOP
 #endif
 
+#if defined(ENABLE_CLI_AGENTFWD) || defined(DROPBEAR_PRNGD_SOCKET)
+#define ENABLE_CONNECT_UNIX
+#endif
+
 #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
 #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
 #endif
@@ -202,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
--- a/tcpfwd.h	Wed Feb 24 16:13:15 2010 +0000
+++ b/tcpfwd.h	Sat Feb 27 11:53:18 2010 +0000
@@ -25,6 +25,7 @@
 #define _TCPFWD_H
 
 #include "channel.h"
+#include "list.h"
 
 struct TCPListener {
 
@@ -43,17 +44,14 @@
 	enum {direct, forwarded} tcp_type;
 };
 
-/* A link in a list of forwards */
-struct TCPFwdList {
-
+/* A forwarding entry */
+struct TCPFwdEntry {
 	const unsigned char* connectaddr;
 	unsigned int connectport;
 	const unsigned char* listenaddr;
 	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;
-
 };
 
 /* Server */
@@ -71,5 +69,4 @@
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);
 
-
 #endif