diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 1999-10-17 16:56:10 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 1999-10-17 16:56:10 +0000 |
commit | 0238757d8ee02e7037dc604d487a1629085383ce (patch) | |
tree | 9b1500f45db4597e51335ccc02f356a83147a5bf /usr.bin | |
parent | 5cbb3086fbb15213b5d1fbc89fbd23641a8a95fa (diff) |
re-implement the proto-1.5 channel close protocol, see nchan.ms.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/channels.c | 45 | ||||
-rw-r--r-- | usr.bin/ssh/channels.h | 9 | ||||
-rw-r--r-- | usr.bin/ssh/nchan.c | 231 | ||||
-rw-r--r-- | usr.bin/ssh/nchan.h | 76 | ||||
-rw-r--r-- | usr.bin/ssh/nchan.ms | 46 |
5 files changed, 260 insertions, 147 deletions
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c index 81b30fce868..0d4b1d5eda5 100644 --- a/usr.bin/ssh/channels.c +++ b/usr.bin/ssh/channels.c @@ -16,7 +16,7 @@ arbitrary tcp/ip connections, and the authentication agent connection. */ #include "includes.h" -RCSID("$Id: channels.c,v 1.15 1999/10/16 21:19:00 deraadt Exp $"); +RCSID("$Id: channels.c,v 1.16 1999/10/17 16:56:08 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -136,10 +136,11 @@ int channel_allocate(int type, int sock, char *remote_name) buffer_init(&channels[i].output); channels[i].self = i; channels[i].type = type; + channels[i].x11 = 0; channels[i].sock = sock; - channels[i].flags = 0; channels[i].remote_id = -1; channels[i].remote_name = remote_name; + chan_init_iostates(&channels[i]); return i; } @@ -156,10 +157,11 @@ int channel_allocate(int type, int sock, char *remote_name) buffer_init(&channels[old_channels].output); channels[old_channels].self = old_channels; channels[old_channels].type = type; + channels[old_channels].x11 = 0; channels[old_channels].sock = sock; - channels[old_channels].flags = 0; channels[old_channels].remote_id = -1; channels[old_channels].remote_name = remote_name; + chan_init_iostates(&channels[old_channels]); return old_channels; } @@ -213,17 +215,14 @@ void channel_prepare_select(fd_set *readset, fd_set *writeset) break; } /* test whether sockets are 'alive' for read/write */ - if (!(ch->flags & CHAN_SHUT_RD)) + if (ch->istate == CHAN_INPUT_OPEN) if (buffer_len(&ch->input) < 32768) FD_SET(ch->sock, readset); - if (!(ch->flags & CHAN_SHUT_WR)){ + if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN){ if (buffer_len(&ch->output) > 0){ FD_SET(ch->sock, writeset); - }else if(ch->flags & CHAN_IEOF_RCVD){ - /* if output-buffer empty AND IEOF received, - we won't get more data for writing */ - chan_shutdown_write(ch); - chan_send_oclose(ch); + }else if(ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(ch); } } break; @@ -319,6 +318,8 @@ void channel_prepare_select(fd_set *readset, fd_set *writeset) /* Start normal processing for the channel. */ ch->type = SSH_CHANNEL_OPEN; + /* Enable X11 Problem FIX */ + ch->x11 = 1; goto redo; reject: @@ -335,10 +336,10 @@ void channel_prepare_select(fd_set *readset, fd_set *writeset) packet_put_int(ch->remote_id); packet_send(); }else{ - chan_shutdown_read(ch); /* shutdown, since close() does not update ch->flags */ - chan_send_ieof(ch); /* no need to wait for output-buffer */ - chan_shutdown_write(ch); - chan_send_oclose(ch); + debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate); + chan_read_failed(ch); + chan_write_failed(ch); + debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate); } break; @@ -461,10 +462,7 @@ void channel_after_select(fd_set *readset, fd_set *writeset) ch->type = SSH_CHANNEL_INPUT_DRAINING; debug("Channel %d status set to input draining.", i); }else{ - buffer_consume(&ch->output, buffer_len(&ch->output)); - chan_shutdown_read(ch); - /* we have to wait until the input-buffer has been - sent to our peer before we can send IEOF */ + chan_read_failed(ch); } break; } @@ -482,15 +480,12 @@ void channel_after_select(fd_set *readset, fd_set *writeset) debug("Channel %d status set to input draining.", i); ch->type = SSH_CHANNEL_INPUT_DRAINING; }else{ - buffer_consume(&ch->output, buffer_len(&ch->output)); - chan_shutdown_write(ch); - chan_send_oclose(ch); + chan_write_failed(ch); } break; } buffer_consume(&ch->output, len); } - chan_del_if_dead(ch); break; case SSH_CHANNEL_OUTPUT_DRAINING: @@ -553,13 +548,13 @@ void channel_output_poll() packet_send(); buffer_consume(&ch->input, len); } - else if(ch->flags & CHAN_SHUT_RD) + else if(ch->istate == CHAN_INPUT_WAIT_DRAIN) { if (compat13) - fatal("cannot happen: CHAN_SHUT_RD set for proto 1.3"); + fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* input-buffer is empty and read-socket shutdown: tell peer, that we will not send more data: send IEOF */ - chan_send_ieof(ch); + chan_ibuf_empty(ch); } } } diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h index a046889d5d3..9cdf10f3111 100644 --- a/usr.bin/ssh/channels.h +++ b/usr.bin/ssh/channels.h @@ -1,4 +1,4 @@ -/* RCSID("$Id: channels.h,v 1.2 1999/10/16 22:29:00 markus Exp $"); */ +/* RCSID("$Id: channels.h,v 1.3 1999/10/17 16:56:09 markus Exp $"); */ #ifndef CHANNELS_H #define CHANNELS_H @@ -17,17 +17,18 @@ #define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to conn */ #define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to app */ - /* Data structure for channel data. This is iniailized in channel_allocate and cleared in channel_free. */ -typedef struct +typedef struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ int remote_id; /* channel identifier for remote peer */ /* peer can be reached over encrypted connection, via packet-sent */ - int flags; /* flags for close in proto 1.5 */ + int istate; + int ostate; + int x11; int sock; /* data socket, linked to this channel */ Buffer input; /* data read from socket, to be sent over encrypted connection */ Buffer output; /* data received over encrypted connection for send on socket */ diff --git a/usr.bin/ssh/nchan.c b/usr.bin/ssh/nchan.c index 1916894ab06..175cd666f9b 100644 --- a/usr.bin/ssh/nchan.c +++ b/usr.bin/ssh/nchan.c @@ -1,101 +1,200 @@ #include "includes.h" -RCSID("$Id: nchan.c,v 1.2 1999/10/16 22:29:01 markus Exp $"); +RCSID("$Id: nchan.c,v 1.3 1999/10/17 16:56:09 markus Exp $"); #include "ssh.h" #include "buffer.h" -#include "channels.h" #include "packet.h" +#include "channels.h" #include "nchan.h" +static void chan_send_ieof(Channel *c); +static void chan_send_oclose(Channel *c); +static void chan_shutdown_write(Channel *c); +static void chan_shutdown_read(Channel *c); +static void chan_delele_if_full_closed(Channel *c); + +/* + * EVENTS: update channel input/ouput states + * execute ACTIONS + */ +/* events concerning the INPUT from socket for channel (istate) */ void -dump_chan(Channel *c){ - debug("chan %d type %d flags 0x%x", c->self, c->type, c->flags); +chan_rcvd_oclose(Channel *c){ + switch(c->istate){ + case CHAN_INPUT_WAIT_OCLOSE: + debug("channel %d: INPUT_WAIT_CLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); + c->istate=CHAN_INPUT_CLOSED; + chan_delele_if_full_closed(c); + break; + case CHAN_INPUT_OPEN: + debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); + chan_shutdown_read(c); + chan_send_ieof(c); + c->istate=CHAN_INPUT_CLOSED; + chan_delele_if_full_closed(c); + break; + default: + debug("protocol error: chan_rcvd_oclose %d for istate %d",c->self,c->istate); + break; + } } void -chan_rcvd_ieof(Channel *c){ - dump_chan(c); - if(c->flags & CHAN_IEOF_RCVD){ - debug("chan_rcvd_ieof twice: %d",c->self); - return; +chan_read_failed(Channel *c){ + switch(c->istate){ + case CHAN_INPUT_OPEN: + debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); + chan_shutdown_read(c); + c->istate=CHAN_INPUT_WAIT_DRAIN; + break; + default: + debug("internal error: we do not read, but chan_read_failed %d for istate %d", + c->self,c->istate); + break; } - debug("rcvd_CHAN_IEOF %d",c->self); - c->flags |= CHAN_IEOF_RCVD; - /* cannot clear input buffer. remaining data has to be sent to client */ - chan_del_if_dead(c); } void -chan_rcvd_oclose(Channel *c){ - dump_chan(c); - if(c->flags & CHAN_OCLOSE_RCVD){ - debug("chan_rcvd_oclose twice: %d",c->self); +chan_ibuf_empty(Channel *c){ + if(buffer_len(&c->input)){ + debug("internal error: chan_ibuf_empty %d for non empty buffer",c->self); return; } - debug("rcvd_CHAN_OCLOSE %d",c->self); - c->flags |= CHAN_OCLOSE_RCVD; - /* our peer can no longer consume, so there is not need to read */ - chan_shutdown_read(c); - buffer_consume(&c->output, buffer_len(&c->output)); - /* Note: for type==OPEN IEOF is sent by channel_output_poll() */ - chan_del_if_dead(c); + switch(c->istate){ + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send OCLOSE]", c->self); + chan_send_ieof(c); + c->istate=CHAN_INPUT_WAIT_OCLOSE; + break; + default: + debug("internal error: chan_ibuf_empty %d for istate %d",c->self,c->istate); + break; + } } +/* events concerning the OUTPUT from channel for socket (ostate) */ void -chan_send_ieof(Channel *c){ - if(c->flags & CHAN_IEOF_SENT){ - /* this is ok: it takes some time before we get OCLOSE */ - /* debug("send_chan_ieof twice %d", c->self); */ +chan_rcvd_ieof(Channel *c){ + + /* X11: if we receive IEOF for X11, then we have to FORCE sending of IEOF, + * this is from ssh-1.2.27 debugging output. + */ + if(c->x11){ + debug("channel %d: OUTPUT_OPEN -> OUTPUT_CLOSED/INPUT_WAIT_OCLOSED [X11 FIX]", c->self); + chan_send_ieof(c); + c->istate=CHAN_INPUT_WAIT_OCLOSE; + chan_send_oclose(c); + c->ostate=CHAN_OUTPUT_CLOSED; + chan_delele_if_full_closed(c); return; } - debug("send_CHAN_IEOF %d", c->self); - packet_start(CHAN_IEOF); - packet_put_int(c->remote_id); - packet_send(); - c->flags |= CHAN_IEOF_SENT; - dump_chan(c); + switch(c->ostate){ + case CHAN_OUTPUT_OPEN: + debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); + c->ostate=CHAN_OUTPUT_WAIT_DRAIN; + break; + case CHAN_OUTPUT_WAIT_IEOF: + debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); + c->ostate=CHAN_OUTPUT_CLOSED; + chan_delele_if_full_closed(c); + break; + default: + debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self,c->ostate); + break; + } } void -chan_send_oclose(Channel *c){ - if(c->flags & CHAN_OCLOSE_SENT){ - debug("send_chan_oclose twice %d", c->self); - return; +chan_write_failed(Channel *c){ + switch(c->ostate){ + case CHAN_OUTPUT_OPEN: + debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); + chan_send_oclose(c); + c->ostate=CHAN_OUTPUT_WAIT_IEOF; + break; + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); + chan_send_oclose(c); + c->ostate=CHAN_OUTPUT_CLOSED; + chan_delele_if_full_closed(c); + break; + default: + debug("internal error: chan_write_failed %d for ostate %d",c->self,c->ostate); + break; } - debug("send_CHAN_OCLOSE %d", c->self); - packet_start(CHAN_OCLOSE); - packet_put_int(c->remote_id); - packet_send(); - c->flags |= CHAN_OCLOSE_SENT; - dump_chan(c); } void -chan_shutdown_write(Channel *c){ - if(c->flags & CHAN_SHUT_WR){ - debug("chan_shutdown_write twice %d",c->self); +chan_obuf_empty(Channel *c){ + if(buffer_len(&c->output)){ + debug("internal error: chan_obuf_empty %d for non empty buffer",c->self); return; } - debug("chan_shutdown_write %d", c->self); + switch(c->ostate){ + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); + chan_send_oclose(c); + c->ostate=CHAN_OUTPUT_CLOSED; + chan_delele_if_full_closed(c); + break; + default: + debug("internal error: chan_obuf_empty %d for ostate %d",c->self,c->ostate); + break; + } +} +/* + * ACTIONS: should never update c->istate or c->ostate + */ +static void +chan_send_ieof(Channel *c){ + switch(c->istate){ + case CHAN_INPUT_OPEN: + case CHAN_INPUT_WAIT_DRAIN: + packet_start(SSH_MSG_CHANNEL_INPUT_EOF); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate); + break; + } +} +static void +chan_send_oclose(Channel *c){ + switch(c->ostate){ + case CHAN_OUTPUT_OPEN: + case CHAN_OUTPUT_WAIT_DRAIN: + chan_shutdown_write(c); + buffer_consume(&c->output, buffer_len(&c->output)); + packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate); + break; + } +} +/* helper */ +static void +chan_shutdown_write(Channel *c){ + debug("channel %d: shutdown_write", c->self); if(shutdown(c->sock, SHUT_WR)<0) - error("chan_shutdown_write failed %.100s", strerror(errno)); - c->flags |= CHAN_SHUT_WR; - /* clear output buffer, since there is noone going to read the data - we just closed the output-socket */ - /* buffer_consume(&c->output, buffer_len(&c->output)); */ + error("chan_shutdown_write failed for %d/%d %.100s", + c->self, c->sock, strerror(errno)); } -void +static void chan_shutdown_read(Channel *c){ - if(c->flags & CHAN_SHUT_RD){ - /* chan_shutdown_read is called for read-errors and OCLOSE */ - /* debug("chan_shutdown_read twice %d",c->self); */ - return; - } - debug("chan_shutdown_read %d", c->self); + debug("channel %d: shutdown_read", c->self); if(shutdown(c->sock, SHUT_RD)<0) - error("chan_shutdown_read failed %.100s", strerror(errno)); - c->flags |= CHAN_SHUT_RD; + error("chan_shutdown_read failed for %d/%d %.100s", + c->self, c->sock, strerror(errno)); } -void -chan_del_if_dead(Channel *c){ - if(c->flags == CHAN_CLOSED){ - debug("channel %d closing",c->self); +static void +chan_delele_if_full_closed(Channel *c){ + if(c->istate==CHAN_INPUT_CLOSED && c->ostate==CHAN_OUTPUT_CLOSED){ + debug("channel %d: closing", c->self); channel_free(c->self); } } +void +chan_init_iostates(Channel *c){ + c->ostate=CHAN_OUTPUT_OPEN; + c->istate=CHAN_INPUT_OPEN; +} diff --git a/usr.bin/ssh/nchan.h b/usr.bin/ssh/nchan.h index f312b0ec64b..5fb3bc04e90 100644 --- a/usr.bin/ssh/nchan.h +++ b/usr.bin/ssh/nchan.h @@ -1,4 +1,4 @@ -/* RCSID("$Id: nchan.h,v 1.2 1999/10/16 22:29:01 markus Exp $"); */ +/* RCSID("$Id: nchan.h,v 1.3 1999/10/17 16:56:09 markus Exp $"); */ #ifndef NCHAN_H #define NCHAN_H @@ -25,61 +25,33 @@ * See the debugging output from 'ssh -v' and 'sshd -d' of * ssh-1.2.27 as an example. * - * Details: (for Channel data structure see channels.h) - * - * - the output_buffer gets data received from the remote peer and - * is written to the socket, - * - the input_buffer gets data from the socket and is sent to remote peer. - * - the socket represents the local object communicating with an object - * reachable via the peer - * - * PEER A PEER B - * - * read(sock, input_buffer) < 0; - * shutdown_read(); - * flush(input_buffer) =: DATA - * send(DATA) -> rcvd(DATA) - * write(sock, output_buffer:=DATA); - * send(IEOF) -> rcvd(IEOF) - * shutdown_write() if: - * a) write fails - * b) rcvd_IEOF==true && - * output_buffer==empty - * rcvd(OCLOSE) <- send(OCLOSE) - * - * The channel is now half closed. No data will flow from A to B. - * - * Note that each side can remove the channel only if 2 messages - * have been sent and received and the associated socket has been - * shutdown, see below: */ -enum { - /* ssh-proto-1.5 overloads message-types */ - CHAN_IEOF = SSH_MSG_CHANNEL_CLOSE, - /* there will be no more data from sender */ - CHAN_OCLOSE = SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, - /* all received data has been written to the socket */ +/* ssh-proto-1.5 overloads prot-1.3-message-types */ +#define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE +#define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION - /* channel close flags */ - CHAN_IEOF_SENT = 0x01, - CHAN_IEOF_RCVD = 0x02, - CHAN_OCLOSE_SENT = 0x04, - CHAN_OCLOSE_RCVD = 0x08, - CHAN_SHUT_RD = 0x10, - CHAN_SHUT_WR = 0x20, +/* possible input states */ +#define CHAN_INPUT_OPEN 0x01 +#define CHAN_INPUT_WAIT_DRAIN 0x02 +#define CHAN_INPUT_WAIT_OCLOSE 0x04 +#define CHAN_INPUT_CLOSED 0x08 - /* a channel can be removed if ALL the following flags are set: */ - CHAN_CLOSED = CHAN_IEOF_SENT | CHAN_IEOF_RCVD | - CHAN_OCLOSE_SENT | CHAN_OCLOSE_RCVD | - CHAN_SHUT_RD | CHAN_SHUT_WR -}; +/* possible output states */ +#define CHAN_OUTPUT_OPEN 0x10 +#define CHAN_OUTPUT_WAIT_DRAIN 0x20 +#define CHAN_OUTPUT_WAIT_IEOF 0x40 +#define CHAN_OUTPUT_CLOSED 0x80 -void chan_del_if_dead(Channel *c); -void chan_rcvd_ieof(Channel *c); +/* EVENTS for the input state */ void chan_rcvd_oclose(Channel *c); -void chan_send_ieof(Channel *c); -void chan_send_oclose(Channel *c); -void chan_shutdown_read(Channel *c); -void chan_shutdown_write(Channel *c); +void chan_read_failed(Channel *c); +void chan_ibuf_empty(Channel *c); + +/* EVENTS for the output state */ +void chan_rcvd_ieof(Channel *c); +void chan_write_failed(Channel *c); +void chan_obuf_empty(Channel *c); + +void chan_init_iostates(Channel *c); #endif diff --git a/usr.bin/ssh/nchan.ms b/usr.bin/ssh/nchan.ms new file mode 100644 index 00000000000..642e07eefe6 --- /dev/null +++ b/usr.bin/ssh/nchan.ms @@ -0,0 +1,46 @@ +.TL +OpenSSH Channel Close Protocol 1.5 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S4: ellipse "INPUT" "CLOSED" +move down l from last ellipse.s +S3: ellipse "INPUT" "WAIT" "OCLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow "" "rcvd OCLOSE/" "shutdown_read" "send IEOF" from S1.e to S4.w +arrow "ibuf_empty/" "send IEOF" from S2.e to S3.w +arrow from S1.s to S2.n +box invis "read_failed/" "shutdown_read" with .e at last arrow.c +arrow from S3.n to S4.s +box invis "rcvd OCLOSE/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse "OUTPUT" "WAIT" "IEOF" +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow "" "write_failed/" "shutdown_write" "send OCLOSE" from S1.e to S3.w +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "send OCLOSE" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd IEOF/" "-" with .e at last arrow.c +arrow from S3.s to S4.n +box invis "rcvd IEOF/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE |