diff --git a/cli-main.c b/cli-main.c
index 68cf0230f9f832ceddf79542a10d856558edc1d0..1f1cf916a65138fda642037e5502e84b34ed94bf 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -39,7 +39,7 @@ int cli_main(int argc, char ** argv) {
 int main(int argc, char ** argv) {
 #endif
 
-	int sock;
+	int sock_in, sock_out;
 	char* error = NULL;
 	char* hostandport;
 	int len;
@@ -58,10 +58,18 @@ int main(int argc, char ** argv) {
 		dropbear_exit("signal() error");
 	}
 
-	sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
-			0, &error);
+#ifdef CLI_ENABLE_PROXYCMD
+	if (cli_runopts.proxycmd) {
 
-	if (sock < 0) {
+	} else
+#endif
+	{
+		int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
+				0, &error);
+		sock_in = sock_out = sock;
+	}
+
+	if (sock_in < 0) {
 		dropbear_exit("%s", error);
 	}
 
@@ -72,7 +80,7 @@ int main(int argc, char ** argv) {
 	snprintf(hostandport, len, "%s:%s", 
 			cli_opts.remotehost, cli_opts.remoteport);
 
-	cli_session(sock, hostandport);
+	cli_session(sock_in, sock_out, hostandport);
 
 	/* not reached */
 	return -1;
diff --git a/cli-runopts.c b/cli-runopts.c
index 42c5a9f22428e838e87489ff72c5e6f3f55b7b9a..a1be06a633204e3ef140141ad65569984a82f3d4 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -65,6 +65,9 @@ static void printhelp() {
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d)\n"
+#ifdef ENABLE_CLI_PROXYCMD
+					"-J <proxy_program> Use program rather than tcp connection"
+#endif
 #ifdef DEBUG_TRACE
 					"-v    verbose\n"
 #endif
@@ -86,6 +89,9 @@ void cli_getopts(int argc, char ** argv) {
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	int nextisremote = 0;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+	int nextisproxycmd = 0;
 #endif
 	char* dummy = NULL; /* Not used for anything real */
 
@@ -198,6 +204,11 @@ void cli_getopts(int argc, char ** argv) {
 				case 'R':
 					nextisremote = 1;
 					break;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+				case 'J':
+					next = &cli_opts.proxycmd;
+					break;
 #endif
 				case 'l':
 					next = &cli_opts.username;
diff --git a/cli-session.c b/cli-session.c
index 360187f41a26e2c0768dc1d10527f1018c9abdb6..3d9ce98901fa7ca786dadfb2c4a9b4a0afcd0e97 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -74,13 +74,13 @@ static const struct ChanType *cli_chantypes[] = {
 	NULL /* Null termination */
 };
 
-void cli_session(int sock, char* remotehost) {
+void cli_session(int sock_in, int sock_out, char* remotehost) {
 
 	seedrandom();
 
 	crypto_init();
 
-	common_session_init(sock, remotehost);
+	common_session_init(sock_in, sock_out, remotehost);
 
 	chaninitialise(cli_chantypes);
 
@@ -294,8 +294,10 @@ 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;
+	m_close(ses.sock_in);
+	m_close(ses.sock_out);
+	ses.sock_in = -1;
+	ses.sock_out = -1;
 	dropbear_exit("remote closed the connection");
 }
 
diff --git a/common-session.c b/common-session.c
index 79313f247d22487f19ace2282eea0f7543653dcb..30c0a58f48e78a69c35e693996d13fcefe46f4d8 100644
--- a/common-session.c
+++ b/common-session.c
@@ -52,14 +52,15 @@ int exitflag = 0; /* GLOBAL */
 
 
 /* called only at the start of a session, set up initial state */
-void common_session_init(int sock, char* remotehost) {
+void common_session_init(int sock_in, int sock_out, char* remotehost) {
 
 	TRACE(("enter session_init"))
 
 	ses.remotehost = remotehost;
 
-	ses.sock = sock;
-	ses.maxfd = sock;
+	ses.sock_in = sock_in;
+	ses.sock_out = sock_out;
+	ses.maxfd = MAX(sock_in, sock_out);
 
 	ses.connect_time = 0;
 	ses.last_packet_time = 0;
@@ -137,11 +138,11 @@ void session_loop(void(*loophandler)()) {
 		FD_ZERO(&writefd);
 		FD_ZERO(&readfd);
 		dropbear_assert(ses.payload == NULL);
-		if (ses.sock != -1) {
-			FD_SET(ses.sock, &readfd);
-			if (!isempty(&ses.writequeue)) {
-				FD_SET(ses.sock, &writefd);
-			}
+		if (ses.sock_in != -1) {
+			FD_SET(ses.sock_in, &readfd);
+		}
+		if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
+			FD_SET(ses.sock_out, &writefd);
 		}
 		
 		/* We get woken up when signal handlers write to this pipe.
@@ -183,12 +184,14 @@ void session_loop(void(*loophandler)()) {
 		checktimeouts();
 
 		/* process session socket's incoming/outgoing data */
-		if (ses.sock != -1) {
-			if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+		if (ses.sock_out != -1) {
+			if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) {
 				write_packet();
 			}
+		}
 
-			if (FD_ISSET(ses.sock, &readfd)) {
+		if (ses.sock_in != -1) {
+			if (FD_ISSET(ses.sock_in, &readfd)) {
 				read_packet();
 			}
 			
@@ -248,14 +251,14 @@ void session_identification() {
 	int i;
 
 	/* write our version string, this blocks */
-	if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
+	if (atomicio(write, ses.sock_out, LOCAL_IDENT "\r\n",
 				strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
 		ses.remoteclosed();
 	}
 
     /* If they send more than 50 lines, something is wrong */
 	for (i = 0; i < 50; i++) {
-		len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
+		len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
 
 		if (len < 0 && errno != EINTR) {
 			/* It failed */
diff --git a/options.h b/options.h
index 5385d3039cbd9a6461c98fe192c497330413d17e..6a90163eb2d62ddc0d181b2e51aed7cfecbda157 100644
--- a/options.h
+++ b/options.h
@@ -60,6 +60,10 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
 #define ENABLE_CLI_LOCALTCPFWD
 #define ENABLE_CLI_REMOTETCPFWD
 
+/* Allow using -J <proxycommand> to run the connection through a 
+   pipe to a program, rather the normal TCP connection */
+/*#define ENABLE_CLI_PROXYCMD*/
+
 #define ENABLE_SVR_LOCALTCPFWD
 #define ENABLE_SVR_REMOTETCPFWD
 
diff --git a/packet.c b/packet.c
index c559dab8560e8c7a7b64170536890781bd0a44f1..30f4758689bb1bc6f2b20fea01a0a15b2a8179fe 100644
--- a/packet.c
+++ b/packet.c
@@ -61,7 +61,7 @@ void write_packet() {
 	len = writebuf->len - writebuf->pos;
 	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
-	written = write(ses.sock, buf_getptr(writebuf, len), len);
+	written = write(ses.sock_out, buf_getptr(writebuf, len), len);
 
 	if (written < 0) {
 		if (errno == EINTR) {
@@ -122,7 +122,7 @@ void read_packet() {
 	 * mightn't be any available (EAGAIN) */
 	dropbear_assert(ses.readbuf != NULL);
 	maxlen = ses.readbuf->len - ses.readbuf->pos;
-	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
+	len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
 
 	if (len == 0) {
 		ses.remoteclosed();
@@ -171,7 +171,7 @@ static void read_packet_init() {
 	maxlen = blocksize - ses.readbuf->pos;
 			
 	/* read the rest of the packet if possible */
-	len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
+	len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
 			maxlen);
 	if (len == 0) {
 		ses.remoteclosed();
diff --git a/runopts.h b/runopts.h
index d6e8917097b420349578c6e520a8347cc55e5ded..6b34f923008fc90bfec90f7c5a0e1a3fd3a5925f 100644
--- a/runopts.h
+++ b/runopts.h
@@ -117,6 +117,9 @@ typedef struct cli_runopts {
 #ifdef ENABLE_CLI_LOCALTCPFWD
 	struct TCPFwdList * localfwds;
 #endif
+#ifdef ENABLE_CLI_PROXYCMD
+	char *proxycmd;
+#endif
 
 } cli_runopts;
 
diff --git a/session.h b/session.h
index e11c959980cfcba85a72aef1eff756ad22fc4b2d..c6d6ef89879139715e018681168b0c4d2ca8b6b6 100644
--- a/session.h
+++ b/session.h
@@ -41,7 +41,7 @@
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
 
-void common_session_init(int sock, char* remotehost);
+void common_session_init(int sock_in, int sock_out, char* remotehost);
 void session_loop(void(*loophandler)());
 void common_session_cleanup();
 void session_identification();
@@ -54,7 +54,7 @@ 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_session(int sock_in, int sock_out, char *remotehost);
 void cli_session_cleanup();
 void cleantext(unsigned char* dirtytext);
 
@@ -97,7 +97,8 @@ struct sshsession {
 							(cleared after auth once we're not
 							respecting AUTH_TIMEOUT any more) */
 
-	int sock;
+	int sock_in;
+	int sock_out;
 
 	unsigned char *remotehost; /* the peer hostname */
 
diff --git a/svr-session.c b/svr-session.c
index 5a8364a295ca2ce4f1c6df7bb0a8a25df2045fcf..658bc6eb0cfa8fc9a5f961f086a2d0563e3b4ba3 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -80,7 +80,7 @@ void svr_session(int sock, int childpipe,
     reseedrandom();
 
 	crypto_init();
-	common_session_init(sock, remotehost);
+	common_session_init(sock, sock, remotehost);
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
@@ -183,7 +183,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
 						localtime(&timesec)) == 0)
 		{
 			/* upon failure, just print the epoch-seconds time. */
-			snprintf(datestr, sizeof(datestr), "%d", timesec);
+			snprintf(datestr, sizeof(datestr), "%d", (int)timesec);
 		}
 		fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf);
 	}
@@ -192,8 +192,10 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
 /* called when the remote side closes the connection */
 static void svr_remoteclosed() {
 
-	close(ses.sock);
-	ses.sock = -1;
+	m_close(ses.sock_in);
+	m_close(ses.sock_out);
+	ses.sock_in = -1;
+	ses.sock_out = -1;
 	dropbear_close("Exited normally");
 
 }