changeset 26:0969767bca0d

snapshot of stuff
author Matt Johnston <matt@ucc.asn.au>
date Mon, 26 Jul 2004 02:44:20 +0000
parents e4b6e2d569b2
children 08da099e8337
files algo.h cli-algo.c cli-kex.c cli-main.c cli-session.c common-kex.c common-session.c dbutil.c dbutil.h debug.h kex.h main.c options.h process-packet.c session.h signkey.c svr-algo.c svr-kex.c svr-session.c tcpfwd-direct.c
diffstat 20 files changed, 750 insertions(+), 321 deletions(-) [+]
line wrap: on
line diff
--- a/algo.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/algo.h	Mon Jul 26 02:44:20 2004 +0000
@@ -66,10 +66,9 @@
 int have_algo(char* algo, size_t algolen, algo_type algos[]);
 void buf_put_algolist(buffer * buf, algo_type localalgos[]);
 
-algo_type * common_buf_match_algo(buffer* buf, algo_type localalgos[],
-		int *goodguess);
 algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
 		int *goodguess);
-algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]);
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess);
 
 #endif /* _ALGO_H_ */
--- a/cli-algo.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/cli-algo.c	Mon Jul 26 02:44:20 2004 +0000
@@ -33,7 +33,8 @@
  * direction MUST be the first algorithm  on the client's list
  * that is also on the server's list.
  */
-algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) {
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess) {
 
 	unsigned char * algolist = NULL;
 	unsigned char * remotealgos[MAX_PROPOSED_ALGO];
@@ -41,6 +42,8 @@
 	unsigned int count, i, j;
 	algo_type * ret = NULL;
 
+	*goodguess = 0;
+
 	/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
 	algolist = buf_getstring(buf, &len);
 	TRACE(("cli_buf_match_algo: %s", algolist));
@@ -78,6 +81,10 @@
 				if (len == strlen(remotealgos[i]) 
 						&& strncmp(localalgos[j].name, 
 							remotealgos[i], len) == 0) {
+					if (i == 0 && j == 0) {
+						/* was a good guess */
+						*goodguess = 1;
+					}
 					ret = &localalgos[j];
 					goto out;
 				}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-kex.c	Mon Jul 26 02:44:20 2004 +0000
@@ -0,0 +1,91 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2002,2003 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"
+#include "session.h"
+#include "dbutil.h"
+#include "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+#include "runopts.h"
+
+
+
+void send_msg_kexdh_init() {
+
+	cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
+	cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
+
+	m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x);
+	gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
+	buf_putmpint(ses.writepayload, cli_ses.dh_e);
+	encrypt_packet();
+	ses.requirenext = SSH_MSG_KEXDH_REPLY;
+}
+
+/* Handle a diffie-hellman key exchange reply. */
+void recv_msg_kexdh_reply() {
+
+	mp_int dh_f;
+	sign_key *hostkey = NULL;
+	int type;
+
+	type = ses.newkeys->algo_hostkey;
+
+	hostkey = new_sign_key();
+	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
+		dropbear_exit("Bad KEX packet");
+	}
+
+	m_mp_init(&dh_f);
+	if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
+		dropbear_exit("Bad KEX packet");
+	}
+
+	kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
+	mp_clear(&dh_f);
+
+	if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) 
+			!= DROPBEAR_SUCCESS) {
+		dropbear_exit("Bad hostkey signature");
+	}
+
+	/* XXX TODO */
+	dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment");
+
+	sign_key_free(hostkey);
+	hostkey = NULL;
+
+	send_msg_newkeys();
+	ses.requirenext = SSH_MSG_NEWKEYS;
+	TRACE(("leave recv_msg_kexdh_init"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-main.c	Mon Jul 26 02:44:20 2004 +0000
@@ -0,0 +1,33 @@
+#include <includes.h>
+
+int main(int argc, char ** argv) {
+
+	int sock;
+	char* error = NULL;
+	char* hostandport;
+	int len;
+
+	_dropbear_exit = cli_dropbear_exit;
+	_dropbear_log = cli_dropbear_log;
+
+	cli_getopts(argc, argv);
+
+	sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
+			0, &error);
+
+	if (sock < 0) {
+		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%d", 
+			cli_opts.remotehost, cli_opts.remoteport);
+
+	cli_session(sock, hostandport);
+
+	/* not reached */
+	return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-session.c	Mon Jul 26 02:44:20 2004 +0000
@@ -0,0 +1,101 @@
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "tcpfwd-direct.h"
+#include "tcpfwd-remote.h"
+#include "channel.h"
+#include "random.h"
+
+static void cli_remoteclosed();
+static void cli_sessionloop();
+
+struct clientsession cli_ses; /* GLOBAL */
+
+static const packettype cli_packettypes[] = {
+	/* TYPE, AUTHREQUIRED, FUNCTION */
+	{SSH_MSG_KEXINIT, recv_msg_kexinit},
+	{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, // client
+	{SSH_MSG_NEWKEYS, recv_msg_newkeys},
+	{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
+	{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
+	{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
+	{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
+	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
+	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
+	{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
+	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
+	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
+	{0, 0} /* End */
+};
+
+static const struct ChanType *cli_chantypes[] = {
+//	&clichansess,
+	/* &chan_tcpdirect etc, though need to only allow if we've requested
+	 * that forwarding */
+	NULL /* Null termination */
+};
+void cli_session(int sock, char* remotehost) {
+
+	crypto_init();
+	common_session_init(sock, remotehost);
+
+	chaninitialise(cli_chantypes);
+
+	/* For printing "remote host closed" for the user */
+	session_remoteclosed = cli_remoteclosed;
+
+	/* packet handlers */
+	ses.packettypes = cli_packettypes;
+
+	/* Ready to go */
+	sessinitdone = 1;
+
+	/* Exchange identification */
+	session_identification();
+
+	seedrandom();
+
+	send_msg_kexinit();
+
+	/* XXX here we do stuff differently */
+
+	session_loop(cli_sessionloop);
+
+	/* Not reached */
+
+
+}
+
+static void cli_sessionloop() {
+
+	switch (cli_ses.state) {
+
+		KEXINIT_RCVD:
+			/* We initiate the KEX. If DH wasn't the correct type, the KEXINIT
+			 * negotiation would have failed. */
+			send_msg_kexdh_init();
+			cli_ses.state = KEXDH_INIT_SENT;
+			break;
+
+		default:
+			break;
+	}
+
+	if (cli_ses.donefirstkex && !cli_ses.authdone) {
+
+
+
+}
+
+/* called when the remote side closes the connection */
+static void cli_remoteclosed() {
+
+	/* XXX TODO perhaps print a friendlier message if we get this but have
+	 * already sent/received disconnect message(s) ??? */
+	close(ses.sock);
+	ses.sock = -1;
+	dropbear_exit("%s closed the connection", ses.remotehost);
+}
--- a/common-kex.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/common-kex.c	Mon Jul 26 02:44:20 2004 +0000
@@ -193,7 +193,6 @@
 	ses.kexstate.sentnewkeys = 0;
 
 	/* first_packet_follows */
-	/* TODO - currently not handled */
 	ses.kexstate.firstfollows = 0;
 
 	ses.kexstate.datatrans = 0;
@@ -402,47 +401,54 @@
 
 
 	if (IS_DROPBEAR_CLIENT) {
+#ifdef DROPBEAR_CLIENT
 
-	/* read the peer's choice of algos */
-		cli_read_kex();
+		/* read the peer's choice of algos */
+		read_kex_algos(cli_buf_match_algo);
 
-	/* V_C, the client's version string (CR and NL excluded) */
+		/* V_C, the client's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf,
 			(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
-	/* V_S, the server's version string (CR and NL excluded) */
+		/* V_S, the server's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf, 
 			ses.remoteident, strlen((char*)ses.remoteident));
 
-	/* I_C, the payload of the client's SSH_MSG_KEXINIT */
+		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
 	    buf_putstring(ses.kexhashbuf,
 			buf_getptr(ses.transkexinit, ses.transkexinit->len),
 			ses.transkexinit->len);
-	/* I_S, the payload of the server's SSH_MSG_KEXINIT */
+		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_setpos(ses.payload, 0);
 	    buf_putstring(ses.kexhashbuf,
 			buf_getptr(ses.payload, ses.payload->len),
 			ses.payload->len);
 
+		cli_ses.state = KEXINIT_RCVD;
+#endif
 	} else {
+		/* SERVER */
+#ifdef DROPBEAR_SERVER
 
-	/* read the peer's choice of algos */
-		svr_read_kex();
-	/* V_C, the client's version string (CR and NL excluded) */
+		/* read the peer's choice of algos */
+		read_kex_algos(svr_buf_match_algo);
+		/* V_C, the client's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf, 
 			ses.remoteident, strlen((char*)ses.remoteident));
-	/* V_S, the server's version string (CR and NL excluded) */
+		/* V_S, the server's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf,
 			(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
 
-	/* I_C, the payload of the client's SSH_MSG_KEXINIT */
+		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
 	    buf_setpos(ses.payload, 0);
 	    buf_putstring(ses.kexhashbuf,
 			buf_getptr(ses.payload, ses.payload->len),
 			ses.payload->len);
-	/* I_S, the payload of the server's SSH_MSG_KEXINIT */
+		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_putstring(ses.kexhashbuf,
 			buf_getptr(ses.transkexinit, ses.transkexinit->len),
 			ses.transkexinit->len);
+		ses.requirenext = SSH_MSG_KEXDH_INIT;
+#endif
 	}
 
 	buf_free(ses.transkexinit);
@@ -450,9 +456,233 @@
 	/* the rest of ses.kexhashbuf will be done after DH exchange */
 
 	ses.kexstate.recvkexinit = 1;
-	ses.requirenext = SSH_MSG_KEXDH_INIT;
 //	ses.expecting = 0; // client matt
 
 	TRACE(("leave recv_msg_kexinit"));
 }
 
+/* Initialises and generate one side of the diffie-hellman key exchange values.
+ * See the ietf-secsh-transport draft, section 6, for details */
+void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
+
+	mp_int dh_p, dh_q, dh_g;
+	unsigned char randbuf[DH_P_LEN];
+	int dh_q_len;
+
+	TRACE(("enter send_msg_kexdh_reply"));
+	
+	m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL);
+
+	/* read the prime and generator*/
+	if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
+			!= MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	
+	if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* calculate q = (p-1)/2 */
+	/* dh_priv is just a temp var here */
+	if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { 
+		dropbear_exit("Diffie-Hellman error");
+	}
+	if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	dh_q_len = mp_unsigned_bin_size(&dh_q);
+
+	/* calculate our random value dh_y */
+	do {
+		assert((unsigned int)dh_q_len <= sizeof(randbuf));
+		genrandom(randbuf, dh_q_len);
+		if (mp_read_unsigned_bin(dh_priv, randbuf, dh_q_len) != MP_OKAY) {
+			dropbear_exit("Diffie-Hellman error");
+		}
+	} while (mp_cmp(dh_priv, &dh_q) == MP_GT || mp_cmp_d(dh_priv, 0) != MP_GT);
+
+	/* f = g^y mod p */
+	if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL);
+}
+
+/* This function is fairly common between client/server, with some substitution
+ * of dh_e/dh_f etc. Hence these arguments:
+ * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is 
+ * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+		sign_key *hostkey) {
+
+	mp_int dh_p;
+	mp_int *dh_e = NULL, *dh_f = NULL;
+	hash_state hs;
+
+	/* read the prime and generator*/
+	mp_init(&dh_p);
+	if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
+			!= MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
+	if (mp_cmp(dh_pub_them, &dh_p) != MP_LT 
+			|| mp_cmp_d(dh_pub_them, 0) != MP_GT) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	
+	/* K = e^y mod p = f^x mod p */
+	ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
+	m_mp_init(ses.dh_K);
+	if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* clear no longer needed vars */
+	mp_clear_multi(&dh_p, NULL);
+
+	/* From here on, the code needs to work with the _same_ vars on each side,
+	 * not vice-versaing for client/server */
+	if (IS_DROPBEAR_CLIENT) {
+		dh_e = dh_pub_us;
+		dh_f = dh_pub_them;
+	} else {
+		dh_e = dh_pub_them;
+		dh_f = dh_pub_us;
+	} 
+
+	/* Create the remainder of the hash buffer, to generate the exchange hash */
+	/* K_S, the host key */
+	buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+	/* e, exchange value sent by the client */
+	buf_putmpint(ses.kexhashbuf, dh_e);
+	/* f, exchange value sent by the server */
+	buf_putmpint(ses.kexhashbuf, dh_f);
+	/* K, the shared secret */
+	buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+	/* calculate the hash H to sign */
+	sha1_init(&hs);
+	buf_setpos(ses.kexhashbuf, 0);
+	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
+			ses.kexhashbuf->len);
+	sha1_done(&hs, ses.hash);
+	buf_free(ses.kexhashbuf);
+	ses.kexhashbuf = NULL;
+	
+	/* first time around, we set the session_id to H */
+	if (ses.session_id == NULL) {
+		/* create the session_id, this never needs freeing */
+		ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
+		memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
+	}
+}
+
+/* read the other side's algo list. buf_match_algo is a callback to match
+ * algos for the client or server. */
+void read_kex_algos(
+		algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
+			int *goodguess)) {
+
+	algo_type * algo;
+	char * erralgo = NULL;
+
+	int goodguess = 0;
+	int allgood = 1; /* we AND this with each goodguess and see if its still
+						true after */
+
+	buf_incrpos(ses.payload, 16); /* start after the cookie */
+
+	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
+
+	/* kex_algorithms */
+	algo = buf_match_algo(ses.payload, sshkex, &goodguess);
+	allgood &= goodguess;
+	if (algo == NULL) {
+		erralgo = "kex";
+		goto error;
+	}
+	ses.newkeys->algo_kex = algo->val;
+
+	/* server_host_key_algorithms */
+	algo = buf_match_algo(ses.payload, sshhostkey, &goodguess);
+	allgood &= goodguess;
+	if (algo == NULL) {
+		erralgo = "hostkey";
+		goto error;
+	}
+	ses.newkeys->algo_hostkey = algo->val;
+
+	/* encryption_algorithms_client_to_server */
+	algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
+	if (algo == NULL) {
+		erralgo = "enc c->s";
+		goto error;
+	}
+	ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
+
+	/* encryption_algorithms_server_to_client */
+	algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
+	if (algo == NULL) {
+		erralgo = "enc s->c";
+		goto error;
+	}
+	ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
+
+	/* mac_algorithms_client_to_server */
+	algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
+	if (algo == NULL) {
+		erralgo = "mac c->s";
+		goto error;
+	}
+	ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
+
+	/* mac_algorithms_server_to_client */
+	algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
+	if (algo == NULL) {
+		erralgo = "mac s->c";
+		goto error;
+	}
+	ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
+
+	/* compression_algorithms_client_to_server */
+	algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
+	if (algo == NULL) {
+		erralgo = "comp c->s";
+		goto error;
+	}
+	ses.newkeys->recv_algo_comp = algo->val;
+
+	/* compression_algorithms_server_to_client */
+	algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
+	if (algo == NULL) {
+		erralgo = "comp s->c";
+		goto error;
+	}
+	ses.newkeys->trans_algo_comp = algo->val;
+
+	/* languages_client_to_server */
+	buf_eatstring(ses.payload);
+
+	/* languages_server_to_client */
+	buf_eatstring(ses.payload);
+
+	/* first_kex_packet_follows */
+	if (buf_getbyte(ses.payload)) {
+		ses.kexstate.firstfollows = 1;
+		/* if the guess wasn't good, we ignore the packet sent */
+		if (!allgood) {
+			ses.ignorenext = 1;
+		}
+	}
+
+	/* reserved for future extensions */
+	buf_getint(ses.payload);
+	return;
+
+error:
+	dropbear_exit("no matching algo %s", erralgo);
+}
--- a/common-session.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/common-session.c	Mon Jul 26 02:44:20 2004 +0000
@@ -35,6 +35,7 @@
 #include "channel.h"
 #include "atomicio.h"
 
+
 struct sshsession ses; /* GLOBAL */
 
 /* need to know if the session struct has been initialised, this way isn't the
@@ -44,19 +45,18 @@
 /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
 int exitflag = 0; /* GLOBAL */
 
-static int ident_readln(int fd, char* buf, int count);
-
-
 void(*session_remoteclosed)() = NULL;
 
 
+static void checktimeouts();
+static int ident_readln(int fd, char* buf, int count);
+
 /* called only at the start of a session, set up initial state */
-void common_session_init(int sock) {
+void common_session_init(int sock, char* remotehost) {
 
 	TRACE(("enter session_init"));
 
-	ses.remoteaddr = NULL;
-	ses.remotehost = NULL;
+	ses.remotehost = remotehost;
 
 	ses.sock = sock;
 	ses.maxfd = sock;
@@ -114,6 +114,86 @@
 	TRACE(("leave session_init"));
 }
 
+void session_loop(void(*loophandler)()) {
+
+	fd_set readfd, writefd;
+	struct timeval timeout;
+	int val;
+
+	/* main loop, select()s for all sockets in use */
+	for(;;) {
+
+		timeout.tv_sec = SELECT_TIMEOUT;
+		timeout.tv_usec = 0;
+		FD_ZERO(&writefd);
+		FD_ZERO(&readfd);
+		assert(ses.payload == NULL);
+		if (ses.sock != -1) {
+			FD_SET(ses.sock, &readfd);
+			if (!isempty(&ses.writequeue)) {
+				FD_SET(ses.sock, &writefd);
+			}
+		}
+
+		/* set up for channels which require reading/writing */
+		if (ses.dataallowed) {
+			setchannelfds(&readfd, &writefd);
+		}
+		val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
+
+		if (exitflag) {
+			dropbear_exit("Terminated by signal");
+		}
+		
+		if (val < 0) {
+			if (errno == EINTR) {
+				continue;
+			} else {
+				dropbear_exit("Error in select");
+			}
+		}
+
+		/* check for auth timeout, rekeying required etc */
+		checktimeouts();
+		
+		if (val == 0) {
+			/* timeout */
+			TRACE(("select timeout"));
+			continue;
+		}
+
+		/* process session socket's incoming/outgoing data */
+		if (ses.sock != -1) {
+			if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+				write_packet();
+			}
+
+			if (FD_ISSET(ses.sock, &readfd)) {
+				read_packet();
+			}
+			
+			/* Process the decrypted packet. After this, the read buffer
+			 * will be ready for a new packet */
+			if (ses.payload != NULL) {
+				process_packet();
+			}
+		}
+
+		/* process pipes etc for the channels, ses.dataallowed == 0
+		 * during rekeying ) */
+		if (ses.dataallowed) {
+			channelio(&readfd, &writefd);
+		}
+
+		if (loophandler) {
+			loophandler();
+		}
+
+	} /* for(;;) */
+	
+	/* Not reached */
+}
+
 /* clean up a session on exit */
 void common_session_cleanup() {
 	
@@ -134,35 +214,7 @@
 	TRACE(("leave session_cleanup"));
 }
 
-/* Check all timeouts which are required. Currently these are the time for
- * user authentication, and the automatic rekeying. */
-void checktimeouts() {
 
-	struct timeval tv;
-	long secs;
-
-	if (gettimeofday(&tv, 0) < 0) {
-		dropbear_exit("Error getting time");
-	}
-
-	secs = tv.tv_sec;
-	
-	if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
-			dropbear_close("Timeout before auth");
-	}
-
-	/* we can't rekey if we haven't done remote ident exchange yet */
-	if (ses.remoteident == NULL) {
-		return;
-	}
-
-	if (!ses.kexstate.sentkexinit
-			&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
-			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
-		TRACE(("rekeying after timeout or max data reached"));
-		send_msg_kexinit();
-	}
-}
 void session_identification() {
 
 	/* max length of 255 chars */
@@ -268,3 +320,33 @@
 	return pos+1;
 }
 
+/* Check all timeouts which are required. Currently these are the time for
+ * user authentication, and the automatic rekeying. */
+static void checktimeouts() {
+
+	struct timeval tv;
+	long secs;
+
+	if (gettimeofday(&tv, 0) < 0) {
+		dropbear_exit("Error getting time");
+	}
+
+	secs = tv.tv_sec;
+	
+	if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
+			dropbear_close("Timeout before auth");
+	}
+
+	/* we can't rekey if we haven't done remote ident exchange yet */
+	if (ses.remoteident == NULL) {
+		return;
+	}
+
+	if (!ses.kexstate.sentkexinit
+			&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
+			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
+		TRACE(("rekeying after timeout or max data reached"));
+		send_msg_kexinit();
+	}
+}
+
--- a/dbutil.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/dbutil.c	Mon Jul 26 02:44:20 2004 +0000
@@ -113,6 +113,95 @@
 }
 #endif /* DEBUG_TRACE */
 
