From a0e931005b6f9de407ba2ac5b5df9e34d4a7fb7e Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Sun, 31 Mar 2013 00:40:00 +0800
Subject: [PATCH] send out our kexinit packet before blocking to read the SSH
 version string

---
 cli-session.c    |  2 +-
 common-session.c | 29 +++++++++++++++++++----------
 session.h        |  7 +++++--
 svr-session.c    |  2 +-
 4 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/cli-session.c b/cli-session.c
index e58fdbde..f862bc85 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -99,7 +99,7 @@ void cli_session(int sock_in, int sock_out) {
 	sessinitdone = 1;
 
 	/* Exchange identification */
-	session_identification();
+	send_session_identification();
 
 	send_msg_kexinit();
 
diff --git a/common-session.c b/common-session.c
index f4fa579c..ec5c9ed5 100644
--- a/common-session.c
+++ b/common-session.c
@@ -39,6 +39,7 @@
 static void checktimeouts();
 static long select_timeout();
 static int ident_readln(int fd, char* buf, int count);
+static void read_session_identification();
 
 struct sshsession ses; /* GLOBAL */
 
@@ -141,7 +142,10 @@ void session_loop(void(*loophandler)()) {
 		FD_ZERO(&writefd);
 		FD_ZERO(&readfd);
 		dropbear_assert(ses.payload == NULL);
-		if (ses.sock_in != -1) {
+
+		/* during initial setup we flush out the KEXINIT packet before
+		 * attempting to read the remote version string, which might block */
+		if (ses.sock_in != -1 && (ses.remoteident || isempty(&ses.writequeue))) {
 			FD_SET(ses.sock_in, &readfd);
 		}
 		if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
@@ -195,7 +199,12 @@ void session_loop(void(*loophandler)()) {
 
 		if (ses.sock_in != -1) {
 			if (FD_ISSET(ses.sock_in, &readfd)) {
-				read_packet();
+				if (!ses.remoteident) {
+					/* blocking read of the version string */
+					read_session_identification();
+				} else {
+					read_packet();
+				}
 			}
 			
 			/* Process the decrypted packet. After this, the read buffer
@@ -245,20 +254,20 @@ void common_session_cleanup() {
 }
 
 
-void session_identification() {
-
-	/* max length of 255 chars */
-	char linebuf[256];
-	int len = 0;
-	char done = 0;
-	int i;
-
+void send_session_identification() {
 	/* write our version string, this blocks */
 	if (atomicio(write, ses.sock_out, LOCAL_IDENT "\r\n",
 				strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
 		ses.remoteclosed();
 	}
+}
 
+static void read_session_identification() {
+	/* max length of 255 chars */
+	char linebuf[256];
+	int len = 0;
+	char done = 0;
+	int i;
 	/* If they send more than 50 lines, something is wrong */
 	for (i = 0; i < 50; i++) {
 		len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
diff --git a/session.h b/session.h
index 0719e34f..d524226f 100644
--- a/session.h
+++ b/session.h
@@ -45,7 +45,7 @@ extern int exitflag;
 void common_session_init(int sock_in, int sock_out);
 void session_loop(void(*loophandler)());
 void common_session_cleanup();
-void session_identification();
+void send_session_identification();
 void send_msg_ignore();
 
 const char* get_user_shell();
@@ -111,7 +111,10 @@ struct sshsession {
 	int sock_in;
 	int sock_out;
 
-	unsigned char *remoteident;
+	/* remotehost will be initially NULL as we delay
+	 * reading the remote version string. it will be set
+	 * by the time any recv_() packet methods are called */
+	unsigned char *remoteident; 
 
 	int maxfd; /* the maximum file descriptor to check with select() */
 
diff --git a/svr-session.c b/svr-session.c
index cf822897..7234f4ab 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -114,7 +114,7 @@ void svr_session(int sock, int childpipe) {
 	sessinitdone = 1;
 
 	/* exchange identification, version etc */
-	session_identification();
+	send_session_identification();
 
 	/* start off with key exchange */
 	send_msg_kexinit();
-- 
GitLab