mirror of
https://github.com/clearml/dropbear
synced 2025-06-26 18:17:32 +00:00
Rearranged (and hopefully simplified) channel close/eof handling
--HG-- branch : channel-fix extra : convert_revision : d44aac5fec50b1d20515da77d410d3c511f16277
This commit is contained in:
parent
df57eb3824
commit
786ea39ac4
200
common-channel.c
200
common-channel.c
@ -43,17 +43,14 @@ static void send_msg_channel_open_confirmation(struct Channel* channel,
|
|||||||
static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
|
static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
|
||||||
static void send_msg_channel_window_adjust(struct Channel *channel,
|
static void send_msg_channel_window_adjust(struct Channel *channel,
|
||||||
unsigned int incr);
|
unsigned int incr);
|
||||||
static void send_msg_channel_data(struct Channel *channel, int isextended,
|
static void send_msg_channel_data(struct Channel *channel, int isextended);
|
||||||
unsigned int exttype);
|
|
||||||
static void send_msg_channel_eof(struct Channel *channel);
|
static void send_msg_channel_eof(struct Channel *channel);
|
||||||
static void send_msg_channel_close(struct Channel *channel);
|
static void send_msg_channel_close(struct Channel *channel);
|
||||||
static void remove_channel(struct Channel *channel);
|
static void remove_channel(struct Channel *channel);
|
||||||
static void delete_channel(struct Channel *channel);
|
static void delete_channel(struct Channel *channel);
|
||||||
static void check_in_progress(struct Channel *channel);
|
static void check_in_progress(struct Channel *channel);
|
||||||
|
static unsigned int write_pending(struct Channel * channel);
|
||||||
static void check_close(struct Channel *channel);
|
static void check_close(struct Channel *channel);
|
||||||
|
|
||||||
static void close_write_fd(struct Channel * channel);
|
|
||||||
static void close_read_fd(struct Channel * channel, int fd);
|
|
||||||
static void close_chan_fd(struct Channel *channel, int fd, int how);
|
static void close_chan_fd(struct Channel *channel, int fd, int how);
|
||||||
|
|
||||||
#define FD_UNINIT (-2)
|
#define FD_UNINIT (-2)
|
||||||
@ -192,7 +189,7 @@ void channelio(fd_set *readfds, fd_set *writefds) {
|
|||||||
struct Channel *channel;
|
struct Channel *channel;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* iterate through all the possible channels */
|
/* foreach channel */
|
||||||
for (i = 0; i < ses.chansize; i++) {
|
for (i = 0; i < ses.chansize; i++) {
|
||||||
|
|
||||||
channel = ses.channels[i];
|
channel = ses.channels[i];
|
||||||
@ -203,31 +200,19 @@ void channelio(fd_set *readfds, fd_set *writefds) {
|
|||||||
|
|
||||||
/* read data and send it over the wire */
|
/* read data and send it over the wire */
|
||||||
if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
|
if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
|
||||||
send_msg_channel_data(channel, 0, 0);
|
send_msg_channel_data(channel, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read stderr data and send it over the wire */
|
/* read stderr data and send it over the wire */
|
||||||
if (channel->extrabuf == NULL &&
|
if (channel->extrabuf == NULL &&
|
||||||
channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
|
channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
|
||||||
send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
|
send_msg_channel_data(channel, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* XXX where is this required? */
|
|
||||||
if (channel->initconn) {
|
|
||||||
/* Handling for "in progress" connection - this is needed
|
|
||||||
* to avoid spinning 100% CPU when we connect to a server
|
|
||||||
* which doesn't send anything (tcpfwding) */
|
|
||||||
check_in_progress(channel);
|
|
||||||
continue; /* Important not to use the channel after
|
|
||||||
check_in_progress(), as it may be NULL */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* write to program/pipe stdin */
|
/* write to program/pipe stdin */
|
||||||
if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
|
if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
|
||||||
if (channel->initconn) {
|
if (channel->initconn) {
|
||||||
/* XXX could this go somewhere cleaner? */
|
/* XXX should this go somewhere cleaner? */
|
||||||
check_in_progress(channel);
|
check_in_progress(channel);
|
||||||
continue; /* Important not to use the channel after
|
continue; /* Important not to use the channel after
|
||||||
check_in_progress(), as it may be NULL */
|
check_in_progress(), as it may be NULL */
|
||||||
@ -241,10 +226,10 @@ void channelio(fd_set *readfds, fd_set *writefds) {
|
|||||||
writechannel(channel, channel->errfd, channel->extrabuf);
|
writechannel(channel, channel->errfd, channel->extrabuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now handle any of the channel-closing type stuff */
|
/* handle any channel closing etc */
|
||||||
check_close(channel);
|
check_close(channel);
|
||||||
|
|
||||||
} /* foreach channel */
|
}
|
||||||
|
|
||||||
/* Listeners such as TCP, X11, agent-auth */
|
/* Listeners such as TCP, X11, agent-auth */
|
||||||
#ifdef USING_LISTENERS
|
#ifdef USING_LISTENERS
|
||||||
@ -253,6 +238,20 @@ void channelio(fd_set *readfds, fd_set *writefds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns true if there is data remaining to be written to stdin or
|
||||||
|
* stderr of a channel's endpoint. */
|
||||||
|
static unsigned int write_pending(struct Channel * channel) {
|
||||||
|
|
||||||
|
if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) {
|
||||||
|
return 1;
|
||||||
|
} else if (channel->errfd >= 0 && channel->extrabuf &&
|
||||||
|
cbuf_getused(channel->writebuf) > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* EOF/close handling */
|
/* EOF/close handling */
|
||||||
static void check_close(struct Channel *channel) {
|
static void check_close(struct Channel *channel) {
|
||||||
|
|
||||||
@ -264,8 +263,13 @@ static void check_close(struct Channel *channel) {
|
|||||||
channel->writebuf,
|
channel->writebuf,
|
||||||
channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)))
|
channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)))
|
||||||
|
|
||||||
/* XXX not good, doesn't flush out */
|
if (!channel->sent_close
|
||||||
if (channel->recv_close) {
|
&& channel->writefd == FD_CLOSED
|
||||||
|
&& (channel->errfd == FD_CLOSED || channel->extrabuf == NULL)) {
|
||||||
|
send_msg_channel_close(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel->recv_close && !write_pending(channel)) {
|
||||||
if (! channel->sent_close) {
|
if (! channel->sent_close) {
|
||||||
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
|
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
|
||||||
send_msg_channel_close(channel);
|
send_msg_channel_close(channel);
|
||||||
@ -274,8 +278,10 @@ static void check_close(struct Channel *channel) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* server chansession channels are special, since readfd mightn't
|
#if 0
|
||||||
* close in the case of "sleep 4 & echo blah" until the sleep is up */
|
// The only use of check_close is "return channel->writefd == -1;" for a server
|
||||||
|
// chansession. Should be able to handle that with just the general
|
||||||
|
// socket close handling...?
|
||||||
if (channel->type->check_close) {
|
if (channel->type->check_close) {
|
||||||
if (channel->type->check_close(channel)) {
|
if (channel->type->check_close(channel)) {
|
||||||
close_write_fd(channel);
|
close_write_fd(channel);
|
||||||
@ -283,6 +289,7 @@ static void check_close(struct Channel *channel) {
|
|||||||
close_read_fd(channel, channel->errfd);
|
close_read_fd(channel, channel->errfd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!channel->sent_eof
|
if (!channel->sent_eof
|
||||||
&& channel->readfd == FD_CLOSED
|
&& channel->readfd == FD_CLOSED
|
||||||
@ -296,25 +303,6 @@ static void check_close(struct Channel *channel) {
|
|||||||
&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
|
&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
|
||||||
send_msg_channel_close(channel);
|
send_msg_channel_close(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX blah */
|
|
||||||
if (channel->recv_eof &&
|
|
||||||
(cbuf_getused(channel->writebuf) == 0
|
|
||||||
&& (channel->extrabuf == NULL
|
|
||||||
|| cbuf_getused(channel->extrabuf) == 0))) {
|
|
||||||
close_write_fd(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When either party wishes to terminate the channel, it sends
|
|
||||||
* SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST
|
|
||||||
* send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
|
|
||||||
* message for the channel. The channel is considered closed for a
|
|
||||||
* party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
|
|
||||||
* the party may then reuse the channel number. A party MAY send
|
|
||||||
* SSH_MSG_CHANNEL_CLOSE without having sent or received
|
|
||||||
* SSH_MSG_CHANNEL_EOF.
|
|
||||||
* (from draft-ietf-secsh-connect)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -398,9 +386,7 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
|
|||||||
len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
|
len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
if (len < 0 && errno != EINTR) {
|
if (len < 0 && errno != EINTR) {
|
||||||
/* no more to write - we close it even if the fd was stderr, since
|
close_chan_fd(channel, fd, SHUT_WR);
|
||||||
* that's a nasty failure too */
|
|
||||||
close_write_fd(channel);
|
|
||||||
}
|
}
|
||||||
TRACE(("leave writechannel: len <= 0"))
|
TRACE(("leave writechannel: len <= 0"))
|
||||||
return;
|
return;
|
||||||
@ -409,6 +395,13 @@ static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
|
|||||||
cbuf_incrread(cbuf, len);
|
cbuf_incrread(cbuf, len);
|
||||||
channel->recvdonelen += len;
|
channel->recvdonelen += len;
|
||||||
|
|
||||||
|
/* We're closing out */
|
||||||
|
if (channel->recv_eof && cbuf_getused(cbuf) == 0) {
|
||||||
|
TRACE(("leave writechannel"))
|
||||||
|
close_chan_fd(channel, fd, SHUT_WR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Window adjust handling */
|
/* Window adjust handling */
|
||||||
if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
|
if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
|
||||||
/* Set it back to max window */
|
/* Set it back to max window */
|
||||||
@ -572,16 +565,12 @@ void recv_msg_channel_request() {
|
|||||||
* chan is the remote channel, isextended is 0 if it is normal data, 1
|
* chan is the remote channel, isextended is 0 if it is normal data, 1
|
||||||
* if it is extended data. if it is extended, then the type is in
|
* if it is extended data. if it is extended, then the type is in
|
||||||
* exttype */
|
* exttype */
|
||||||
static void send_msg_channel_data(struct Channel *channel, int isextended,
|
static void send_msg_channel_data(struct Channel *channel, int isextended) {
|
||||||
unsigned int exttype) {
|
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
size_t maxlen, size_pos;
|
size_t maxlen, size_pos;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
/* TRACE(("enter send_msg_channel_data"))
|
|
||||||
TRACE(("extended = %d type = %d", isextended, exttype))*/
|
|
||||||
|
|
||||||
CHECKCLEARTOWRITE();
|
CHECKCLEARTOWRITE();
|
||||||
|
|
||||||
dropbear_assert(!channel->sent_close);
|
dropbear_assert(!channel->sent_close);
|
||||||
@ -608,7 +597,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended,
|
|||||||
isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA);
|
isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA);
|
||||||
buf_putint(ses.writepayload, channel->remotechan);
|
buf_putint(ses.writepayload, channel->remotechan);
|
||||||
if (isextended) {
|
if (isextended) {
|
||||||
buf_putint(ses.writepayload, exttype);
|
buf_putint(ses.writepayload, SSH_EXTENDED_DATA_STDERR);
|
||||||
}
|
}
|
||||||
/* a dummy size first ...*/
|
/* a dummy size first ...*/
|
||||||
size_pos = ses.writepayload->pos;
|
size_pos = ses.writepayload->pos;
|
||||||
@ -618,7 +607,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended,
|
|||||||
len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen);
|
len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
if (len == 0 || errno != EINTR) {
|
if (len == 0 || errno != EINTR) {
|
||||||
close_read_fd(channel, fd);
|
close_chan_fd(channel, fd, SHUT_RD);
|
||||||
}
|
}
|
||||||
ses.writepayload->len = ses.writepayload->pos = 0;
|
ses.writepayload->len = ses.writepayload->pos = 0;
|
||||||
TRACE(("leave send_msg_channel_data: read err or EOF for fd %d",
|
TRACE(("leave send_msg_channel_data: read err or EOF for fd %d",
|
||||||
@ -891,6 +880,47 @@ static void send_msg_channel_open_confirmation(struct Channel* channel,
|
|||||||
TRACE(("leave send_msg_channel_open_confirmation"))
|
TRACE(("leave send_msg_channel_open_confirmation"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* close a fd, how is SHUT_RD or SHUT_WR */
|
||||||
|
static void close_chan_fd(struct Channel *channel, int fd, int how) {
|
||||||
|
|
||||||
|
int closein = 0, closeout = 0;
|
||||||
|
|
||||||
|
if (channel->type->sepfds) {
|
||||||
|
TRACE(("shutdown((%d), %d)", fd, how))
|
||||||
|
shutdown(fd, how);
|
||||||
|
if (how == 0) {
|
||||||
|
closeout = 1;
|
||||||
|
} else {
|
||||||
|
closein = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
close(fd);
|
||||||
|
closein = closeout = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeout && fd == channel->readfd) {
|
||||||
|
channel->readfd = FD_CLOSED;
|
||||||
|
}
|
||||||
|
if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
|
||||||
|
channel->errfd = FD_CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closein && fd == channel->writefd) {
|
||||||
|
channel->writefd = FD_CLOSED;
|
||||||
|
}
|
||||||
|
if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
|
||||||
|
channel->errfd = FD_CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we called shutdown on it and all references are gone, then we
|
||||||
|
* need to close() it to stop it lingering */
|
||||||
|
if (channel->type->sepfds && channel->readfd == FD_CLOSED
|
||||||
|
&& channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
|
#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
|
||||||
/* Create a new channel, and start the open request. This is intended
|
/* Create a new channel, and start the open request. This is intended
|
||||||
* for X11, agent, tcp forwarding, and should be filled with channel-specific
|
* for X11, agent, tcp forwarding, and should be filled with channel-specific
|
||||||
@ -980,61 +1010,3 @@ void recv_msg_channel_open_failure() {
|
|||||||
remove_channel(channel);
|
remove_channel(channel);
|
||||||
}
|
}
|
||||||
#endif /* USING_LISTENERS */
|
#endif /* USING_LISTENERS */
|
||||||
|
|
||||||
/* close a stdout/stderr fd */
|
|
||||||
static void close_read_fd(struct Channel * channel, int fd) {
|
|
||||||
|
|
||||||
/* don't close it if it is the same as writefd,
|
|
||||||
* unless writefd is already set -1 */
|
|
||||||
TRACE(("enter close_read_fd"))
|
|
||||||
close_chan_fd(channel, fd, 0);
|
|
||||||
TRACE(("leave close_read_fd"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close a stdin fd */
|
|
||||||
static void close_write_fd(struct Channel * channel) {
|
|
||||||
|
|
||||||
TRACE(("enter close_write_fd"))
|
|
||||||
close_chan_fd(channel, channel->writefd, 1);
|
|
||||||
TRACE(("leave close_write_fd"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close a fd, how is 0 for stdout/stderr, 1 for stdin */
|
|
||||||
static void close_chan_fd(struct Channel *channel, int fd, int how) {
|
|
||||||
|
|
||||||
int closein = 0, closeout = 0;
|
|
||||||
|
|
||||||
if (channel->type->sepfds) {
|
|
||||||
TRACE(("shutdown((%d), %d)", fd, how))
|
|
||||||
shutdown(fd, how);
|
|
||||||
if (how == 0) {
|
|
||||||
closeout = 1;
|
|
||||||
} else {
|
|
||||||
closein = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
close(fd);
|
|
||||||
closein = closeout = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closeout && fd == channel->readfd) {
|
|
||||||
channel->readfd = FD_CLOSED;
|
|
||||||
}
|
|
||||||
if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
|
|
||||||
channel->errfd = FD_CLOSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closein && fd == channel->writefd) {
|
|
||||||
channel->writefd = FD_CLOSED;
|
|
||||||
}
|
|
||||||
if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
|
|
||||||
channel->errfd = FD_CLOSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if we called shutdown on it and all references are gone, then we
|
|
||||||
* need to close() it to stop it lingering */
|
|
||||||
if (channel->type->sepfds && channel->readfd == FD_CLOSED
|
|
||||||
&& channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
2
debug.h
2
debug.h
@ -39,7 +39,7 @@
|
|||||||
* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
||||||
* since the printing may not sanitise strings etc. This will add a reasonable
|
* since the printing may not sanitise strings etc. This will add a reasonable
|
||||||
* amount to your executable size. */
|
* amount to your executable size. */
|
||||||
/*#define DEBUG_TRACE */
|
#define DEBUG_TRACE
|
||||||
|
|
||||||
/* All functions writing to the cleartext payload buffer call
|
/* All functions writing to the cleartext payload buffer call
|
||||||
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're
|
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're
|
||||||
|
Loading…
Reference in New Issue
Block a user