merge of 50be59810e462f9f44f55e421227d6aa0b31982b
and 69b007796063cb5f042be7cca2d479e90db869c3
--HG--
extra : convert_revision : 5d0dfaa8c0ee6c728a3b4f0f0855199ba729db83
diff --git a/cli-chansession.c b/cli-chansession.c
index 50226dd..35be671 100644
--- a/cli-chansession.c
+++ b/cli-chansession.c
@@ -341,8 +341,14 @@
channel->infd = STDOUT_FILENO;
+ setnonblocking(STDOUT_FILENO);
+
channel->outfd = STDIN_FILENO;
+ setnonblocking(STDIN_FILENO);
+
channel->errfd = STDERR_FILENO;
+ setnonblocking(STDERR_FILENO);
+
channel->extrabuf = cbuf_new(RECV_MAXWINDOW);
if (cli_opts.wantpty) {
diff --git a/cli-main.c b/cli-main.c
index 34a1e42..def2c72 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -52,6 +52,10 @@
TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
cli_opts.remotehost, cli_opts.remoteport));
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
0, &error);
diff --git a/common-channel.c b/common-channel.c
index a4cc44b..d30528c 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -192,7 +192,8 @@
}
/* read from program/pipe stderr */
- if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
+ if (channel->extrabuf == NULL &&
+ channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
}
@@ -245,6 +246,14 @@
/* do all the EOF/close type stuff checking for a channel */
static void checkclose(struct Channel *channel) {
+ TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d",
+ channel->infd, channel->outfd,
+ channel->errfd, channel->sentclosed, channel->recvclosed));
+ TRACE(("writebuf %d extrabuf %s extrabuf %d",
+ cbuf_getused(channel->writebuf),
+ channel->writebuf,
+ channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)));
+
if (!channel->sentclosed) {
/* check for exited - currently only used for server sessions,
@@ -257,13 +266,13 @@
if (!channel->senteof
&& channel->outfd == FD_CLOSED
- && channel->errfd == FD_CLOSED) {
+ && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
send_msg_channel_eof(channel);
}
if (channel->infd == FD_CLOSED
- && channel->outfd == FD_CLOSED
- && channel->errfd == FD_CLOSED) {
+ && channel->outfd == FD_CLOSED
+ && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
send_msg_channel_close(channel);
}
}
@@ -383,9 +392,8 @@
if (fd == channel->infd && len == maxlen && channel->recveof) {
/* Check if we're closing up */
closeinfd(channel);
- return;
TRACE(("leave writechannel: recveof set"));
-
+ return;
}
/* Window adjust handling */
@@ -433,7 +441,9 @@
/* 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,
+ TRACE(("infd = %d, outfd %d, errfd %d, bufused %d",
+ channel->infd, channel->outfd,
+ channel->errfd,
cbuf_getused(channel->writebuf) ));
if (channel->infd >= 0 && channel->infd != channel->outfd) {
FD_SET(channel->infd, readfd);
@@ -534,9 +544,8 @@
* yet (ie they were shutdown etc */
close(channel->infd);
close(channel->outfd);
- if (channel->errfd >= 0) {
- close(channel->errfd);
- }
+ close(channel->errfd);
+
channel->typedata = NULL;
deletechannel(channel);
@@ -619,16 +628,19 @@
}
/* read the data */
+ TRACE(("maxlen %d", maxlen));
buf = buf_new(maxlen);
+ TRACE(("buf pos %d data %x", buf->pos, buf->data));
len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
if (len <= 0) {
/* on error/eof, send eof */
if (len == 0 || errno != EINTR) {
closeoutfd(channel, fd);
- TRACE(("leave send_msg_channel_data: read err %d", channel->index));
}
buf_free(buf);
buf = NULL;
+ TRACE(("leave send_msg_channel_data: read err or EOF for fd %d",
+ channel->index));
return;
}
buf_incrlen(buf, len);
@@ -714,7 +726,7 @@
len -= buflen;
}
- assert(channel->recvwindow > datalen);
+ assert(channel->recvwindow >= datalen);
channel->recvwindow -= datalen;
assert(channel->recvwindow <= RECV_MAXWINDOW);
@@ -927,10 +939,7 @@
}
/* set fd non-blocking */
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
- TRACE(("leave send_msg_channel_open_init() - FAILED in fcntl()"));
- return DROPBEAR_FAILURE;
- }
+ setnonblocking(fd);
chan->infd = chan->outfd = fd;
ses.maxfd = MAX(ses.maxfd, fd);
@@ -1034,15 +1043,19 @@
closein = closeout = 1;
}
- if (closeout && fd == channel->errfd) {
- channel->errfd = FD_CLOSED;
- }
if (closeout && fd == channel->outfd) {
channel->outfd = FD_CLOSED;
}
+ if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
+ channel->errfd = FD_CLOSED;
+ }
+
if (closein && fd == channel->infd) {
channel->infd = FD_CLOSED;
}
+ if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
+ channel->errfd = FD_CLOSED;
+ }
}
#endif /* USING_LISTENERS */
diff --git a/dbutil.c b/dbutil.c
index a1ec3ed..c126a2f 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -595,3 +595,13 @@
}
}
+
+void setnonblocking(int fd) {
+
+ TRACE(("setnonblocking: %d", fd));
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ dropbear_exit("Couldn't set nonblocking");
+ }
+ TRACE(("leave setnonblocking"));
+}
diff --git a/dbutil.h b/dbutil.h
index 0409e36..6363f70 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -61,6 +61,7 @@
#define m_free(X) __m_free(X); (X) = NULL;
void __m_free(void* ptr);
void m_burn(void* data, unsigned int len);
+void setnonblocking(int fd);
/* Used to force mp_ints to be initialised */
#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
diff --git a/debug.h b/debug.h
index cc161dc..37f51ac 100644
--- a/debug.h
+++ b/debug.h
@@ -33,12 +33,13 @@
* etc. Don't use this normally, it might cause problems */
/* #define DEBUG_VALGRIND */
-/* Define this to compile in trace debugging printf()s. You'll need to
- * run programs with "-v" to turn this on.
+/* Define this to compile in trace debugging printf()s.
+ * You'll need to run programs with "-v" to turn this on.
+ *
* 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
@@ -49,6 +50,7 @@
/* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon
* output when Dropbear forks. This will allow it gprof to be used.
* It's useful to run dropbear -F, so you don't fork as much */
+/* (This is Linux specific) */
/*#define DEBUG_FORKGPROF*/
/* A couple of flags, not usually useful, and mightn't do anything */
diff --git a/svr-agentfwd.c b/svr-agentfwd.c
index 2674138..60c23f7 100644
--- a/svr-agentfwd.c
+++ b/svr-agentfwd.c
@@ -73,9 +73,7 @@
}
/* set non-blocking */
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
- goto fail;
- }
+ setnonblocking(fd);
/* pass if off to listener */
chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
diff --git a/svr-chansession.c b/svr-chansession.c
index 8c9fa3b..be6678d 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -651,11 +651,10 @@
ses.maxfd = MAX(ses.maxfd, channel->outfd);
ses.maxfd = MAX(ses.maxfd, channel->errfd);
- if ((fcntl(channel->outfd, F_SETFL, O_NONBLOCK) < 0) ||
- (fcntl(channel->infd, F_SETFL, O_NONBLOCK) < 0) ||
- (fcntl(channel->errfd, F_SETFL, O_NONBLOCK) < 0)) {
- dropbear_exit("Couldn't set nonblocking");
- }
+ setnonblocking(channel->outfd);
+ setnonblocking(channel->infd);
+ setnonblocking(channel->errfd);
+
}
#undef FDIN
#undef FDOUT
@@ -761,9 +760,7 @@
/* don't need to set stderr here */
ses.maxfd = MAX(ses.maxfd, chansess->master);
- if (fcntl(chansess->master, F_SETFL, O_NONBLOCK) < 0) {
- dropbear_exit("Couldn't set nonblocking");
- }
+ setnonblocking(chansess->master);
}
diff --git a/svr-x11fwd.c b/svr-x11fwd.c
index 75e94b8..45c9060 100644
--- a/svr-x11fwd.c
+++ b/svr-x11fwd.c
@@ -75,9 +75,7 @@
}
/* set non-blocking */
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
- goto fail;
- }
+ setnonblocking(fd);
/* listener code will handle the socket now.
* No cleanup handler needed, since listener_remove only happens