# HG changeset patch # User Matt Johnston # Date 1093371138 0 # Node ID d3eb1fa8484edcb9ea706393ea6b8913f022ac34 # Parent e13f8a712a1c1ea383fecf943242a296ad997156 Nasty. diff -r e13f8a712a1c -r d3eb1fa8484e Makefile.in --- a/Makefile.in Tue Aug 24 07:22:36 2004 +0000 +++ b/Makefile.in Tue Aug 24 18:12:18 2004 +0000 @@ -28,12 +28,12 @@ CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ - cli-authpubkey.o cli-tcpfwd.o + cli-authpubkey.o cli-tcpfwd.o cli-channel.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ tcp-accept.o listener.o process-packet.o \ - common-runopts.o + common-runopts.o circbuffer.o KEYOBJS=dropbearkey.o gendss.o genrsa.o diff -r e13f8a712a1c -r d3eb1fa8484e TODO --- a/TODO Tue Aug 24 07:22:36 2004 +0000 +++ b/TODO Tue Aug 24 18:12:18 2004 +0000 @@ -2,6 +2,8 @@ Things which might need doing: +- errfd needs fixing + - Make options.h generated from configure perhaps? - Improved queueing of unauthed connections diff -r e13f8a712a1c -r d3eb1fa8484e channel.h --- a/channel.h Tue Aug 24 07:22:36 2004 +0000 +++ b/channel.h Tue Aug 24 18:12:18 2004 +0000 @@ -27,6 +27,7 @@ #include "includes.h" #include "buffer.h" +#include "circbuffer.h" /* channel->type values */ #define CHANNEL_ID_NONE 0 @@ -50,6 +51,8 @@ #define CHAN_EXTEND_SIZE 3 /* how many extra slots to add when we need more */ #define RECV_MAXWINDOW 6000 /* tweak */ +#define RECV_WINDOWEXTEND (RECV_MAXWINDOW/2) /* We send a "window extend" every + RECV_WINDOWEXTEND bytes */ #define RECV_MAXPACKET 1400 /* tweak */ #define RECV_MINWINDOW 19000 /* when we get below this, we send a windowadjust */ @@ -62,13 +65,13 @@ unsigned int recvwindow, transwindow; unsigned int recvmaxpacket, transmaxpacket; void* typedata; /* a pointer to type specific data */ - int infd; /* stdin for the program, we write to this */ - int outfd; /* stdout for the program, we read from this */ - int errfd; /* stdout for a program. This doesn't really fit here, - but makes the code a lot tidyer without being too bad. This - is -1 for channels which don't requre it. Currently only - a 'session' without a pty will use it */ - buffer *writebuf; /* data for the program */ + int infd; /* data to send over the wire */ + int outfd; /* data for consumption, what was in writebuf */ + int errfd; /* used like infd or errfd, depending if it's client or server. + Doesn't exactly belong here, but is cleaner here */ + circbuffer *writebuf; /* data from the wire, for local consumption */ + circbuffer *extrabuf; /* extended-data for the program - used like writebuf + but for stderr */ int sentclosed, recvclosed; @@ -97,6 +100,7 @@ void chancleanup(); void setchannelfds(fd_set *readfd, fd_set *writefd); void channelio(fd_set *readfd, fd_set *writefd); +struct Channel* getchannel(unsigned int chan); struct Channel* newchannel(unsigned int remotechan, const struct ChanType *type, unsigned int transwindow, unsigned int transmaxpacket); @@ -106,10 +110,16 @@ void send_msg_channel_failure(struct Channel *channel); void send_msg_channel_success(struct Channel *channel); void recv_msg_channel_data(); +void recv_msg_channel_extended_data(); void recv_msg_channel_window_adjust(); void recv_msg_channel_close(); void recv_msg_channel_eof(); +void common_recv_msg_channel_data(struct Channel *channel, int fd, + circbuffer * buf); + +const struct ChanType clichansess; + #ifdef USING_LISTENERS int send_msg_channel_open_init(int fd, const struct ChanType *type); void recv_msg_channel_open_confirmation(); diff -r e13f8a712a1c -r d3eb1fa8484e cli-channel.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-channel.c Tue Aug 24 18:12:18 2004 +0000 @@ -0,0 +1,65 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002-2004 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 "channel.h" +#include "buffer.h" +#include "circbuffer.h" +#include "dbutil.h" +#include "session.h" +#include "ssh.h" + +/* We receive channel data - only used by the client chansession code*/ +void recv_msg_channel_extended_data() { + + unsigned int chan; + struct Channel *channel; + unsigned int datatype; + + TRACE(("enter recv_msg_channel_extended_data")); + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + if (channel->type != &clichansess) { + TRACE(("leave recv_msg_channel_extended_data: chantype is wrong")); + return; /* we just ignore it */ + } + + datatype = buf_getint(ses.payload); + + if (datatype != SSH_EXTENDED_DATA_STDERR) { + TRACE(("leave recv_msg_channel_extended_data: wrong datatype: %d", + datatype)); + return; + } + + common_recv_msg_channel_data(channel, channel->errfd, channel->extrabuf); + + TRACE(("leave recv_msg_channel_extended_data")); +} diff -r e13f8a712a1c -r d3eb1fa8484e cli-chansession.c --- a/cli-chansession.c Tue Aug 24 07:22:36 2004 +0000 +++ b/cli-chansession.c Tue Aug 24 18:12:18 2004 +0000 @@ -44,7 +44,7 @@ static void cli_tty_setup(); void cli_tty_cleanup(); -static const struct ChanType clichansess = { +const struct ChanType clichansess = { 0, /* sepfds */ "session", /* name */ cli_initchansess, /* inithandler */ @@ -316,7 +316,8 @@ channel->infd = STDOUT_FILENO; channel->outfd = STDIN_FILENO; - //channel->errfd = STDERR_FILENO; + channel->errfd = STDERR_FILENO; + channel->extrabuf = buf_new(RECV_MAXWINDOW); if (cli_opts.wantpty) { send_chansess_pty_req(channel); diff -r e13f8a712a1c -r d3eb1fa8484e cli-session.c --- a/cli-session.c Tue Aug 24 07:22:36 2004 +0000 +++ b/cli-session.c Tue Aug 24 18:12:18 2004 +0000 @@ -48,6 +48,7 @@ static const packettype cli_packettypes[] = { /* TYPE, FUNCTION */ {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, + {SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */ {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */ diff -r e13f8a712a1c -r d3eb1fa8484e common-channel.c --- a/common-channel.c Tue Aug 24 07:22:36 2004 +0000 +++ b/common-channel.c Tue Aug 24 18:12:18 2004 +0000 @@ -1,7 +1,7 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,6 +29,7 @@ #include "packet.h" #include "ssh.h" #include "buffer.h" +#include "circbuffer.h" #include "dbutil.h" #include "channel.h" #include "ssh.h" @@ -147,7 +148,8 @@ newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ newchan->initconn = 0; - newchan->writebuf = buf_new(RECV_MAXWINDOW); + newchan->writebuf = cbuf_new(RECV_MAXWINDOW); + newchan->extrabuf = NULL; /* The user code can set it up */ newchan->recvwindow = RECV_MAXWINDOW; newchan->recvmaxpacket = RECV_MAXPACKET; @@ -160,7 +162,7 @@ } /* Get the channel structure corresponding to a channel number */ -static struct Channel* getchannel(unsigned int chan) { +struct Channel* getchannel(unsigned int chan) { if (chan >= ses.chansize || ses.channels[chan] == NULL) { return NULL; } @@ -345,21 +347,23 @@ TRACE(("leave send_msg_channel_eof")); } -/* Called to write data out to the server side of a channel (eg a shell or a - * program. +/* Called to write data out to the local side of the channel. * Only called when we know we can write to a channel, writes as much as * possible */ static void writechannel(struct Channel* channel) { int len, maxlen; - buffer *buf; + circbuffer *cbuf; TRACE(("enter writechannel")); - buf = channel->writebuf; - maxlen = buf->len - buf->pos; + cbuf = channel->writebuf; + maxlen = cbuf_readlen(cbuf); - len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); + TRACE(("maxlen = %d", maxlen)); + + /* Write the data out */ + len = write(channel->infd, cbuf_readptr(cbuf, maxlen), maxlen); if (len <= 0) { if (len < 0 && errno != EINTR) { /* no more to write */ @@ -368,26 +372,27 @@ TRACE(("leave writechannel: len <= 0")); return; } - - if (len == maxlen) { - buf_setpos(buf, 0); - buf_setlen(buf, 0); + + TRACE(("len = %d", len)); + cbuf_incrread(cbuf, len); + + if (len == maxlen && channel->recveof) { + /* Check if we're closing up */ + closeinfd(channel); + return; + TRACE(("leave writechannel: recveof set")); - if (channel->recveof) { - /* we're closing up */ - closeinfd(channel); - return; - TRACE(("leave writechannel: recveof set")); - } + } - /* extend the window if we're at the end*/ - /* TODO - this is inefficient */ - send_msg_channel_window_adjust(channel, buf->size - - channel->recvwindow); - channel->recvwindow = buf->size; - } else { - buf_incrpos(buf, len); + /* Window adjust handling */ + if (channel->recvwindow < (RECV_MAXWINDOW - RECV_WINDOWEXTEND)) { + /* Set it back to max window */ + send_msg_channel_window_adjust(channel, RECV_MAXWINDOW - + channel->recvwindow); + channel->recvwindow = RECV_MAXWINDOW; } + + TRACE(("leave writechannel")); } @@ -405,32 +410,38 @@ continue; } - /* stdout and stderr */ + /* Stuff to put over the wire */ if (channel->transwindow > 0) { - /* stdout */ if (channel->outfd >= 0) { - /* there's space to read more from the program */ FD_SET(channel->outfd, readfd); } - /* stderr */ - if (channel->errfd >= 0) { + + if (channel->extrabuf == NULL && channel->errfd >= 0) { FD_SET(channel->errfd, readfd); } } + /* For checking FD status (ie closure etc) - we don't actually + * read data from infd */ if (channel->infd >= 0 && channel->infd != channel->outfd) { FD_SET(channel->infd, readfd); } - /* stdin */ - if (channel->infd >= 0 && - (channel->writebuf->pos < channel->writebuf->len || - channel->initconn)) { - /* there's space to write more to the program */ - FD_SET(channel->infd, writefd); + /* Stuff from the wire, to local program/shell/user etc */ + if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 ) + || channel->initconn) { + + FD_SET(channel->infd, writefd); } + /* + if (channel->extrabuf != NULL && channel->errfd >= 0 + && cbuf_getavail(channel->extrabuf) > 0 ) { + FD_SET(channel->errfd, writefd); + } + */ + } /* foreach channel */ #ifdef USING_LISTENERS @@ -457,7 +468,7 @@ } channel->recveof = 1; - if (channel->writebuf->len == 0) { + if (cbuf_getused(channel->writebuf) == 0) { closeinfd(channel); } @@ -499,9 +510,15 @@ TRACE(("enter removechannel")); TRACE(("channel index is %d", channel->index)); - buf_free(channel->writebuf); + cbuf_free(channel->writebuf); channel->writebuf = NULL; + if (channel->extrabuf) { + cbuf_free(channel->extrabuf); + channel->extrabuf = NULL; + } + + /* close the FDs in case they haven't been done * yet (ie they were shutdown etc */ close(channel->infd); @@ -623,60 +640,75 @@ TRACE(("leave send_msg_channel_data")); } - -/* when we receive channel data, put it in a buffer for writing to the program/ - * shell etc */ +/* We receive channel data */ void recv_msg_channel_data() { unsigned int chan; - struct Channel * channel; - unsigned int datalen; - unsigned int pos; - unsigned int maxdata; + struct Channel *channel; - TRACE(("enter recv_msg_channel_data")); - chan = buf_getint(ses.payload); channel = getchannel(chan); + if (channel == NULL) { dropbear_exit("Unknown channel"); } + common_recv_msg_channel_data(channel, channel->infd, channel->writebuf); +} + +/* Shared for data and stderr data - when we receive data, put it in a buffer + * for writing to the local file descriptor */ +void common_recv_msg_channel_data(struct Channel *channel, int fd, + circbuffer * cbuf) { + + unsigned int datalen; + unsigned int maxdata; + unsigned int buflen; + unsigned int len; + + TRACE(("enter recv_msg_channel_data")); + if (channel->recveof) { dropbear_exit("received data after eof"); } - if (channel->infd < 0) { + if (fd < 0) { dropbear_exit("received data with bad infd"); } datalen = buf_getint(ses.payload); + TRACE(("datalen = %d", datalen)); + /* if the client is going to send us more data than we've allocated, then * it has ignored the windowsize, so we "MAY ignore all extra data" */ - maxdata = channel->writebuf->size - channel->writebuf->pos; + maxdata = cbuf_getavail(cbuf); + TRACE(("maxdata = %d", maxdata)); if (datalen > maxdata) { TRACE(("Warning: recv_msg_channel_data: extra data past window")); datalen = maxdata; } - /* write to the buffer - we always append to the end of the buffer */ - pos = channel->writebuf->pos; - buf_setpos(channel->writebuf, channel->writebuf->len); - memcpy(buf_getwriteptr(channel->writebuf, datalen), - buf_getptr(ses.payload, datalen), datalen); - buf_incrwritepos(channel->writebuf, datalen); - buf_setpos(channel->writebuf, pos); /* revert pos */ + + /* We may have to run throught twice, if the buffer wraps around. Can't + * just "leave it for next time" like with writechannel, since this + * is payload data */ + len = datalen; + while (len > 0) { + buflen = cbuf_writelen(cbuf); + TRACE(("buflen = %d", buflen)); + buflen = MIN(buflen, len); + TRACE(("buflenmin = %d", buflen)); + + memcpy(cbuf_writeptr(cbuf, buflen), + buf_getptr(ses.payload, buflen), buflen); + cbuf_incrwrite(cbuf, buflen); + len -= buflen; + TRACE(("len = %d", buflen)); + } channel->recvwindow -= datalen; - /* matt - this might be for later */ -/* if (channel->recvwindow < RECV_MINWINDOW) { - send_msg_channel_window_adjust(channel, - RECV_MAXWINDOW - channel->recvwindow); - channel->recvwindow = RECV_MAXWINDOW; - }*/ - TRACE(("leave recv_msg_channel_data")); } diff -r e13f8a712a1c -r d3eb1fa8484e common-session.c --- a/common-session.c Tue Aug 24 07:22:36 2004 +0000 +++ b/common-session.c Tue Aug 24 18:12:18 2004 +0000 @@ -235,7 +235,7 @@ * account for wrappers/cruft etc. According to the spec only the client * needs to handle this, but no harm in letting the server handle it too */ for (i = 0; i < 10; i++) { - len = ident_readln(ses.sock, linebuf, sizzeof(linebuf)); + len = ident_readln(ses.sock, linebuf, sizeof(linebuf)); if (len < 0 && errno != EINTR) { /* It failed */ diff -r e13f8a712a1c -r d3eb1fa8484e dbutil.c --- a/dbutil.c Tue Aug 24 07:22:36 2004 +0000 +++ b/dbutil.c Tue Aug 24 18:12:18 2004 +0000 @@ -146,6 +146,7 @@ fprintf(stderr, "TRACE: "); vfprintf(stderr, format, param); fprintf(stderr, "\n"); + fflush(stderr); va_end(param); } #endif /* DEBUG_TRACE */ diff -r e13f8a712a1c -r d3eb1fa8484e debug.h --- a/debug.h Tue Aug 24 07:22:36 2004 +0000 +++ b/debug.h Tue Aug 24 18:12:18 2004 +0000 @@ -38,7 +38,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -/* #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 diff -r e13f8a712a1c -r d3eb1fa8484e signkey.c --- a/signkey.c Tue Aug 24 07:22:36 2004 +0000 +++ b/signkey.c Tue Aug 24 18:12:18 2004 +0000 @@ -153,6 +153,7 @@ m_free(ident); if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { + TRACE(("wrong key type: %d %d", *type, keytype)); return DROPBEAR_FAILURE; }