+/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
+ * return immediately if nonblocking is set */
+int connect_remote(const char* remotehost, const char* remoteport,
+		int nonblocking, char ** errstring) {
+
+	struct addrinfo *res0 = NULL, *res = NULL, hints;
+	int sock;
+	int err;
+
+	TRACE(("enter connect_remote"));
+
+	if (errstring != NULL) {
+		*errstring = NULL;
+	}
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = PF_UNSPEC;
+
+	err = getaddrinfo(remotehost, remoteport, &hints, &res0);
+	if (err) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(gai_strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+		}
+		TRACE(("Error resolving: %s", gai_strerror(err)));
+		return -1;
+	}
+
+	sock = -1;
+	err = EADDRNOTAVAIL;
+	for (res = res0; res; res = res->ai_next) {
+
+		sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+		if (sock < 0) {
+			err = errno;
+			continue;
+		}
+
+		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;
+			}
+		}
+
+		if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			if (errno == EINPROGRESS) {
+				TRACE(("Connect in progress"));
+				break;
+			} else {
+				err = errno;
+				close(sock);
+				sock = -1;
+				continue;
+			}
+		}
+
+		break; /* Success */
+	}
+
+	if (sock < 0) {
+		/* Failed */
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error connecting: %s", strerror(err));
+		}
+		TRACE(("Error connecting: %s", strerror(err)));
+	} else {
+		/* Success */
+		/* (err is used as a dummy var here) */
+		setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&err, sizeof(err));
+	}
+
+	freeaddrinfo(res0);
+
+	TRACE(("leave connect_remote: sock %d", sock));
+	return sock;
+}
+
 /* Return a string representation of the socket address passed. The return
  * value is allocated with malloc() */
 unsigned char * getaddrstring(struct sockaddr * addr) {
@@ -304,3 +393,4 @@
 		*p++ = 0x66;
 	}
 }
+
--- a/dbutil.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/dbutil.h	Mon Jul 26 02:44:20 2004 +0000
@@ -45,6 +45,8 @@
 #endif
 char * stripcontrol(const char * text);
 unsigned char * getaddrstring(struct sockaddr * addr);
+int connect_remote(const char* remotehost, const char* remoteport,
+		int nonblocking, char ** errstring);
 char* getaddrhostname(struct sockaddr * addr);
 int buf_readfile(buffer* buf, const char* filename);
 
@@ -56,4 +58,7 @@
 void __m_free(void* ptr);
 void m_burn(void* data, unsigned int len);
 
+/* Used to force mp_ints to be initialised */
+#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
+
 #endif /* _DBUTIL_H_ */
--- a/debug.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/debug.h	Mon Jul 26 02:44:20 2004 +0000
@@ -36,7 +36,7 @@
 /* Define this to print trace statements - very verbose */
 /* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
  * since the printing does not sanitise strings etc */
-#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/kex.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/kex.h	Mon Jul 26 02:44:20 2004 +0000
@@ -26,17 +26,25 @@
 #define _KEX_H_
 
 #include "includes.h"
+#include "algo.h"
 
 void send_msg_kexinit();
 void recv_msg_kexinit();
-void send_dh_kex();
-void recv_msg_kexdh_init();
 void send_msg_newkeys();
 void recv_msg_newkeys();
 void kexinitialise();
