summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2015-03-01 00:44:45 +0800
committerMatt Johnston <matt@ucc.asn.au>2015-03-01 00:44:45 +0800
commit8b562aec18e068fb233cba8826a7a398e9157815 (patch)
tree8fb4210b7cd03f08e5c85f87cf1a26f782c637c9
parente7d88b53c33de8c982de78c8b172f1f6d4625c1a (diff)
downloaddropbear-8b562aec18e068fb233cba8826a7a398e9157815.tar.gz
Avoid copying data into circular buffer
-rw-r--r--circbuffer.c17
-rw-r--r--circbuffer.h3
-rw-r--r--common-channel.c86
-rw-r--r--netio.h1
4 files changed, 95 insertions, 12 deletions
diff --git a/circbuffer.c b/circbuffer.c
index 55c4422..949fa84 100644
--- a/circbuffer.c
+++ b/circbuffer.c
@@ -110,6 +110,21 @@ unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) {
return &cbuf->data[cbuf->readpos];
}
+void cbuf_readptrs(circbuffer *cbuf,
+ unsigned char **p1, unsigned int *len1,
+ unsigned char **p2, unsigned int *len2) {
+ *p1 = &cbuf->data[cbuf->readpos];
+ *len1 = MIN(cbuf->used, cbuf->size - cbuf->readpos);
+
+ if (*len1 < cbuf->used) {
+ *p2 = cbuf->data;
+ *len2 = cbuf->used - *len1;
+ } else {
+ *p2 = NULL;
+ *len2 = 0;
+ }
+}
+
unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) {
if (len > cbuf_writelen(cbuf)) {
@@ -131,9 +146,11 @@ 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;
diff --git a/circbuffer.h b/circbuffer.h
index 32ed13c..81bb91d 100644
--- a/circbuffer.h
+++ b/circbuffer.h
@@ -44,6 +44,9 @@ 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);
+void cbuf_readptrs(circbuffer *cbuf,
+ unsigned char **p1, unsigned int *len1,
+ unsigned char **p2, unsigned int *len2);
unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len);
void cbuf_incrwrite(circbuffer *cbuf, unsigned int len);
void cbuf_incrread(circbuffer *cbuf, unsigned int len);
diff --git a/common-channel.c b/common-channel.c
index 3622729..b6c9673 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -42,7 +42,8 @@ static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
static void send_msg_channel_open_confirmation(struct Channel* channel,
unsigned int recvwindow,
unsigned int recvmaxpacket);
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+ const unsigned char *moredata, unsigned int *morelen);
static void send_msg_channel_window_adjust(struct Channel *channel,
unsigned int incr);
static void send_msg_channel_data(struct Channel *channel, int isextended);
@@ -241,14 +242,14 @@ void channelio(fd_set *readfds, fd_set *writefds) {
/* write to program/pipe stdin */
if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
- writechannel(channel, channel->writefd, channel->writebuf);
+ writechannel(channel, channel->writefd, channel->writebuf, NULL, NULL);
do_check_close = 1;
}
/* stderr for client mode */
if (ERRFD_IS_WRITE(channel)
&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
- writechannel(channel, channel->errfd, channel->extrabuf);
+ writechannel(channel, channel->errfd, channel->extrabuf, NULL, NULL);
do_check_close = 1;
}
@@ -434,14 +435,67 @@ static void send_msg_channel_eof(struct Channel *channel) {
}
/* 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 fd, circbuffer *cbuf) {
+ 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) {
- int len, maxlen;
+ struct iovec iov[3];
+ unsigned char *circ_p1, *circ_p2;
+ unsigned int circ_len1, circ_len2;
+ int io_count = 0;
+
+ int written;
TRACE(("enter writechannel fd %d", fd))
+ cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2);
+
+ if (circ_len1 > 0) {
+ TRACE(("circ1 %d", circ_len1))
+ iov[io_count].iov_base = circ_p1;
+ iov[io_count].iov_len = circ_len1;
+ io_count++;
+ }
+
+ if (circ_len2 > 0) {
+ TRACE(("circ2 %d", circ_len2))
+ iov[io_count].iov_base = circ_p2;
+ iov[io_count].iov_len = circ_len2;
+ io_count++;
+ }
+
+ if (morelen) {
+ assert(moredata);
+ TRACE(("more %d", *morelen))
+ iov[io_count].iov_base = (void*)moredata;
+ iov[io_count].iov_len = *morelen;
+ io_count++;
+ }
+
+ if (morelen) {
+ /* Default return value, none consumed */
+ *morelen = 0;
+ }
+
+ written = writev(fd, iov, io_count);
+
+ if (written < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ TRACE(("errno %d len %d", errno, len))
+ close_chan_fd(channel, fd, SHUT_WR);
+ }
+ } else {
+ int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written);
+ cbuf_incrread(cbuf, cbuf_written);
+ if (morelen) {
+ *morelen = written - cbuf_written;
+ }
+ channel->recvdonelen += written;
+ }
+
+#if 0
+
maxlen = cbuf_readlen(cbuf);
/* Write the data out */
@@ -458,10 +512,10 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
cbuf_incrread(cbuf, len);
channel->recvdonelen += len;
+#endif
/* Window adjust handling */
if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
- /* Set it back to max window */
send_msg_channel_window_adjust(channel, channel->recvdonelen);
channel->recvwindow += channel->recvdonelen;
channel->recvdonelen = 0;
@@ -745,6 +799,7 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd,
unsigned int maxdata;
unsigned int buflen;
unsigned int len;
+ unsigned int consumed;
TRACE(("enter recv_msg_channel_data"))
@@ -771,6 +826,17 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd,
dropbear_exit("Oversized packet");
}
+ dropbear_assert(channel->recvwindow >= datalen);
+ channel->recvwindow -= datalen;
+ dropbear_assert(channel->recvwindow <= opts.recv_window);
+
+ consumed = datalen;
+ writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed);
+
+ datalen -= consumed;
+ buf_incrpos(ses.payload, consumed);
+
+
/* 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 */
@@ -786,10 +852,6 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd,
len -= buflen;
}
- dropbear_assert(channel->recvwindow >= datalen);
- channel->recvwindow -= datalen;
- dropbear_assert(channel->recvwindow <= opts.recv_window);
-
TRACE(("leave recv_msg_channel_data"))
}
diff --git a/netio.h b/netio.h
index 1bf08ce..33e1b4d 100644
--- a/netio.h
+++ b/netio.h
@@ -27,6 +27,7 @@ struct dropbear_progress_connection;
errstring is only set on DROPBEAR_FAILURE, returns failure message for the last attempted socket */
typedef void(*connect_callback)(int result, int sock, void* data, const char* errstring);
+/* Always returns a progress connection, if it fails it will call the callback at a later point */
struct dropbear_progress_connection * connect_remote (const char* remotehost, const char* remoteport,
connect_callback cb, void *cb_data);