aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2011-08-28 14:27:24 +0300
committerAris Adamantiadis <aris@0xbadc0de.be>2011-09-02 13:50:06 +0300
commitef5701a5357e3f5b71aa5c387e4f976fe5df0ab7 (patch)
treef8a6b0418e739b3aefc2e880692589fca4cd28e6
parent7d347aa6f42c1698629f89e4ae7155348a6df8d4 (diff)
downloadlibssh-ef5701a5357e3f5b71aa5c387e4f976fe5df0ab7.tar.gz
libssh-ef5701a5357e3f5b71aa5c387e4f976fe5df0ab7.tar.xz
libssh-ef5701a5357e3f5b71aa5c387e4f976fe5df0ab7.zip
Channels: fix the "server specified invalid channel" bug
Resolved by introducing a flag entry in channel structure.
-rw-r--r--include/libssh/channels.h9
-rw-r--r--src/channels.c41
-rw-r--r--src/session.c2
3 files changed, 41 insertions, 11 deletions
diff --git a/include/libssh/channels.h b/include/libssh/channels.h
index 624a3adf..87415e5f 100644
--- a/include/libssh/channels.h
+++ b/include/libssh/channels.h
@@ -48,6 +48,13 @@ enum ssh_channel_state_e {
SSH_CHANNEL_STATE_CLOSED
};
+/* The channel has been closed by the remote side */
+#define SSH_CHANNEL_FLAG_CLOSED_REMOTE 0x1
+/* The channel has been freed by the calling program */
+#define SSH_CHANNEL_FLAG_FREED_LOCAL 0x2
+/* the channel has not yet been bound to a remote one */
+#define SSH_CHANNEL_FLAG_NOT_BOUND 0x4
+
struct ssh_channel_struct {
ssh_session session; /* SSH_SESSION pointer */
uint32_t local_channel;
@@ -61,6 +68,7 @@ struct ssh_channel_struct {
uint32_t remote_maxpacket;
enum ssh_channel_state_e state;
int delayed_close;
+ int flags;
ssh_buffer stdout_buffer;
ssh_buffer stderr_buffer;
void *userarg;
@@ -90,6 +98,7 @@ uint32_t ssh_channel_new_id(ssh_session session);
ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id);
int channel_write_common(ssh_channel channel, const void *data,
uint32_t len, int is_stderr);
+void ssh_channel_do_free(ssh_channel channel);
#ifdef WITH_SSH1
SSH_PACKET_CALLBACK(ssh_packet_data1);
SSH_PACKET_CALLBACK(ssh_packet_close1);
diff --git a/src/channels.c b/src/channels.c
index a48d6d3f..6dde48b4 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -107,6 +107,7 @@ ssh_channel ssh_channel_new(ssh_session session) {
channel->session = session;
channel->version = session->version;
channel->exit_status = -1;
+ channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND;
if(session->channels == NULL) {
session->channels = ssh_list_new();
@@ -175,6 +176,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
(long unsigned int) channel->remote_maxpacket);
channel->state = SSH_CHANNEL_STATE_OPEN;
+ channel->flags = channel->flags & ~SSH_CHANNEL_FLAG_NOT_BOUND;
leave_function();
return SSH_PACKET_USED;
}
@@ -620,7 +622,6 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
} else {
channel->state = SSH_CHANNEL_STATE_CLOSED;
}
-
if (channel->remote_eof == 0) {
ssh_log(session, SSH_LOG_PACKET,
"Remote host not polite enough to send an eof before close");
@@ -631,12 +632,14 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
* buffer because the eof is ignored until the buffer is empty.
*/
- if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) {
- channel->callbacks->channel_close_function(channel->session,
- channel,
- channel->callbacks->userdata);
- }
-
+ if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) {
+ channel->callbacks->channel_close_function(channel->session,
+ channel,
+ channel->callbacks->userdata);
+ }
+ channel->flags &= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
+ if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
+ ssh_channel_do_free(channel);
leave_function();
return SSH_PACKET_USED;
}
@@ -1019,7 +1022,6 @@ error:
*/
void ssh_channel_free(ssh_channel channel) {
ssh_session session;
- struct ssh_iterator *it;
if (channel == NULL) {
return;
@@ -1031,7 +1033,28 @@ void ssh_channel_free(ssh_channel channel) {
if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) {
ssh_channel_close(channel);
}
+ channel->flags &= SSH_CHANNEL_FLAG_FREED_LOCAL;
+
+ /* The idea behind the flags is the following : it is well possible
+ * that a client closes a channel that stills exists on the server side.
+ * We definitively close the channel when we receive a close message *and*
+ * the user closed it.
+ */
+ if((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE)
+ || (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)){
+ ssh_channel_do_free(channel);
+ }
+ leave_function();
+}
+
+/**
+ * @internal
+ * @brief Effectively free a channel, without caring about flags
+ */
+void ssh_channel_do_free(ssh_channel channel){
+ struct ssh_iterator *it;
+ ssh_session session = channel->session;
it = ssh_list_find(session->channels, channel);
if(it != NULL){
ssh_list_remove(session->channels, it);
@@ -1042,8 +1065,6 @@ void ssh_channel_free(ssh_channel channel) {
/* debug trick to catch use after frees */
memset(channel, 'X', sizeof(struct ssh_channel_struct));
SAFE_FREE(channel);
-
- leave_function();
}
/**
diff --git a/src/session.c b/src/session.c
index 94c28673..751aa537 100644
--- a/src/session.c
+++ b/src/session.c
@@ -189,7 +189,7 @@ void ssh_free(ssh_session session) {
}
/* delete all channels */
while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
- ssh_channel_free(ssh_iterator_value(ssh_channel,it));
+ ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
ssh_list_remove(session->channels, it);
}
ssh_list_free(session->channels);