From 19e1afbd1ca6d306166ce74bcd6c6889f8d196f3 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Sat, 2 May 2015 22:47:25 +0800
Subject: [PATCH] Fix no-writev fallback

---
 LICENSE          |  2 +-
 circbuffer.c     | 31 ----------------------
 circbuffer.h     |  3 +--
 common-channel.c | 68 +++++++++++++++++++++++++++++++-----------------
 common-session.c |  6 ++---
 packet.c         |  6 +++++
 6 files changed, 55 insertions(+), 61 deletions(-)

diff --git a/LICENSE b/LICENSE
index 828b9af3..c400d946 100644
--- a/LICENSE
+++ b/LICENSE
@@ -8,7 +8,7 @@ The majority of code is written by Matt Johnston, under the license below.
 Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
 same license:
 
-Copyright (c) 2002-2014 Matt Johnston
+Copyright (c) 2002-2015 Matt Johnston
 Portions copyright (c) 2004 Mihnea Stoenescu
 All rights reserved.
 
diff --git a/circbuffer.c b/circbuffer.c
index 949fa848..c0c8641d 100644
--- a/circbuffer.c
+++ b/circbuffer.c
@@ -67,23 +67,6 @@ unsigned int cbuf_getavail(circbuffer * cbuf) {
 
 }
 
-unsigned int cbuf_readlen(circbuffer *cbuf) {
-
-	dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
-	dropbear_assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
-
-	if (cbuf->used == 0) {
-		TRACE(("cbuf_readlen: unused buffer"))
-		return 0;
-	}
-
-	if (cbuf->readpos < cbuf->writepos) {
-		return cbuf->writepos - cbuf->readpos;
-	}
-
-	return cbuf->size - cbuf->readpos;
-}
-
 unsigned int cbuf_writelen(circbuffer *cbuf) {
 
 	dropbear_assert(cbuf->used <= cbuf->size);
@@ -102,14 +85,6 @@ unsigned int cbuf_writelen(circbuffer *cbuf) {
 	return cbuf->size - cbuf->writepos;
 }
 
-unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) {
-	if (len > cbuf_readlen(cbuf)) {
-		dropbear_exit("Bad cbuf read");
-	}
-
-	return &cbuf->data[cbuf->readpos];
-}
-
 void cbuf_readptrs(circbuffer *cbuf, 
 	unsigned char **p1, unsigned int *len1, 
 	unsigned char **p2, unsigned int *len2) {
@@ -146,12 +121,6 @@ void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) {
 
 
 void cbuf_incrread(circbuffer *cbuf, unsigned int len) {
-#if 0
-	if (len > cbuf_readlen(cbuf)) {
-		dropbear_exit("Bad cbuf read");
-	}
-#endif
-
 	dropbear_assert(cbuf->used >= len);
 	cbuf->used -= len;
 	cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
diff --git a/circbuffer.h b/circbuffer.h
index 81bb91db..744a2c10 100644
--- a/circbuffer.h
+++ b/circbuffer.h
@@ -40,10 +40,9 @@ void cbuf_free(circbuffer * cbuf);
 
 unsigned int cbuf_getused(circbuffer * cbuf); /* how much data stored */
 unsigned int cbuf_getavail(circbuffer * cbuf); /* how much we can write */
-unsigned int cbuf_readlen(circbuffer *cbuf); /* max linear read len */
 unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */
 
-unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len);
+/* returns pointers to the two portions of the circular buffer that can be read */
 void cbuf_readptrs(circbuffer *cbuf, 
 	unsigned char **p1, unsigned int *len1, 
 	unsigned char **p2, unsigned int *len2);
diff --git a/common-channel.c b/common-channel.c
index 6dce4977..5f4051e5 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -434,10 +434,36 @@ static void send_msg_channel_eof(struct Channel *channel) {
 	TRACE(("leave send_msg_channel_eof"))
 }
 
-/* Called to write data out to the local side of the channel. 
-   Writes the circular buffer contents and also the "moredata" buffer
-   if not null. Will ignore EAGAIN */
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+#ifndef HAVE_WRITEV
+static void writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf,
+	const unsigned char *UNUSED(moredata), unsigned int *morelen) {
+
+	unsigned char *circ_p1, *circ_p2;
+	unsigned int circ_len1, circ_len2;
+	ssize_t written;
+
+	if (morelen) {
+		/* fallback doesn't consume moredata */
+		*morelen = 0;
+	}
+
+	/* Write the first portion of the circular buffer */
+	cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2);
+	written = write(fd, circ_p1, circ_len1);
+	if (written < 0) {
+		if (errno != EINTR && errno != EAGAIN) {
+			TRACE(("channel IO write error fd %d %s", fd, strerror(errno)))
+			close_chan_fd(channel, fd, SHUT_WR);
+		}
+	} else {
+		cbuf_incrread(cbuf, written);
+		channel->recvdonelen += written;
+	}
+}
+#endif /* !HAVE_WRITEV */
+
+#ifdef HAVE_WRITEV
+static void writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf,
 	const unsigned char *moredata, unsigned int *morelen) {
 
 	struct iovec iov[3];
@@ -445,9 +471,7 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
 	unsigned int circ_len1, circ_len2;
 	int io_count = 0;
 
-	int written;
-
-	TRACE(("enter writechannel fd %d", fd))
+	ssize_t written;
 
 	cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2);
 
