diff common-channel.c @ 108:10f4d3319780

- added circular buffering for channels - added stderr support for the client - cleaned up a bunch of "unused" warnings, duplicated header definitions - added exit-status support for the client
author Matt Johnston <matt@ucc.asn.au>
date Thu, 26 Aug 2004 13:16:40 +0000
parents d3eb1fa8484e
children 2e9d1f29c50f
line wrap: on
line diff
--- a/common-channel.c	Tue Aug 24 18:12:18 2004 +0000
+++ b/common-channel.c	Thu Aug 26 13:16:40 2004 +0000
@@ -40,7 +40,7 @@
 static void send_msg_channel_open_confirmation(struct Channel* channel,
 		unsigned int recvwindow, 
 		unsigned int recvmaxpacket);
-static void writechannel(struct Channel *channel);
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
 static void send_msg_channel_window_adjust(struct Channel *channel, 
 		unsigned int incr);
 static void send_msg_channel_data(struct Channel *channel, int isextended,
@@ -151,6 +151,7 @@
 	newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
 	newchan->extrabuf = NULL; /* The user code can set it up */
 	newchan->recvwindow = RECV_MAXWINDOW;
+	newchan->recvdonelen = 0;
 	newchan->recvmaxpacket = RECV_MAXPACKET;
 
 	ses.channels[i] = newchan;
@@ -220,7 +221,13 @@
 				continue; /* Important not to use the channel after
 							 checkinitdone(), as it may be NULL */
 			}
-			writechannel(channel);
+			writechannel(channel, channel->infd, channel->writebuf);
+		}
+		
+		/* stderr for client mode */
+		if (channel->extrabuf != NULL 
+				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) {
+			writechannel(channel, channel->errfd, channel->extrabuf);
 		}
 	
 		/* now handle any of the channel-closing type stuff */
@@ -350,33 +357,30 @@
 /* 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) {
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
 
 	int len, maxlen;
-	circbuffer *cbuf;
 
 	TRACE(("enter writechannel"));
 
-	cbuf = channel->writebuf;
 	maxlen = cbuf_readlen(cbuf);
 
-	TRACE(("maxlen = %d", maxlen));
-
 	/* Write the data out */
-	len = write(channel->infd, cbuf_readptr(cbuf, maxlen), maxlen);
+	len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
 	if (len <= 0) {
 		if (len < 0 && errno != EINTR) {
-			/* no more to write */
+			/* no more to write - we close it even if the fd was stderr, since
+			 * that's a nasty failure too */
 			closeinfd(channel);
 		}
 		TRACE(("leave writechannel: len <= 0"));
 		return;
 	}
 
-	TRACE(("len = %d", len));
 	cbuf_incrread(cbuf, len);
+	channel->recvdonelen += len;
 
-	if (len == maxlen && channel->recveof) { 
+	if (fd == channel->infd && len == maxlen && channel->recveof) { 
 		/* Check if we're closing up */
 		closeinfd(channel);
 		return;
@@ -385,12 +389,17 @@
 	}
 
 	/* Window adjust handling */
-	if (channel->recvwindow < (RECV_MAXWINDOW - RECV_WINDOWEXTEND)) {
+	if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
 		/* Set it back to max window */
-		send_msg_channel_window_adjust(channel, RECV_MAXWINDOW -
-				channel->recvwindow);
-		channel->recvwindow = RECV_MAXWINDOW;
+		send_msg_channel_window_adjust(channel, channel->recvdonelen);
+		channel->recvwindow += channel->recvdonelen;
+		channel->recvdonelen = 0;
 	}
+
+	assert(channel->recvwindow <= RECV_MAXWINDOW);
+	assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
+	assert(channel->extrabuf == NULL ||
+			channel->recvwindow <= cbuf_getavail(channel->extrabuf));
 	
 	
 	TRACE(("leave writechannel"));
@@ -424,6 +433,8 @@
 
 		/* For checking FD status (ie closure etc) - we don't actually
 		 * read data from infd */
+		TRACE(("infd = %d, outfd %d, bufused %d", channel->infd, channel->outfd,
+					cbuf_getused(channel->writebuf) ));
 		if (channel->infd >= 0 && channel->infd != channel->outfd) {
 			FD_SET(channel->infd, readfd);
 		}
@@ -435,12 +446,10 @@
 				FD_SET(channel->infd, writefd);
 		}
 
-		/*
 		if (channel->extrabuf != NULL && channel->errfd >= 0 
-				&& cbuf_getavail(channel->extrabuf) > 0 ) {
+				&& cbuf_getused(channel->extrabuf) > 0 ) {
 				FD_SET(channel->errfd, writefd);
 		}
-		*/
 
 	} /* foreach channel */
 
@@ -468,7 +477,9 @@
 	}
 
 	channel->recveof = 1;
-	if (cbuf_getused(channel->writebuf) == 0) {
+	if (cbuf_getused(channel->writebuf) == 0
+			&& (channel->extrabuf == NULL 
+					|| cbuf_getused(channel->extrabuf) == 0)) {
 		closeinfd(channel);
 	}
 
@@ -678,36 +689,34 @@
 
 	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 = cbuf_getavail(cbuf);
-	TRACE(("maxdata = %d", maxdata));
+
+	/* Whilst the spec says we "MAY ignore data past the end" this could
+	 * lead to corrupted file transfers etc (chunks missed etc). It's better to
+	 * just die horribly */
 	if (datalen > maxdata) {
-		TRACE(("Warning: recv_msg_channel_data: extra data past window"));
-		datalen = maxdata;
+		dropbear_exit("Oversized packet");
 	}
 
-
 	/* 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);
+		buf_incrpos(ses.payload, buflen);
 		len -= buflen;
-		TRACE(("len = %d", buflen));
 	}
 
+	assert(channel->recvwindow > datalen);
 	channel->recvwindow -= datalen;
+	assert(channel->recvwindow <= RECV_MAXWINDOW);
 
 	TRACE(("leave recv_msg_channel_data"));
 }