+void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+		sign_key *hostkey);
 
-void svr_read_kex();
-void cli_read_kex();
+void read_kex_algos(
+		algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
+			int *goodguess));
+
+void recv_msg_kexdh_init(); // server
+
+void send_msg_kexdh_init(); // client
+void recv_msg_kexdh_reply(); // client
 
 extern const unsigned char dh_p_val[];
 #define DH_P_LEN 128 /* The length of the dh_p_val array */
--- a/main.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/main.c	Mon Jul 26 02:44:20 2004 +0000
@@ -240,8 +240,10 @@
 				if (m_close(childpipe[0]) == DROPBEAR_FAILURE) {
 					dropbear_exit("Couldn't close socket");
 				}
+
 				/* start the session */
-				svr_session(childsock, childpipe[1], &remoteaddr);
+				svr_session(childsock, childpipe[1], 
+								getaddrhostname(&remoteaddr));
 				/* don't return */
 				assert(0);
 			}
--- a/options.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/options.h	Mon Jul 26 02:44:20 2004 +0000
@@ -30,7 +30,7 @@
  * parts are to allow for commandline -DDROPBEAR_XXX options etc.
  ******************************************************************/
 #define DROPBEAR_SERVER
-/* #define DROPBEAR_CLIENT */
+//#define DROPBEAR_CLIENT
 
 #ifndef DROPBEAR_PORT
 #define DROPBEAR_PORT 22