@@ -502,24 +526,19 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
 		channel->recvdonelen += written;
 	}
 
-#if 0
-
-	maxlen = cbuf_readlen(cbuf);
-
-	/* Write the data out */
-	len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
-	if (len <= 0) {
-		TRACE(("errno %d len %d", errno, len))
-		if (len < 0 && errno != EINTR) {
-			close_chan_fd(channel, fd, SHUT_WR);
-		}
-		TRACE(("leave writechannel: len <= 0"))
-		return;
-	}
-	TRACE(("writechannel wrote %d", len))
+}
+#endif /* HAVE_WRITEV */
 
-	cbuf_incrread(cbuf, len);
-	channel->recvdonelen += len;
+/* Called to write data out to the local side of the channel. 
+   Writes the circular buffer contents and also the "moredata" buffer
+   if not null. Will ignore EAGAIN */
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+	const unsigned char *moredata, unsigned int *morelen) {
+	TRACE(("enter writechannel fd %d", fd))
+#ifdef HAVE_WRITEV
+	writechannel_writev(channel, fd, cbuf, moredata, morelen);
+#else
+	writechannel_fallback(channel, fd, cbuf, moredata, morelen);
 #endif
 
 	/* Window adjust handling */
@@ -537,6 +556,7 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
 	TRACE(("leave writechannel"))
 }
 
+
 /* Set the file descriptors for the main select in session.c
  * This avoid channels which don't have any window available, are closed, etc*/
 void setchannelfds(fd_set *readfds, fd_set *writefds, int allow_reads) {
diff --git a/common-session.c b/common-session.c
index 44e70308..083b5c52 100644
--- a/common-session.c
+++ b/common-session.c
@@ -1,7 +1,7 @@
 /*
  * Dropbear - a SSH2 server
  * 
- * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) Matt Johnston
  * All rights reserved.
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -280,7 +280,7 @@ void session_cleanup() {
 		return;
 	}
 
-	/* Beware of changing order of functions here. */
+	/* BEWARE of changing order of functions here. */
 
 	/* Must be before extra_session_cleanup() */
 	chancleanup();
@@ -289,7 +289,7 @@ void session_cleanup() {
 		ses.extra_session_cleanup();
 	}
 
-	/* After these are freed most functions will exit */
+	/* After these are freed most functions will fail */
 #ifdef DROPBEAR_CLEANUP
 	/* listeners call cleanup functions, this should occur before
 	other session state is freed. */
diff --git a/packet.c b/packet.c
index 1d1be843..19b57592 100644
--- a/packet.c
+++ b/packet.c
@@ -61,6 +61,10 @@ void write_packet() {
 	/* 50 is somewhat arbitrary */
 	unsigned int iov_count = 50;
 	struct iovec iov[50];
+#else
+	int len;
+	buffer* writebuf;
+	int packet_type;
 #endif
 	
 	TRACE2(("enter write_packet"))
@@ -97,6 +101,8 @@ void write_packet() {
 	 * a cleartext packet_type indicator */
 	packet_type = writebuf->data[writebuf->len-1];
 	len = writebuf->len - 1 - writebuf->pos;
+	TRACE2(("write_packet type %d len %d/%d", packet_type,
+			len, writebuf->len-1))
 	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
 	written = write(ses.sock_out, buf_getptr(writebuf, len), len);
-- 
GitLab