mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-08-08 19:02:06 +03:00
Channels: fix the "server specified invalid channel" bug
Resolved by introducing a flag entry in channel structure.
This commit is contained in:
@@ -48,6 +48,13 @@ enum ssh_channel_state_e {
|
|||||||
SSH_CHANNEL_STATE_CLOSED
|
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 {
|
struct ssh_channel_struct {
|
||||||
ssh_session session; /* SSH_SESSION pointer */
|
ssh_session session; /* SSH_SESSION pointer */
|
||||||
uint32_t local_channel;
|
uint32_t local_channel;
|
||||||
@@ -61,6 +68,7 @@ struct ssh_channel_struct {
|
|||||||
uint32_t remote_maxpacket;
|
uint32_t remote_maxpacket;
|
||||||
enum ssh_channel_state_e state;
|
enum ssh_channel_state_e state;
|
||||||
int delayed_close;
|
int delayed_close;
|
||||||
|
int flags;
|
||||||
ssh_buffer stdout_buffer;
|
ssh_buffer stdout_buffer;
|
||||||
ssh_buffer stderr_buffer;
|
ssh_buffer stderr_buffer;
|
||||||
void *userarg;
|
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);
|
ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id);
|
||||||
int channel_write_common(ssh_channel channel, const void *data,
|
int channel_write_common(ssh_channel channel, const void *data,
|
||||||
uint32_t len, int is_stderr);
|
uint32_t len, int is_stderr);
|
||||||
|
void ssh_channel_do_free(ssh_channel channel);
|
||||||
#ifdef WITH_SSH1
|
#ifdef WITH_SSH1
|
||||||
SSH_PACKET_CALLBACK(ssh_packet_data1);
|
SSH_PACKET_CALLBACK(ssh_packet_data1);
|
||||||
SSH_PACKET_CALLBACK(ssh_packet_close1);
|
SSH_PACKET_CALLBACK(ssh_packet_close1);
|
||||||
|
@@ -107,6 +107,7 @@ ssh_channel ssh_channel_new(ssh_session session) {
|
|||||||
channel->session = session;
|
channel->session = session;
|
||||||
channel->version = session->version;
|
channel->version = session->version;
|
||||||
channel->exit_status = -1;
|
channel->exit_status = -1;
|
||||||
|
channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND;
|
||||||
|
|
||||||
if(session->channels == NULL) {
|
if(session->channels == NULL) {
|
||||||
session->channels = ssh_list_new();
|
session->channels = ssh_list_new();
|
||||||
@@ -175,6 +176,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
|
|||||||
(long unsigned int) channel->remote_maxpacket);
|
(long unsigned int) channel->remote_maxpacket);
|
||||||
|
|
||||||
channel->state = SSH_CHANNEL_STATE_OPEN;
|
channel->state = SSH_CHANNEL_STATE_OPEN;
|
||||||
|
channel->flags = channel->flags & ~SSH_CHANNEL_FLAG_NOT_BOUND;
|
||||||
leave_function();
|
leave_function();
|
||||||
return SSH_PACKET_USED;
|
return SSH_PACKET_USED;
|
||||||
}
|
}
|
||||||
@@ -620,7 +622,6 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
|
|||||||
} else {
|
} else {
|
||||||
channel->state = SSH_CHANNEL_STATE_CLOSED;
|
channel->state = SSH_CHANNEL_STATE_CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel->remote_eof == 0) {
|
if (channel->remote_eof == 0) {
|
||||||
ssh_log(session, SSH_LOG_PACKET,
|
ssh_log(session, SSH_LOG_PACKET,
|
||||||
"Remote host not polite enough to send an eof before close");
|
"Remote host not polite enough to send an eof before close");
|
||||||
@@ -636,7 +637,9 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
|
|||||||
channel,
|
channel,
|
||||||
channel->callbacks->userdata);
|
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();
|
leave_function();
|
||||||
return SSH_PACKET_USED;
|
return SSH_PACKET_USED;
|
||||||
}
|
}
|
||||||
@@ -1019,7 +1022,6 @@ error:
|
|||||||
*/
|
*/
|
||||||
void ssh_channel_free(ssh_channel channel) {
|
void ssh_channel_free(ssh_channel channel) {
|
||||||
ssh_session session;
|
ssh_session session;
|
||||||
struct ssh_iterator *it;
|
|
||||||
|
|
||||||
if (channel == NULL) {
|
if (channel == NULL) {
|
||||||
return;
|
return;
|
||||||
@@ -1031,7 +1033,28 @@ void ssh_channel_free(ssh_channel channel) {
|
|||||||
if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) {
|
if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) {
|
||||||
ssh_channel_close(channel);
|
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);
|
it = ssh_list_find(session->channels, channel);
|
||||||
if(it != NULL){
|
if(it != NULL){
|
||||||
ssh_list_remove(session->channels, it);
|
ssh_list_remove(session->channels, it);
|
||||||
@@ -1042,8 +1065,6 @@ void ssh_channel_free(ssh_channel channel) {
|
|||||||
/* debug trick to catch use after frees */
|
/* debug trick to catch use after frees */
|
||||||
memset(channel, 'X', sizeof(struct ssh_channel_struct));
|
memset(channel, 'X', sizeof(struct ssh_channel_struct));
|
||||||
SAFE_FREE(channel);
|
SAFE_FREE(channel);
|
||||||
|
|
||||||
leave_function();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -189,7 +189,7 @@ void ssh_free(ssh_session session) {
|
|||||||
}
|
}
|
||||||
/* delete all channels */
|
/* delete all channels */
|
||||||
while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
|
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_remove(session->channels, it);
|
||||||
}
|
}
|
||||||
ssh_list_free(session->channels);
|
ssh_list_free(session->channels);
|
||||||
|
Reference in New Issue
Block a user