@@ -48,6 +48,7 @@
  * perhaps 20% slower for pubkey operations (it is probably worth experimenting
  * if you want to use this) */
 /*#define NO_FAST_EXPTMOD*/
+#define DROPBEAR_SMALL_CODE
 
 /* Enable X11 Forwarding */
 #define ENABLE_X11FWD
@@ -181,7 +182,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "0.41"
+#define DROPBEAR_VERSION "0.41-and-client"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
--- a/process-packet.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/process-packet.c	Mon Jul 26 02:44:20 2004 +0000
@@ -104,9 +104,11 @@
 		 */
 		 
 		/* check that we aren't expecting a particular packet */
+#if 0
 		if (cli_ses.expecting && cli_ses.expecting == type) {
 			cli_ses.expecting = 0;
 		}
+#endif
 	}
 #endif
 
--- a/session.h	Tue Jul 20 12:06:37 2004 +0000
+++ b/session.h	Mon Jul 26 02:44:20 2004 +0000
@@ -26,6 +26,7 @@
 #define _SESSION_H_
 
 #include "includes.h"
+#include "options.h"
 #include "buffer.h"
 #include "signkey.h"
 #include "kex.h"
@@ -38,7 +39,8 @@
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
 
-void common_session_init(int sock);
+void common_session_init(int sock, char* remotehost);
+void session_loop(void(*loophandler)());
 void common_session_cleanup();
 void checktimeouts();
 void session_identification();
@@ -46,10 +48,14 @@
 extern void(*session_remoteclosed)();
 
 /* Server */
-void svr_session(int sock, int childpipe, struct sockaddr *remoteaddr);
+void svr_session(int sock, int childpipe, char *remotehost);
 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, char *remotehost);
+void cli_dropbear_exit(int exitcode, const char* format, va_list param);
+void cli_dropbear_log(int priority, const char* format, va_list param);
 
 struct key_context {
 
@@ -85,8 +91,8 @@
 
 	int sock;
 
-	struct sockaddr *remoteaddr;
 	unsigned char *remotehost; /* the peer hostname */
+
 	unsigned char *remoteident;
 
 	int maxfd; /* the maximum file descriptor to check with select() */
@@ -166,11 +172,20 @@
 
 };
 
+typedef enum {
+	NOTHING,
+	KEXINIT_RCVD,
+	KEXDH_INIT_SENT,
+	KEXDH_REPLY_RCVD,
 
+} cli_state;
 
 struct clientsession {
 
+	mp_int *dh_e, *dh_x; /* Used during KEX */
+	cli_state state; /* Used to progress the KEX/auth/channelsession etc */
 	int something; /* XXX */
+	unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
 
 };
 
@@ -182,7 +197,7 @@
 #endif /* DROPBEAR_SERVER */
 
 #ifdef DROPBEAR_CLIENT
-extern struct serversession cli_ses;
+extern struct clientsession cli_ses;
 #endif /* DROPBEAR_CLIENT */
 
 #endif /* _SESSION_H_ */
--- a/signkey.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/signkey.c	Mon Jul 26 02:44:20 2004 +0000
@@ -45,7 +45,8 @@
 }
 
 /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
- * type is set to hold the type returned */
+ * type should be set by the caller to specify the type to read, and
+ * on return is set to the type read (useful when type = _ANY) */
 int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
 
 	unsigned char* ident;
--- a/svr-algo.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/svr-algo.c	Mon Jul 26 02:44:20 2004 +0000
@@ -16,6 +16,8 @@
 	unsigned int count, i, j;
 	algo_type * ret = NULL;
 
+	*goodguess = 0;
+
 	/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
 	algolist = buf_getstring(buf, &len);
 	/* Debug this */
@@ -57,8 +59,6 @@
 					/* set if it was a good guess */
 					if (i == 0 && j == 0) {
 						*goodguess = 1;
-					} else {
-						*goodguess = 0;
 					}
 					/* set the algo to return */
 					ret = &localalgos[j];
