- djm@cvs.openbsd.org 2010/01/26 01:28:35
     [channels.c channels.h clientloop.c clientloop.h mux.c nchan.c ssh.c]
     rewrite ssh(1) multiplexing code to a more sensible protocol.

     The new multiplexing code uses channels for the listener and
     accepted control sockets to make the mux master non-blocking, so
     no stalls when processing messages from a slave.

     avoid use of fatal() in mux master protocol parsing so an errant slave
     process cannot take down a running master.

     implement requesting of port-forwards over multiplexed sessions. Any
     port forwards requested by the slave are added to those the master has
     established.

     add support for stdio forwarding ("ssh -W host:port ...") in mux slaves.

     document master/slave mux protocol so that other tools can use it to
     control a running ssh(1). Note: there are no guarantees that this
     protocol won't be incompatibly changed (though it is versioned).

     feedback Salvador Fandino, dtucker@
     channel changes ok markus@
diff --git a/nchan.c b/nchan.c
index 160445e..20f6a2f 100644
--- a/nchan.c
+++ b/nchan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nchan.c,v 1.62 2008/11/07 18:50:18 stevesk Exp $ */
+/* $OpenBSD: nchan.c,v 1.63 2010/01/26 01:28:35 djm Exp $ */
 /*
  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
  *
@@ -161,7 +161,7 @@
 	switch (c->istate) {
 	case CHAN_INPUT_WAIT_DRAIN:
 		if (compat20) {
-			if (!(c->flags & CHAN_CLOSE_SENT))
+			if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL)))
 				chan_send_eof2(c);
 			chan_set_istate(c, CHAN_INPUT_CLOSED);
 		} else {
@@ -278,9 +278,12 @@
 chan_rcvd_close2(Channel *c)
 {
 	debug2("channel %d: rcvd close", c->self);
-	if (c->flags & CHAN_CLOSE_RCVD)
-		error("channel %d: protocol error: close rcvd twice", c->self);
-	c->flags |= CHAN_CLOSE_RCVD;
+	if (!(c->flags & CHAN_LOCAL)) {
+		if (c->flags & CHAN_CLOSE_RCVD)
+			error("channel %d: protocol error: close rcvd twice",
+			    c->self);
+		c->flags |= CHAN_CLOSE_RCVD;
+	}
 	if (c->type == SSH_CHANNEL_LARVAL) {
 		/* tear down larval channels immediately */
 		chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
@@ -302,11 +305,13 @@
 		chan_set_istate(c, CHAN_INPUT_CLOSED);
 		break;
 	case CHAN_INPUT_WAIT_DRAIN:
-		chan_send_eof2(c);
+		if (!(c->flags & CHAN_LOCAL))
+			chan_send_eof2(c);
 		chan_set_istate(c, CHAN_INPUT_CLOSED);
 		break;
 	}
 }
+
 void
 chan_rcvd_eow(Channel *c)
 {
@@ -454,6 +459,10 @@
 		    c->self, c->efd, buffer_len(&c->extended));
 		return 0;
 	}
+	if (c->flags & CHAN_LOCAL) {
+		debug2("channel %d: is dead (local)", c->self);
+		return 1;
+	}		
 	if (!(c->flags & CHAN_CLOSE_SENT)) {
 		if (do_send) {
 			chan_send_close2(c);