--- a/svr-kex.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/svr-kex.c	Mon Jul 26 02:44:20 2004 +0000
@@ -70,87 +70,15 @@
  * See the ietf-secsh-transport draft, section 6, for details */
 static void send_msg_kexdh_reply(mp_int *dh_e) {
 
-	mp_int dh_p, dh_q, dh_g, dh_y, dh_f;
-	unsigned char randbuf[DH_P_LEN];
-	int dh_q_len;
-	hash_state hs;
+	mp_int dh_y, dh_f;
 
 	TRACE(("enter send_msg_kexdh_reply"));
 	
-	m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL);
-
-	/* read the prime and generator*/
-	if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
-			!= MP_OKAY) {
-		dropbear_exit("Diffie-Hellman error");
-	}
-	
-	if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
-		dropbear_exit("Diffie-Hellman error");
-	}
-
-	/* calculate q = (p-1)/2 */
-	if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/
-		dropbear_exit("Diffie-Hellman error");
-	}
-	if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) {
-		dropbear_exit("Diffie-Hellman error");
-	}
-
-	dh_q_len = mp_unsigned_bin_size(&dh_q);
-
-	/* calculate our random value dh_y */
-	do {
-		assert((unsigned int)dh_q_len <= sizeof(randbuf));
-		genrandom(randbuf, dh_q_len);
-		if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) {
-			dropbear_exit("Diffie-Hellman error");
-		}
-	} while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT);
-
-	/* f = g^y mod p */
-	if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) {
-		dropbear_exit("Diffie-Hellman error");
-	}
-	mp_clear(&dh_g);
+	gen_kexdh_vals(&dh_f, &dh_y);
 
-	/* K = e^y mod p */
-	ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
-	m_mp_init(ses.dh_K);
-	if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) {
-		dropbear_exit("Diffie-Hellman error");
-	}
-
-	/* clear no longer needed vars */
-	mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL);
+	kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey);
+	mp_clear(&dh_y);
 
-	/* Create the remainder of the hash buffer, to generate the exchange hash */
-	/* K_S, the host key */
-	buf_put_pub_key(ses.kexhashbuf, svr_opts.hostkey, 
-			ses.newkeys->algo_hostkey);
-	/* e, exchange value sent by the client */
-	buf_putmpint(ses.kexhashbuf, dh_e);
-	/* f, exchange value sent by the server */
-	buf_putmpint(ses.kexhashbuf, &dh_f);
-	/* K, the shared secret */
-	buf_putmpint(ses.kexhashbuf, ses.dh_K);
-
-	/* calculate the hash H to sign */
-	sha1_init(&hs);
-	buf_setpos(ses.kexhashbuf, 0);
-	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
-			ses.kexhashbuf->len);
-	sha1_done(&hs, ses.hash);
-	buf_free(ses.kexhashbuf);
-	ses.kexhashbuf = NULL;
-	
-	/* first time around, we set the session_id to H */
-	if (ses.session_id == NULL) {
-		/* create the session_id, this never needs freeing */
-		ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
-		memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
-	}
-	
 	/* we can start creating the kexdh_reply packet */
 	CHECKCLEARTOWRITE();
 	buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
@@ -171,105 +99,3 @@
 	TRACE(("leave send_msg_kexdh_reply"));
 }
 
-/* read the client's choice of algorithms */
-void svr_read_kex() {
-
-	algo_type * algo;
-	char * erralgo = NULL;
-
-	int goodguess = 0;
-	int allgood = 1; /* we AND this with each goodguess and see if its still
-						true after */
-
-	buf_incrpos(ses.payload, 16); /* start after the cookie */
-
-	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
-
-	/* kex_algorithms */
-	algo = svr_buf_match_algo(ses.payload, sshkex, &goodguess);
-	allgood &= goodguess;
-	if (algo == NULL) {
-		erralgo = "kex";
-		goto error;
-	}
-	ses.newkeys->algo_kex = algo->val;
-
-	/* server_host_key_algorithms */
-	algo = svr_buf_match_algo(ses.payload, sshhostkey, &goodguess);
-	allgood &= goodguess;
-	if (algo == NULL) {
-		erralgo = "hostkey";
-		goto error;
-	}
-	ses.newkeys->algo_hostkey = algo->val;
-
-	/* encryption_algorithms_client_to_server */
-	algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess);
-	if (algo == NULL) {
-		erralgo = "enc c->s";
-		goto error;
-	}
-	ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
-
-	/* encryption_algorithms_server_to_client */
-	algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess);
-	if (algo == NULL) {
-		erralgo = "enc s->c";
-		goto error;
-	}
-	ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
-
-	/* mac_algorithms_client_to_server */
-	algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess);
-	if (algo == NULL) {
-		erralgo = "mac c->s";
-		goto error;
-	}
-	ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
-
-	/* mac_algorithms_server_to_client */
-	algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess);
-	if (algo == NULL) {
-		erralgo = "mac s->c";
-		goto error;
-	}
-	ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
-
-	/* compression_algorithms_client_to_server */
-	algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess);
-	if (algo == NULL) {
-		erralgo = "comp c->s";
-		goto error;
-	}
-	ses.newkeys->recv_algo_comp = algo->val;
-
-	/* compression_algorithms_server_to_client */
-	algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess);
-	if (algo == NULL) {
-		erralgo = "comp s->c";
-		goto error;
-	}
-	ses.newkeys->trans_algo_comp = algo->val;
-
-	/* languages_client_to_server */
-	buf_eatstring(ses.payload);
-
-	/* languages_server_to_client */
-	buf_eatstring(ses.payload);
-
-	/* first_kex_packet_follows */
-	if (buf_getbyte(ses.payload)) {
-		ses.kexstate.firstfollows = 1;
-		/* if the guess wasn't good, we ignore the packet sent */
-		if (!allgood) {
-			ses.ignorenext = 1;
-		}
-	}
-
-	/* reserved for future extensions */
-	buf_getint(ses.payload);
-	return;
-
-error:
-	dropbear_exit("no matching algo %s", erralgo);
-}
--- a/svr-session.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/svr-session.c	Mon Jul 26 02:44:20 2004 +0000
@@ -50,7 +50,7 @@
 	{SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server
 	{SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server
 	{SSH_MSG_KEXINIT, recv_msg_kexinit},
-	{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init},
+	{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, // server
 	{SSH_MSG_NEWKEYS, recv_msg_newkeys},
 	{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
 	{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
@@ -70,17 +70,12 @@
 	NULL /* Null termination is mandatory. */
 };
 
-void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) {
+void svr_session(int sock, int childpipe, char* remotehost) {
 
-	fd_set readfd, writefd;
 	struct timeval timeout;
-	int val;
 	
 	crypto_init();
-	common_session_init(sock);
-
-	ses.remoteaddr = remoteaddr;
-	ses.remotehost = getaddrhostname(remoteaddr);
+	common_session_init(sock, remotehost);
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
@@ -111,75 +106,12 @@
 	/* start off with key exchange */
 	send_msg_kexinit();
 
-	FD_ZERO(&readfd);
-	FD_ZERO(&writefd);
-
-	/* main loop, select()s for all sockets in use */
-	for(;;) {
-
-		timeout.tv_sec = SELECT_TIMEOUT;
-		timeout.tv_usec = 0;
-		FD_ZERO(&writefd);
-		FD_ZERO(&readfd);
-		assert(ses.payload == NULL);
-		if (ses.sock != -1) {
-			FD_SET(ses.sock, &readfd);
-			if (!isempty(&ses.writequeue)) {
-				FD_SET(ses.sock, &writefd);
-			}
-		}
-
-		/* set up for channels which require reading/writing */
-		if (ses.dataallowed) {
-			setchannelfds(&readfd, &writefd);
-		}
-		val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
+	/* Run the main for loop. NULL is for the dispatcher - only the client
+	 * code makes use of it */
+	session_loop(NULL);
 
-		if (exitflag) {
-			dropbear_exit("Terminated by signal");
-		}
-		
-		if (val < 0) {
-			if (errno == EINTR) {
-				continue;
-			} else {
-				dropbear_exit("Error in select");
-			}
-		}
-
-		/* check for auth timeout, rekeying required etc */
-		checktimeouts();
-		
-		if (val == 0) {
-			/* timeout */
-			TRACE(("select timeout"));
-			continue;
-		}
+	/* Not reached */
 
-		/* process session socket's incoming/outgoing data */
-		if (ses.sock != -1) {
-			if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
-				write_packet();
-			}
-
-			if (FD_ISSET(ses.sock, &readfd)) {
-				read_packet();
-			}
-			
-			/* Process the decrypted packet. After this, the read buffer
-			 * will be ready for a new packet */
-			if (ses.payload != NULL) {
-				process_packet();
-			}
-		}
-
-		/* process pipes etc for the channels, ses.dataallowed == 0
-		 * during rekeying ) */
-		if (ses.dataallowed) {
-			channelio(&readfd, &writefd);
-		}
-
-	} /* for(;;) */
 }
 
 /* failure exit - format must be <= 100 chars */
--- a/tcpfwd-direct.c	Tue Jul 20 12:06:37 2004 +0000
+++ b/tcpfwd-direct.c	Mon Jul 26 02:44:20 2004 +0000
@@ -27,6 +27,7 @@
 	unsigned int destport;
 	unsigned char* orighost = NULL;
 	unsigned int origport;
+	char portstring[6];
 	int sock;
 	int len;
 	int ret = DROPBEAR_FAILURE;
@@ -58,7 +59,8 @@
 		goto out;
 	}
 
-	sock = newtcp(desthost, destport);
+	snprintf(portstring, sizeof(portstring), "%d", destport);
+	sock = connect_remote(desthost, portstring, 1, NULL);
 	if (sock < 0) {
 		TRACE(("leave newtcpdirect: sock failed"));
 		goto out;
@@ -86,6 +88,7 @@
  * returned will need to be checked for success when it is first written.
  * Similarities with OpenSSH's connect_to() are not coincidental.
  * Returns -1 on failure */
+#if 0
 static int newtcp(const char * host, int port) {
 
 	int sock = -1;
@@ -152,4 +155,5 @@
 	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
 	return sock;
 }
+#endif
 #endif /* DISABLE_TCPFWD_DIRECT */