channels.c | channels.c | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
* License for more details. | * License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Lesser General Public License | * You should have received a copy of the GNU Lesser General Public License | |||
* along with the SSH Library; see the file COPYING. If not, write to | * along with the SSH Library; see the file COPYING. If not, write to | |||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |||
* MA 02111-1307, USA. | * MA 02111-1307, USA. | |||
*/ | */ | |||
#include <string.h> | #include <string.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <unistd.h> | ||||
#include <stdio.h> | #include <stdio.h> | |||
#include <errno.h> | #include <errno.h> | |||
#include <time.h> | ||||
#ifndef _WIN32 | #ifndef _WIN32 | |||
#include <arpa/inet.h> | #include <arpa/inet.h> | |||
#endif | #endif | |||
#include "libssh/priv.h" | #include "libssh/priv.h" | |||
#include "libssh/ssh2.h" | #include "libssh/ssh2.h" | |||
#include "libssh/buffer.h" | ||||
#include "libssh/packet.h" | ||||
#include "libssh/socket.h" | ||||
#include "libssh/channels.h" | ||||
#include "libssh/session.h" | ||||
#include "libssh/misc.h" | ||||
#include "libssh/messages.h" | ||||
#define WINDOWBASE 128000 | #define WINDOWBASE 128000 | |||
#define WINDOWLIMIT (WINDOWBASE/2) | #define WINDOWLIMIT (WINDOWBASE/2) | |||
/** | /** | |||
* @defgroup ssh_channel SSH Channels | * @defgroup ssh_channel SSH Channels | |||
* @brief Functions that manage a channel. | * @brief Functions that manage a channel. | |||
*/ | */ | |||
/** | /** | |||
skipping to change at line 58 | skipping to change at line 65 | |||
* @{ | * @{ | |||
*/ | */ | |||
/** | /** | |||
* @brief Allocate a new channel. | * @brief Allocate a new channel. | |||
* | * | |||
* @param session The ssh session to use. | * @param session The ssh session to use. | |||
* | * | |||
* @return A pointer to a newly allocated channel, NULL on error. | * @return A pointer to a newly allocated channel, NULL on error. | |||
*/ | */ | |||
CHANNEL *channel_new(SSH_SESSION *session) { | ssh_channel channel_new(ssh_session session) { | |||
CHANNEL *channel = NULL; | ssh_channel channel = NULL; | |||
channel = malloc(sizeof(CHANNEL)); | channel = malloc(sizeof(struct ssh_channel_struct)); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
return NULL; | return NULL; | |||
} | } | |||
memset(channel,0,sizeof(CHANNEL)); | memset(channel,0,sizeof(struct ssh_channel_struct)); | |||
channel->stdout_buffer = buffer_new(); | channel->stdout_buffer = buffer_new(); | |||
if (channel->stdout_buffer == NULL) { | if (channel->stdout_buffer == NULL) { | |||
SAFE_FREE(channel); | SAFE_FREE(channel); | |||
return NULL; | return NULL; | |||
} | } | |||
channel->stderr_buffer = buffer_new(); | channel->stderr_buffer = buffer_new(); | |||
if (channel->stderr_buffer == NULL) { | if (channel->stderr_buffer == NULL) { | |||
buffer_free(channel->stdout_buffer); | ||||
SAFE_FREE(channel); | SAFE_FREE(channel); | |||
return NULL; | return NULL; | |||
} | } | |||
channel->session = session; | channel->session = session; | |||
channel->version = session->version; | channel->version = session->version; | |||
channel->exit_status = -1; | channel->exit_status = -1; | |||
if(session->channels == NULL) { | if(session->channels == NULL) { | |||
session->channels = channel; | session->channels = channel; | |||
skipping to change at line 105 | skipping to change at line 113 | |||
/** | /** | |||
* @internal | * @internal | |||
* | * | |||
* @brief Create a new channel identifier. | * @brief Create a new channel identifier. | |||
* | * | |||
* @param session The SSH session to use. | * @param session The SSH session to use. | |||
* | * | |||
* @return The new channel identifier. | * @return The new channel identifier. | |||
*/ | */ | |||
u32 ssh_channel_new_id(SSH_SESSION *session) { | uint32_t ssh_channel_new_id(ssh_session session) { | |||
return ++(session->maxchannel); | return ++(session->maxchannel); | |||
} | } | |||
static int channel_open(CHANNEL *channel, const char *type_c, int window, | static int channel_open(ssh_channel channel, const char *type_c, int window | |||
int maxpacket, BUFFER *payload) { | , | |||
SSH_SESSION *session = channel->session; | int maxpacket, ssh_buffer payload) { | |||
STRING *type = NULL; | ssh_session session = channel->session; | |||
u32 tmp = 0; | ssh_string type = NULL; | |||
uint32_t tmp = 0; | ||||
enter_function(); | enter_function(); | |||
channel->local_channel = ssh_channel_new_id(session); | channel->local_channel = ssh_channel_new_id(session); | |||
channel->local_maxpacket = maxpacket; | channel->local_maxpacket = maxpacket; | |||
channel->local_window = window; | channel->local_window = window; | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"Creating a channel %d with %d window and %d max packet", | "Creating a channel %d with %d window and %d max packet", | |||
channel->local_channel, window, maxpacket); | channel->local_channel, window, maxpacket); | |||
skipping to change at line 199 | skipping to change at line 207 | |||
ssh_log(session, SSH_LOG_PROTOCOL, | ssh_log(session, SSH_LOG_PROTOCOL, | |||
"Remote window : %lu, maxpacket : %lu", | "Remote window : %lu, maxpacket : %lu", | |||
(long unsigned int) channel->remote_window, | (long unsigned int) channel->remote_window, | |||
(long unsigned int) channel->remote_maxpacket); | (long unsigned int) channel->remote_maxpacket); | |||
channel->open = 1; | channel->open = 1; | |||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
case SSH2_MSG_CHANNEL_OPEN_FAILURE: | case SSH2_MSG_CHANNEL_OPEN_FAILURE: | |||
{ | { | |||
STRING *error_s; | ssh_string error_s; | |||
char *error; | char *error; | |||
u32 code; | uint32_t code; | |||
buffer_get_u32(session->in_buffer, &tmp); | buffer_get_u32(session->in_buffer, &tmp); | |||
buffer_get_u32(session->in_buffer, &code); | buffer_get_u32(session->in_buffer, &code); | |||
error_s = buffer_get_ssh_string(session->in_buffer); | error_s = buffer_get_ssh_string(session->in_buffer); | |||
error = string_to_char(error_s); | error = string_to_char(error_s); | |||
string_free(error_s); | string_free(error_s); | |||
if (error == NULL) { | if (error == NULL) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
skipping to change at line 236 | skipping to change at line 244 | |||
"Received unknown packet %d\n", session->in_packet.type); | "Received unknown packet %d\n", session->in_packet.type); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
/* get ssh channel from local session? */ | /* get ssh channel from local session? */ | |||
CHANNEL *ssh_channel_from_local(SSH_SESSION *session, u32 id) { | ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { | |||
CHANNEL *initchan = session->channels; | ssh_channel initchan = session->channels; | |||
CHANNEL *channel; | ssh_channel channel; | |||
/* We assume we are always the local */ | /* We assume we are always the local */ | |||
if (initchan == NULL) { | if (initchan == NULL) { | |||
return NULL; | return NULL; | |||
} | } | |||
for (channel = initchan; channel->local_channel != id; | for (channel = initchan; channel->local_channel != id; | |||
channel=channel->next) { | channel=channel->next) { | |||
if (channel->next == initchan) { | if (channel->next == initchan) { | |||
return NULL; | return NULL; | |||
} | } | |||
} | } | |||
return channel; | return channel; | |||
} | } | |||
static int grow_window(SSH_SESSION *session, CHANNEL *channel, int minimums | static int grow_window(ssh_session session, ssh_channel channel, int minimu | |||
ize) { | msize) { | |||
u32 new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; | uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE | |||
; | ||||
enter_function(); | enter_function(); | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 || | if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 || | |||
buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || | buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || | |||
buffer_add_u32(session->out_buffer, htonl(new_window)) < 0) { | buffer_add_u32(session->out_buffer, htonl(new_window)) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
if (packet_send(session) != SSH_OK) { | if (packet_send(session) != SSH_OK) { | |||
skipping to change at line 289 | skipping to change at line 297 | |||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
error: | error: | |||
buffer_reinit(session->out_buffer); | buffer_reinit(session->out_buffer); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
static CHANNEL *channel_from_msg(SSH_SESSION *session) { | static ssh_channel channel_from_msg(ssh_session session) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
u32 chan; | uint32_t chan; | |||
if (buffer_get_u32(session->in_buffer, &chan) != sizeof(u32)) { | if (buffer_get_u32(session->in_buffer, &chan) != sizeof(uint32_t)) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Getting channel from message: short read"); | "Getting channel from message: short read"); | |||
return NULL; | return NULL; | |||
} | } | |||
channel = ssh_channel_from_local(session, ntohl(chan)); | channel = ssh_channel_from_local(session, ntohl(chan)); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Server specified invalid channel %lu", | "Server specified invalid channel %lu", | |||
(long unsigned int) ntohl(chan)); | (long unsigned int) ntohl(chan)); | |||
} | } | |||
return channel; | return channel; | |||
} | } | |||
static void channel_rcv_change_window(SSH_SESSION *session) { | static void channel_rcv_change_window(ssh_session session) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
u32 bytes; | uint32_t bytes; | |||
int rc; | int rc; | |||
enter_function(); | enter_function(); | |||
channel = channel_from_msg(session); | channel = channel_from_msg(session); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | |||
} | } | |||
rc = buffer_get_u32(session->in_buffer, &bytes); | rc = buffer_get_u32(session->in_buffer, &bytes); | |||
if (channel == NULL || rc != sizeof(u32)) { | if (channel == NULL || rc != sizeof(uint32_t)) { | |||
ssh_log(session, SSH_LOG_PACKET, | ssh_log(session, SSH_LOG_PACKET, | |||
"Error getting a window adjust message: invalid packet"); | "Error getting a window adjust message: invalid packet"); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
bytes = ntohl(bytes); | bytes = ntohl(bytes); | |||
ssh_log(session, SSH_LOG_PROTOCOL, | ssh_log(session, SSH_LOG_PROTOCOL, | |||
"Adding %d bytes to channel (%d:%d) (from %d bytes)", | "Adding %d bytes to channel (%d:%d) (from %d bytes)", | |||
bytes, | bytes, | |||
channel->local_channel, | channel->local_channel, | |||
channel->remote_channel, | channel->remote_channel, | |||
channel->remote_window); | channel->remote_window); | |||
channel->remote_window += bytes; | channel->remote_window += bytes; | |||
leave_function(); | leave_function(); | |||
} | } | |||
/* is_stderr is set to 1 if the data are extended, ie stderr */ | /* is_stderr is set to 1 if the data are extended, ie stderr */ | |||
static void channel_rcv_data(SSH_SESSION *session,int is_stderr) { | static void channel_rcv_data(ssh_session session,int is_stderr) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
STRING *str; | ssh_string str; | |||
size_t len; | size_t len; | |||
enter_function(); | enter_function(); | |||
channel = channel_from_msg(session); | channel = channel_from_msg(session); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_log(session, SSH_LOG_FUNCTIONS, | ssh_log(session, SSH_LOG_FUNCTIONS, | |||
"%s", ssh_get_error(session)); | "%s", ssh_get_error(session)); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
if (is_stderr) { | if (is_stderr) { | |||
u32 ignore; | uint32_t ignore; | |||
/* uint32 data type code. we can ignore it */ | /* uint32 data type code. we can ignore it */ | |||
buffer_get_u32(session->in_buffer, &ignore); | buffer_get_u32(session->in_buffer, &ignore); | |||
} | } | |||
str = buffer_get_ssh_string(session->in_buffer); | str = buffer_get_ssh_string(session->in_buffer); | |||
if (str == NULL) { | if (str == NULL) { | |||
ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!"); | ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!"); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
skipping to change at line 387 | skipping to change at line 395 | |||
channel->remote_window); | channel->remote_window); | |||
/* What shall we do in this case? Let's accept it anyway */ | /* What shall we do in this case? Let's accept it anyway */ | |||
if (len > channel->local_window) { | if (len > channel->local_window) { | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"Data packet too big for our window(%zu vs %d)", | "Data packet too big for our window(%zu vs %d)", | |||
len, | len, | |||
channel->local_window); | channel->local_window); | |||
} | } | |||
if (channel_default_bufferize(channel, str->string, len, | if (channel_default_bufferize(channel, string_data(str), len, | |||
is_stderr) < 0) { | is_stderr) < 0) { | |||
string_free(str); | string_free(str); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
if (len <= channel->local_window) { | if (len <= channel->local_window) { | |||
channel->local_window -= len; | channel->local_window -= len; | |||
} else { | } else { | |||
channel->local_window = 0; /* buggy remote */ | channel->local_window = 0; /* buggy remote */ | |||
skipping to change at line 409 | skipping to change at line 417 | |||
ssh_log(session, SSH_LOG_PROTOCOL, | ssh_log(session, SSH_LOG_PROTOCOL, | |||
"Channel windows are now (local win=%d remote win=%d)", | "Channel windows are now (local win=%d remote win=%d)", | |||
channel->local_window, | channel->local_window, | |||
channel->remote_window); | channel->remote_window); | |||
string_free(str); | string_free(str); | |||
leave_function(); | leave_function(); | |||
} | } | |||
static void channel_rcv_eof(SSH_SESSION *session) { | static void channel_rcv_eof(ssh_session session) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
enter_function(); | enter_function(); | |||
channel = channel_from_msg(session); | channel = channel_from_msg(session); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
ssh_log(session, SSH_LOG_PACKET, | ssh_log(session, SSH_LOG_PACKET, | |||
"Received eof on channel (%d:%d)", | "Received eof on channel (%d:%d)", | |||
channel->local_channel, | channel->local_channel, | |||
channel->remote_channel); | channel->remote_channel); | |||
/* channel->remote_window = 0; */ | /* channel->remote_window = 0; */ | |||
channel->remote_eof = 1; | channel->remote_eof = 1; | |||
leave_function(); | leave_function(); | |||
} | } | |||
static void channel_rcv_close(SSH_SESSION *session) { | static void channel_rcv_close(ssh_session session) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
enter_function(); | enter_function(); | |||
channel = channel_from_msg(session); | channel = channel_from_msg(session); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
skipping to change at line 470 | skipping to change at line 478 | |||
} | } | |||
channel->remote_eof = 1; | channel->remote_eof = 1; | |||
/* | /* | |||
* The remote eof doesn't break things if there was still data into read | * The remote eof doesn't break things if there was still data into read | |||
* buffer because the eof is ignored until the buffer is empty. | * buffer because the eof is ignored until the buffer is empty. | |||
*/ | */ | |||
leave_function(); | leave_function(); | |||
} | } | |||
static void channel_rcv_request(SSH_SESSION *session) { | static void channel_rcv_request(ssh_session session) { | |||
CHANNEL *channel; | ssh_channel channel; | |||
STRING *request_s; | ssh_string request_s; | |||
char *request; | char *request; | |||
u32 status; | uint32_t status; | |||
uint32_t startpos = session->in_buffer->pos; | ||||
enter_function(); | enter_function(); | |||
channel = channel_from_msg(session); | channel = channel_from_msg(session); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); | ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
skipping to change at line 499 | skipping to change at line 508 | |||
return; | return; | |||
} | } | |||
request = string_to_char(request_s); | request = string_to_char(request_s); | |||
string_free(request_s); | string_free(request_s); | |||
if (request == NULL) { | if (request == NULL) { | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
buffer_get_u8(session->in_buffer, (u8 *) &status); | buffer_get_u8(session->in_buffer, (uint8_t *) &status); | |||
if (strcmp(request,"exit-status") == 0) { | if (strcmp(request,"exit-status") == 0) { | |||
SAFE_FREE(request); | SAFE_FREE(request); | |||
ssh_log(session, SSH_LOG_PACKET, "received exit-status"); | ssh_log(session, SSH_LOG_PACKET, "received exit-status"); | |||
buffer_get_u32(session->in_buffer, &status); | buffer_get_u32(session->in_buffer, &status); | |||
channel->exit_status = ntohl(status); | channel->exit_status = ntohl(status); | |||
leave_function(); | leave_function(); | |||
return ; | return ; | |||
} | } | |||
if (strcmp(request, "exit-signal") == 0) { | if (strcmp(request, "exit-signal") == 0) { | |||
const char *core = "(core dumped)"; | const char *core = "(core dumped)"; | |||
STRING *signal_s; | ssh_string signal_s; | |||
char *signal; | char *signal; | |||
u8 i; | uint8_t i; | |||
SAFE_FREE(request); | SAFE_FREE(request); | |||
signal_s = buffer_get_ssh_string(session->in_buffer); | signal_s = buffer_get_ssh_string(session->in_buffer); | |||
if (signal_s == NULL) { | if (signal_s == NULL) { | |||
ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); | ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
skipping to change at line 545 | skipping to change at line 554 | |||
core = ""; | core = ""; | |||
} | } | |||
ssh_log(session, SSH_LOG_PACKET, | ssh_log(session, SSH_LOG_PACKET, | |||
"Remote connection closed by signal SIG %s %s", signal, core); | "Remote connection closed by signal SIG %s %s", signal, core); | |||
SAFE_FREE(signal); | SAFE_FREE(signal); | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
/* TODO call message_handle since it handles channel requests as messages | ||||
*/ | ||||
/* *but* reset buffer before !! */ | ||||
session->in_buffer->pos = startpos; | ||||
message_handle(session, SSH2_MSG_CHANNEL_REQUEST); | ||||
ssh_log(session, SSH_LOG_PACKET, "Unknown request %s", request); | ssh_log(session, SSH_LOG_PACKET, "Unknown request %s", request); | |||
SAFE_FREE(request); | SAFE_FREE(request); | |||
leave_function(); | leave_function(); | |||
} | } | |||
/* | /* | |||
* channel_handle() is called by packet_wait(), for example when there is | * channel_handle() is called by packet_wait(), for example when there is | |||
* channel informations to handle. | * channel informations to handle. | |||
*/ | */ | |||
void channel_handle(SSH_SESSION *session, int type){ | void channel_handle(ssh_session session, int type){ | |||
enter_function(); | enter_function(); | |||
ssh_log(session, SSH_LOG_PROTOCOL, "Channel_handle(%d)", type); | ssh_log(session, SSH_LOG_PROTOCOL, "Channel_handle(%d)", type); | |||
switch(type) { | switch(type) { | |||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST: | case SSH2_MSG_CHANNEL_WINDOW_ADJUST: | |||
channel_rcv_change_window(session); | channel_rcv_change_window(session); | |||
break; | break; | |||
case SSH2_MSG_CHANNEL_DATA: | case SSH2_MSG_CHANNEL_DATA: | |||
channel_rcv_data(session,0); | channel_rcv_data(session,0); | |||
skipping to change at line 593 | skipping to change at line 609 | |||
leave_function(); | leave_function(); | |||
} | } | |||
/* | /* | |||
* When data has been received from the ssh server, it can be applied to th e | * When data has been received from the ssh server, it can be applied to th e | |||
* known user function, with help of the callback, or inserted here | * known user function, with help of the callback, or inserted here | |||
* | * | |||
* FIXME is the window changed? | * FIXME is the window changed? | |||
*/ | */ | |||
int channel_default_bufferize(CHANNEL *channel, void *data, int len, | int channel_default_bufferize(ssh_channel channel, void *data, int len, | |||
int is_stderr) { | int is_stderr) { | |||
struct ssh_session *session = channel->session; | ssh_session session = channel->session; | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); | "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); | |||
if (is_stderr == 0) { | if (is_stderr == 0) { | |||
/* stdout */ | /* stdout */ | |||
if (channel->stdout_buffer == NULL) { | if (channel->stdout_buffer == NULL) { | |||
channel->stdout_buffer = buffer_new(); | channel->stdout_buffer = buffer_new(); | |||
if (channel->stdout_buffer == NULL) { | if (channel->stdout_buffer == NULL) { | |||
ssh_set_error_oom(session); | ||||
return -1; | return -1; | |||
} | } | |||
} | } | |||
if (buffer_add_data(channel->stdout_buffer, data, len) < 0) { | if (buffer_add_data(channel->stdout_buffer, data, len) < 0) { | |||
buffer_free(channel->stdout_buffer); | buffer_free(channel->stdout_buffer); | |||
channel->stdout_buffer = NULL; | ||||
return -1; | return -1; | |||
} | } | |||
} else { | } else { | |||
/* stderr */ | /* stderr */ | |||
if (channel->stderr_buffer == NULL) { | if (channel->stderr_buffer == NULL) { | |||
channel->stderr_buffer = buffer_new(); | channel->stderr_buffer = buffer_new(); | |||
if (channel->stderr_buffer == NULL) { | if (channel->stderr_buffer == NULL) { | |||
ssh_set_error_oom(session); | ||||
return -1; | return -1; | |||
} | } | |||
} | } | |||
if (buffer_add_data(channel->stderr_buffer, data, len) < 0) { | if (buffer_add_data(channel->stderr_buffer, data, len) < 0) { | |||
buffer_free(channel->stderr_buffer); | buffer_free(channel->stderr_buffer); | |||
channel->stderr_buffer = NULL; | ||||
return -1; | return -1; | |||
} | } | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
/** | /** | |||
* @brief Open a session channel (suited for a shell, not TCP forwarding). | * @brief Open a session channel (suited for a shell, not TCP forwarding). | |||
* | * | |||
* @param channel An allocated channel. | * @param channel An allocated channel. | |||
* | * | |||
* @return SSH_OK on success\n | * @return SSH_OK on success\n | |||
* SSH_ERROR on error. | * SSH_ERROR on error. | |||
* | * | |||
* @see channel_open_forward() | * @see channel_open_forward() | |||
* @see channel_request_env() | * @see channel_request_env() | |||
* @see channel_request_shell() | * @see channel_request_shell() | |||
* @see channel_request_exec() | * @see channel_request_exec() | |||
*/ | */ | |||
int channel_open_session(CHANNEL *channel) { | int channel_open_session(ssh_channel channel) { | |||
#ifdef WITH_SSH1 | #ifdef WITH_SSH1 | |||
if (channel->session->version == 1) { | if (channel->session->version == 1) { | |||
return channel_open_session1(channel); | return channel_open_session1(channel); | |||
} | } | |||
#endif | #endif | |||
return channel_open(channel,"session",64000,32000,NULL); | return channel_open(channel,"session",64000,32000,NULL); | |||
} | } | |||
/** | /** | |||
skipping to change at line 667 | skipping to change at line 687 | |||
* | * | |||
* @param remotehost The remote host to connected (host name or IP). | * @param remotehost The remote host to connected (host name or IP). | |||
* | * | |||
* @param remoteport The remote port. | * @param remoteport The remote port. | |||
* | * | |||
* @param sourcehost The source host (your local computer). It's faculta tive | * @param sourcehost The source host (your local computer). It's faculta tive | |||
* and for logging purpose. | * and for logging purpose. | |||
* | * | |||
* @param localport The source port (your local computer). It's faculta tive | * @param localport The source port (your local computer). It's faculta tive | |||
* and for logging purpose. | * and for logging purpose. | |||
* @warning This function does not bind the local port and does not automat | ||||
ically | ||||
* forward the content of a socket to the channel. You still have | ||||
to | ||||
* use channel_read and channel_write for this. | ||||
* | * | |||
* @return SSH_OK on success\n | * @return SSH_OK on success\n | |||
* SSH_ERROR on error | * SSH_ERROR on error | |||
*/ | */ | |||
int channel_open_forward(CHANNEL *channel, const char *remotehost, | int channel_open_forward(ssh_channel channel, const char *remotehost, | |||
int remoteport, const char *sourcehost, int localport) { | int remoteport, const char *sourcehost, int localport) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
BUFFER *payload = NULL; | ssh_buffer payload = NULL; | |||
STRING *str = NULL; | ssh_string str = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
payload = buffer_new(); | payload = buffer_new(); | |||
if (payload == NULL) { | if (payload == NULL) { | |||
goto error; | goto error; | |||
} | } | |||
str = string_from_char(remotehost); | str = string_from_char(remotehost); | |||
if (str == NULL) { | if (str == NULL) { | |||
skipping to change at line 722 | skipping to change at line 745 | |||
return rc; | return rc; | |||
} | } | |||
/** | /** | |||
* @brief Close and free a channel. | * @brief Close and free a channel. | |||
* | * | |||
* @param channel The channel to free. | * @param channel The channel to free. | |||
* | * | |||
* @warning Any data unread on this channel will be lost. | * @warning Any data unread on this channel will be lost. | |||
*/ | */ | |||
void channel_free(CHANNEL *channel) { | void channel_free(ssh_channel channel) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
enter_function(); | enter_function(); | |||
if (channel == NULL) { | if (channel == NULL) { | |||
leave_function(); | leave_function(); | |||
return; | return; | |||
} | } | |||
if (session->alive && channel->open) { | if (session->alive && channel->open) { | |||
channel_close(channel); | channel_close(channel); | |||
} | } | |||
skipping to change at line 752 | skipping to change at line 775 | |||
session->channels = NULL; | session->channels = NULL; | |||
} else { | } else { | |||
channel->prev->next = channel->next; | channel->prev->next = channel->next; | |||
channel->next->prev = channel->prev; | channel->next->prev = channel->prev; | |||
} | } | |||
buffer_free(channel->stdout_buffer); | buffer_free(channel->stdout_buffer); | |||
buffer_free(channel->stderr_buffer); | buffer_free(channel->stderr_buffer); | |||
/* debug trick to catch use after frees */ | /* debug trick to catch use after frees */ | |||
memset(channel, 'X', sizeof(CHANNEL)); | memset(channel, 'X', sizeof(struct ssh_channel_struct)); | |||
SAFE_FREE(channel); | SAFE_FREE(channel); | |||
leave_function(); | leave_function(); | |||
} | } | |||
/** | /** | |||
* @brief Send an end of file on the channel. | * @brief Send an end of file on the channel. | |||
* | * | |||
* This doesn't close the channel. You may still read from it but not write . | * This doesn't close the channel. You may still read from it but not write . | |||
* | * | |||
* @param channel The channel to send the eof to. | * @param channel The channel to send the eof to. | |||
* | * | |||
* @return SSH_SUCCESS on success\n | * @return SSH_SUCCESS on success\n | |||
* SSH_ERROR on error\n | * SSH_ERROR on error\n | |||
* | * | |||
* @see channel_close() | * @see channel_close() | |||
* @see channel_free() | * @see channel_free() | |||
*/ | */ | |||
int channel_send_eof(CHANNEL *channel){ | int channel_send_eof(ssh_channel channel){ | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_EOF) < 0) { | if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_EOF) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
if (buffer_add_u32(session->out_buffer,htonl(channel->remote_channel)) < 0) { | if (buffer_add_u32(session->out_buffer,htonl(channel->remote_channel)) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
skipping to change at line 814 | skipping to change at line 837 | |||
* to recover any data the server was going to send or was in buffers. | * to recover any data the server was going to send or was in buffers. | |||
* | * | |||
* @param channel The channel to close. | * @param channel The channel to close. | |||
* | * | |||
* @return SSH_SUCCESS on success\n | * @return SSH_SUCCESS on success\n | |||
* SSH_ERROR on error | * SSH_ERROR on error | |||
* | * | |||
* @see channel_free() | * @see channel_free() | |||
* @see channel_eof() | * @see channel_eof() | |||
*/ | */ | |||
int channel_close(CHANNEL *channel){ | int channel_close(ssh_channel channel){ | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
int rc = 0; | int rc = 0; | |||
enter_function(); | enter_function(); | |||
if (channel->local_eof == 0) { | if (channel->local_eof == 0) { | |||
rc = channel_send_eof(channel); | rc = channel_send_eof(channel); | |||
} | } | |||
if (rc != SSH_OK) { | if (rc != SSH_OK) { | |||
leave_function(); | leave_function(); | |||
skipping to change at line 853 | skipping to change at line 876 | |||
leave_function(); | leave_function(); | |||
return rc; | return rc; | |||
error: | error: | |||
buffer_reinit(session->out_buffer); | buffer_reinit(session->out_buffer); | |||
leave_function(); | leave_function(); | |||
return rc; | return rc; | |||
} | } | |||
/** | int channel_write_common(ssh_channel channel, const void *data, | |||
* @brief Blocking write on channel. | uint32_t len, int is_stderr) { | |||
* | ssh_session session = channel->session; | |||
* @param channel The channel to write to. | ||||
* | ||||
* @param data A pointer to the data to write. | ||||
* | ||||
* @param len The length of the buffer to write to. | ||||
* | ||||
* @return The number of bytes written, SSH_ERROR on error. | ||||
* | ||||
* @see channel_read() | ||||
*/ | ||||
int channel_write(CHANNEL *channel, const void *data, u32 len) { | ||||
SSH_SESSION *session = channel->session; | ||||
int origlen = len; | int origlen = len; | |||
int effectivelen; | int effectivelen; | |||
enter_function(); | enter_function(); | |||
if (channel->local_eof) { | if (channel->local_eof) { | |||
ssh_set_error(session, SSH_REQUEST_DENIED, | ssh_set_error(session, SSH_REQUEST_DENIED, | |||
"Can't write to channel %d:%d after EOF was sent", | "Can't write to channel %d:%d after EOF was sent", | |||
channel->local_channel, | channel->local_channel, | |||
channel->remote_channel); | channel->remote_channel); | |||
skipping to change at line 917 | skipping to change at line 928 | |||
if (packet_wait(channel->session, 0, 0) == SSH_ERROR) { | if (packet_wait(channel->session, 0, 0) == SSH_ERROR) { | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
} | } | |||
effectivelen = len > channel->remote_window ? channel->remote_window : len; | effectivelen = len > channel->remote_window ? channel->remote_window : len; | |||
} else { | } else { | |||
effectivelen = len; | effectivelen = len; | |||
} | } | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_DATA) < 0 || | if (buffer_add_u8(session->out_buffer, is_stderr ? | |||
SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CH | ||||
ANNEL_DATA) < 0 || | ||||
buffer_add_u32(session->out_buffer, | buffer_add_u32(session->out_buffer, | |||
htonl(channel->remote_channel)) < 0 || | htonl(channel->remote_channel)) < 0 || | |||
buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || | buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || | |||
buffer_add_data(session->out_buffer, data, effectivelen) < 0) { | buffer_add_data(session->out_buffer, data, effectivelen) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
if (packet_send(session) != SSH_OK) { | if (packet_send(session) != SSH_OK) { | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"channel_write wrote %d bytes", effectivelen); | "channel_write wrote %d bytes", effectivelen); | |||
channel->remote_window -= effectivelen; | channel->remote_window -= effectivelen; | |||
len -= effectivelen; | len -= effectivelen; | |||
data += effectivelen; | data = ((uint8_t*)data + effectivelen); | |||
} | } | |||
leave_function(); | leave_function(); | |||
return origlen; | return origlen; | |||
error: | error: | |||
buffer_reinit(session->out_buffer); | buffer_reinit(session->out_buffer); | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
/** | /** | |||
* @brief Blocking write on channel. | ||||
* | ||||
* @param channel The channel to write to. | ||||
* | ||||
* @param data A pointer to the data to write. | ||||
* | ||||
* @param len The length of the buffer to write to. | ||||
* | ||||
* @return The number of bytes written, SSH_ERROR on error. | ||||
* | ||||
* @see channel_read() | ||||
*/ | ||||
int channel_write(ssh_channel channel, const void *data, uint32_t len) { | ||||
return channel_write_common(channel, data, len, 0); | ||||
} | ||||
/** | ||||
* @brief Check if the channel is open or not. | * @brief Check if the channel is open or not. | |||
* | * | |||
* @param channel The channel to check. | * @param channel The channel to check. | |||
* | * | |||
* @return 0 if channel is closed, nonzero otherwise. | * @return 0 if channel is closed, nonzero otherwise. | |||
* | * | |||
* @see channel_is_closed() | * @see channel_is_closed() | |||
*/ | */ | |||
int channel_is_open(CHANNEL *channel) { | int channel_is_open(ssh_channel channel) { | |||
return (channel->open != 0 && channel->session->alive != 0); | return (channel->open != 0 && channel->session->alive != 0); | |||
} | } | |||
/** | /** | |||
* @brief Check if the channel is closed or not. | * @brief Check if the channel is closed or not. | |||
* | * | |||
* @param channel The channel to check. | * @param channel The channel to check. | |||
* | * | |||
* @return 0 if channel is opened, nonzero otherwise. | * @return 0 if channel is opened, nonzero otherwise. | |||
* | * | |||
* @see channel_is_open() | * @see channel_is_open() | |||
*/ | */ | |||
int channel_is_closed(CHANNEL *channel) { | int channel_is_closed(ssh_channel channel) { | |||
return (channel->open == 0 || channel->session->alive == 0); | return (channel->open == 0 || channel->session->alive == 0); | |||
} | } | |||
/** | /** | |||
* @brief Check if remote has sent an EOF. | * @brief Check if remote has sent an EOF. | |||
* | * | |||
* @param channel The channel to check. | * @param channel The channel to check. | |||
* | * | |||
* @return 0 if there is no EOF, nonzero otherwise. | * @return 0 if there is no EOF, nonzero otherwise. | |||
*/ | */ | |||
int channel_is_eof(CHANNEL *channel) { | int channel_is_eof(ssh_channel channel) { | |||
if ((channel->stdout_buffer && | if ((channel->stdout_buffer && | |||
buffer_get_rest_len(channel->stdout_buffer) > 0) || | buffer_get_rest_len(channel->stdout_buffer) > 0) || | |||
(channel->stderr_buffer && | (channel->stderr_buffer && | |||
buffer_get_rest_len(channel->stderr_buffer) > 0)) { | buffer_get_rest_len(channel->stderr_buffer) > 0)) { | |||
return 0; | return 0; | |||
} | } | |||
return (channel->remote_eof != 0); | return (channel->remote_eof != 0); | |||
} | } | |||
/** | /** | |||
* @brief Put the channel into blocking or nonblocking mode. | * @brief Put the channel into blocking or nonblocking mode. | |||
* | * | |||
* @param channel The channel to use. | * @param channel The channel to use. | |||
* | * | |||
* @param blocking A boolean for blocking or nonblocking. | * @param blocking A boolean for blocking or nonblocking. | |||
* | * | |||
* @bug This functionnality is still under development and | * @bug This functionnality is still under development and | |||
* doesn't work correctly. | * doesn't work correctly. | |||
*/ | */ | |||
void channel_set_blocking(CHANNEL *channel, int blocking) { | void channel_set_blocking(ssh_channel channel, int blocking) { | |||
channel->blocking = (blocking == 0 ? 0 : 1); | channel->blocking = (blocking == 0 ? 0 : 1); | |||
} | } | |||
static int channel_request(CHANNEL *channel, const char *request, | static int channel_request(ssh_channel channel, const char *request, | |||
BUFFER *buffer, int reply) { | ssh_buffer buffer, int reply) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
STRING *req = NULL; | ssh_string req = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
req = string_from_char(request); | req = string_from_char(request); | |||
if (req == NULL) { | if (req == NULL) { | |||
goto error; | goto error; | |||
} | } | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_REQUEST) < 0 || | if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_REQUEST) < 0 || | |||
skipping to change at line 1046 | skipping to change at line 1075 | |||
} | } | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"Sent a SSH_MSG_CHANNEL_REQUEST %s", request); | "Sent a SSH_MSG_CHANNEL_REQUEST %s", request); | |||
if (reply == 0) { | if (reply == 0) { | |||
leave_function(); | leave_function(); | |||
return SSH_OK; | return SSH_OK; | |||
} | } | |||
rc = packet_wait(session, SSH2_MSG_CHANNEL_SUCCESS, 1); | rc = packet_wait(session, SSH2_MSG_CHANNEL_SUCCESS, 1); | |||
if (rc) { | if (rc == SSH_ERROR) { | |||
if (session->in_packet.type == SSH2_MSG_CHANNEL_FAILURE) { | if (session->in_packet.type == SSH2_MSG_CHANNEL_FAILURE) { | |||
ssh_log(session, SSH_LOG_PACKET, | ssh_log(session, SSH_LOG_PACKET, | |||
"%s channel request failed", request); | "%s channel request failed", request); | |||
ssh_set_error(session, SSH_REQUEST_DENIED, | ssh_set_error(session, SSH_REQUEST_DENIED, | |||
"Channel request %s failed", request); | "Channel request %s failed", request); | |||
} else { | } else { | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"Received an unexpected %d message", session->in_packet.type); | "Received an unexpected %d message", session->in_packet.type); | |||
} | } | |||
} else { | } else { | |||
skipping to change at line 1083 | skipping to change at line 1112 | |||
* @param channel The channel to sent the request. | * @param channel The channel to sent the request. | |||
* | * | |||
* @param terminal The terminal type ("vt100, xterm,..."). | * @param terminal The terminal type ("vt100, xterm,..."). | |||
* | * | |||
* @param col The number of columns. | * @param col The number of columns. | |||
* | * | |||
* @param row The number of rows. | * @param row The number of rows. | |||
* | * | |||
* @return SSH_SUCCESS on success, SSH_ERROR on error. | * @return SSH_SUCCESS on success, SSH_ERROR on error. | |||
*/ | */ | |||
int channel_request_pty_size(CHANNEL *channel, const char *terminal, | int channel_request_pty_size(ssh_channel channel, const char *terminal, | |||
int col, int row) { | int col, int row) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
STRING *term = NULL; | ssh_string term = NULL; | |||
BUFFER *buffer = NULL; | ssh_buffer buffer = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
#ifdef WITH_SSH1 | #ifdef WITH_SSH1 | |||
if (channel->version==1) { | if (channel->version==1) { | |||
channel_request_pty_size1(channel,terminal, col, row); | channel_request_pty_size1(channel,terminal, col, row); | |||
leave_function(); | leave_function(); | |||
return rc; | return rc; | |||
} | } | |||
#endif | #endif | |||
skipping to change at line 1136 | skipping to change at line 1165 | |||
/** | /** | |||
* @brief Request a PTY. | * @brief Request a PTY. | |||
* | * | |||
* @param channel The channel to send the request. | * @param channel The channel to send the request. | |||
* | * | |||
* @return SSH_SUCCESS on success, SSH_ERROR on error. | * @return SSH_SUCCESS on success, SSH_ERROR on error. | |||
* | * | |||
* @see channel_request_pty_size() | * @see channel_request_pty_size() | |||
*/ | */ | |||
int channel_request_pty(CHANNEL *channel) { | int channel_request_pty(ssh_channel channel) { | |||
return channel_request_pty_size(channel, "xterm", 80, 24); | return channel_request_pty_size(channel, "xterm", 80, 24); | |||
} | } | |||
/** | /** | |||
* @brief Change the size of the terminal associated to a channel. | * @brief Change the size of the terminal associated to a channel. | |||
* | * | |||
* @param channel The channel to change the size. | * @param channel The channel to change the size. | |||
* | * | |||
* @param cols The new number of columns. | * @param cols The new number of columns. | |||
* | * | |||
* @param rows The new number of rows. | * @param rows The new number of rows. | |||
* | * | |||
* @warning Do not call it from a signal handler if you are not | * @warning Do not call it from a signal handler if you are not | |||
* sure any other libssh function using the same channel/session | * sure any other libssh function using the same channel/session | |||
* is running at same time (not 100% threadsafe). | * is running at same time (not 100% threadsafe). | |||
*/ | */ | |||
int channel_change_pty_size(CHANNEL *channel, int cols, int rows) { | int channel_change_pty_size(ssh_channel channel, int cols, int rows) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
BUFFER *buffer = NULL; | ssh_buffer buffer = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
#ifdef WITH_SSH1 | #ifdef WITH_SSH1 | |||
if (channel->version == 1) { | if (channel->version == 1) { | |||
rc = channel_change_pty_size1(channel,cols,rows); | rc = channel_change_pty_size1(channel,cols,rows); | |||
leave_function(); | leave_function(); | |||
return rc; | return rc; | |||
} | } | |||
skipping to change at line 1195 | skipping to change at line 1224 | |||
return rc; | return rc; | |||
} | } | |||
/** | /** | |||
* @brief Request a shell. | * @brief Request a shell. | |||
* | * | |||
* @param channel The channel to send the request. | * @param channel The channel to send the request. | |||
* | * | |||
* @returns SSH_SUCCESS on success, SSH_ERROR on error. | * @returns SSH_SUCCESS on success, SSH_ERROR on error. | |||
*/ | */ | |||
int channel_request_shell(CHANNEL *channel) { | int channel_request_shell(ssh_channel channel) { | |||
#ifdef WITH_SSH1 | #ifdef WITH_SSH1 | |||
if (channel->version == 1) { | if (channel->version == 1) { | |||
return channel_request_shell1(channel); | return channel_request_shell1(channel); | |||
} | } | |||
#endif | #endif | |||
return channel_request(channel, "shell", NULL, 1); | return channel_request(channel, "shell", NULL, 1); | |||
} | } | |||
/** | /** | |||
* @brief Request a subsystem (for example "sftp"). | * @brief Request a subsystem (for example "sftp"). | |||
* | * | |||
* @param channel The channel to send the request. | * @param channel The channel to send the request. | |||
* | * | |||
* @param system The subsystem to request (for example "sftp"). | * @param subsys The subsystem to request (for example "sftp"). | |||
* | * | |||
* @return SSH_SUCCESS on success, SSH_ERROR on error. | * @return SSH_SUCCESS on success, SSH_ERROR on error. | |||
* | * | |||
* @warning You normally don't have to call it for sftp, see sftp_new(). | * @warning You normally don't have to call it for sftp, see sftp_new(). | |||
*/ | */ | |||
int channel_request_subsystem(CHANNEL *channel, const char *sys) { | int channel_request_subsystem(ssh_channel channel, const char *subsys) { | |||
BUFFER *buffer = NULL; | ssh_buffer buffer = NULL; | |||
STRING *subsystem = NULL; | ssh_string subsystem = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
buffer = buffer_new(); | buffer = buffer_new(); | |||
if (buffer == NULL) { | if (buffer == NULL) { | |||
goto error; | goto error; | |||
} | } | |||
subsystem = string_from_char(sys); | subsystem = string_from_char(subsys); | |||
if (subsystem == NULL) { | if (subsystem == NULL) { | |||
goto error; | goto error; | |||
} | } | |||
if (buffer_add_ssh_string(buffer, subsystem) < 0) { | if (buffer_add_ssh_string(buffer, subsystem) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
rc = channel_request(channel, "subsystem", buffer, 1); | rc = channel_request(channel, "subsystem", buffer, 1); | |||
error: | error: | |||
buffer_free(buffer); | buffer_free(buffer); | |||
string_free(subsystem); | string_free(subsystem); | |||
return rc; | return rc; | |||
} | } | |||
int channel_request_sftp( CHANNEL *channel){ | int channel_request_sftp( ssh_channel channel){ | |||
return channel_request_subsystem(channel, "sftp"); | return channel_request_subsystem(channel, "sftp"); | |||
} | } | |||
static ssh_string generate_cookie(void) { | ||||
static const char *hex = "0123456789abcdef"; | ||||
char s[36]; | ||||
int i; | ||||
srand ((unsigned int)time(NULL)); | ||||
for (i = 0; i < 32; i++) { | ||||
s[i] = hex[rand() % 16]; | ||||
} | ||||
s[32] = '\0'; | ||||
return string_from_char(s); | ||||
} | ||||
/** | ||||
* @brief Sends the "x11-req" channel request over an existing session chan | ||||
nel. | ||||
* | ||||
* This will enable redirecting the display of the remote X11 applications | ||||
to | ||||
* local X server over an secure tunnel. | ||||
* | ||||
* @param channel An existing session channel where the remote X11 | ||||
* applications are going to be executed. | ||||
* | ||||
* @param single_connection A boolean to mark only one X11 app will be | ||||
* redirected. | ||||
* | ||||
* @param protocol x11 authentication protocol. Pass NULL to use the | ||||
* default value MIT-MAGIC-COOKIE-1 | ||||
* | ||||
* @param cookie x11 authentication cookie. Pass NULL to generate | ||||
* a random cookie. | ||||
* | ||||
* @param screen_number Screen number. | ||||
* | ||||
* @return SSH_OK on success\n | ||||
* SSH_ERROR on error | ||||
*/ | ||||
int channel_request_x11(ssh_channel channel, int single_connection, const c | ||||
har *protocol, | ||||
const char *cookie, int screen_number) { | ||||
ssh_buffer buffer = NULL; | ||||
ssh_string p = NULL; | ||||
ssh_string c = NULL; | ||||
int rc = SSH_ERROR; | ||||
buffer = buffer_new(); | ||||
if (buffer == NULL) { | ||||
goto error; | ||||
} | ||||
p = string_from_char(protocol ? protocol : "MIT-MAGIC-COOKIE-1"); | ||||
if (p == NULL) { | ||||
goto error; | ||||
} | ||||
if (cookie) { | ||||
c = string_from_char(cookie); | ||||
} else { | ||||
c = generate_cookie(); | ||||
} | ||||
if (c == NULL) { | ||||
goto error; | ||||
} | ||||
if (buffer_add_u8(buffer, single_connection == 0 ? 0 : 1) < 0 || | ||||
buffer_add_ssh_string(buffer, p) < 0 || | ||||
buffer_add_ssh_string(buffer, c) < 0 || | ||||
buffer_add_u32(buffer, htonl(screen_number)) < 0) { | ||||
goto error; | ||||
} | ||||
rc = channel_request(channel, "x11-req", buffer, 1); | ||||
error: | ||||
buffer_free(buffer); | ||||
string_free(p); | ||||
string_free(c); | ||||
return rc; | ||||
} | ||||
static ssh_channel channel_accept(ssh_session session, int channeltype, | ||||
int timeout_ms) { | ||||
#ifndef _WIN32 | ||||
static const struct timespec ts = { | ||||
.tv_sec = 0, | ||||
.tv_nsec = 50000000 /* 50ms */ | ||||
}; | ||||
#endif | ||||
ssh_message msg = NULL; | ||||
struct ssh_iterator *iterator; | ||||
int t; | ||||
for (t = timeout_ms; t >= 0; t -= 50) | ||||
{ | ||||
ssh_handle_packets(session); | ||||
if (session->ssh_message_list) { | ||||
iterator = ssh_list_get_iterator(session->ssh_message_list); | ||||
while (iterator) { | ||||
msg = (ssh_message)iterator->data; | ||||
if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN && | ||||
ssh_message_subtype(msg) == channeltype) { | ||||
ssh_list_remove(session->ssh_message_list, iterator); | ||||
return ssh_message_channel_request_open_reply_accept(msg); | ||||
} | ||||
iterator = iterator->next; | ||||
} | ||||
} | ||||
#ifdef _WIN32 | ||||
Sleep(50); /* 50ms */ | ||||
#else | ||||
nanosleep(&ts, NULL); | ||||
#endif | ||||
} | ||||
return NULL; | ||||
} | ||||
/** | ||||
* @brief Accept an X11 forwarding channel. | ||||
* | ||||
* @param channel An x11-enabled session channel. | ||||
* | ||||
* @param timeout_ms Timeout in milli-seconds. | ||||
* | ||||
* @return Newly created channel, or NULL if no X11 request from the server | ||||
*/ | ||||
ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms) { | ||||
return channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms); | ||||
} | ||||
static int global_request(ssh_session session, const char *request, | ||||
ssh_buffer buffer, int reply) { | ||||
ssh_string req = NULL; | ||||
int rc = SSH_ERROR; | ||||
enter_function(); | ||||
req = string_from_char(request); | ||||
if (req == NULL) { | ||||
goto error; | ||||
} | ||||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || | ||||
buffer_add_ssh_string(session->out_buffer, req) < 0 || | ||||
buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { | ||||
goto error; | ||||
} | ||||
string_free(req); | ||||
if (buffer != NULL) { | ||||
if (buffer_add_data(session->out_buffer, buffer_get(buffer), | ||||
buffer_get_len(buffer)) < 0) { | ||||
goto error; | ||||
} | ||||
} | ||||
if (packet_send(session) != SSH_OK) { | ||||
leave_function(); | ||||
return rc; | ||||
} | ||||
ssh_log(session, SSH_LOG_RARE, | ||||
"Sent a SSH_MSG_GLOBAL_REQUEST %s", request); | ||||
if (reply == 0) { | ||||
leave_function(); | ||||
return SSH_OK; | ||||
} | ||||
rc = packet_wait(session, SSH2_MSG_REQUEST_SUCCESS, 1); | ||||
if (rc == SSH_ERROR) { | ||||
if (session->in_packet.type == SSH2_MSG_REQUEST_FAILURE) { | ||||
ssh_log(session, SSH_LOG_PACKET, | ||||
"%s channel request failed", request); | ||||
ssh_set_error(session, SSH_REQUEST_DENIED, | ||||
"Channel request %s failed", request); | ||||
} else { | ||||
ssh_log(session, SSH_LOG_RARE, | ||||
"Received an unexpected %d message", session->in_packet.type); | ||||
} | ||||
} else { | ||||
ssh_log(session, SSH_LOG_RARE, "Received a SUCCESS"); | ||||
} | ||||
leave_function(); | ||||
return rc; | ||||
error: | ||||
buffer_reinit(session->out_buffer); | ||||
string_free(req); | ||||
leave_function(); | ||||
return rc; | ||||
} | ||||
/** | ||||
* @brief Sends the "tcpip-forward" global request to ask the server to beg | ||||
in | ||||
* listening for inbound connections. | ||||
* | ||||
* @param session The ssh session to send the request. | ||||
* | ||||
* @param address The address to bind to on the server. Pass NULL to | ||||
bind | ||||
* to all available addresses on all protocol families | ||||
* supported by the server. | ||||
* | ||||
* @param port The port to bind to on the server. Pass 0 to ask th | ||||
e | ||||
* server to allocate the next available unprivileged | ||||
port | ||||
* number | ||||
* | ||||
* @param bound_port The pointer to get actual bound port. Pass NULL to | ||||
* ignore. | ||||
* | ||||
* @return SSH_OK on success\n | ||||
* SSH_ERROR on error | ||||
*/ | ||||
int channel_forward_listen(ssh_session session, const char *address, int po | ||||
rt, int *bound_port) { | ||||
ssh_buffer buffer = NULL; | ||||
ssh_string addr = NULL; | ||||
int rc = SSH_ERROR; | ||||
uint32_t tmp; | ||||
buffer = buffer_new(); | ||||
if (buffer == NULL) { | ||||
goto error; | ||||
} | ||||
addr = string_from_char(address ? address : ""); | ||||
if (addr == NULL) { | ||||
goto error; | ||||
} | ||||
if (buffer_add_ssh_string(buffer, addr) < 0 || | ||||
buffer_add_u32(buffer, htonl(port)) < 0) { | ||||
goto error; | ||||
} | ||||
rc = global_request(session, "tcpip-forward", buffer, 1); | ||||
if (rc == SSH_OK && port == 0 && bound_port) { | ||||
buffer_get_u32(session->in_buffer, &tmp); | ||||
*bound_port = ntohl(tmp); | ||||
} | ||||
error: | ||||
buffer_free(buffer); | ||||
string_free(addr); | ||||
return rc; | ||||
} | ||||
/** | ||||
* @brief Accept an incoming TCP/IP forwarding channel. | ||||
* | ||||
* @param session The ssh session to use. | ||||
* | ||||
* @param timeout_ms Timeout in milli-seconds. | ||||
* | ||||
* @return Newly created channel, or NULL if no incoming channel request fr | ||||
om | ||||
* the server | ||||
*/ | ||||
ssh_channel channel_forward_accept(ssh_session session, int timeout_ms) { | ||||
return channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms); | ||||
} | ||||
/** | ||||
* @brief Sends the "cancel-tcpip-forward" global request to ask the server | ||||
to | ||||
* cancel the tcpip-forward request. | ||||
* | ||||
* @param session The ssh session to send the request. | ||||
* | ||||
* @param address The bound address on the server. | ||||
* | ||||
* @param port The bound port on the server. | ||||
* | ||||
* @return SSH_OK on success\n | ||||
* SSH_ERROR on error | ||||
*/ | ||||
int channel_forward_cancel(ssh_session session, const char *address, int po | ||||
rt) { | ||||
ssh_buffer buffer = NULL; | ||||
ssh_string addr = NULL; | ||||
int rc = SSH_ERROR; | ||||
buffer = buffer_new(); | ||||
if (buffer == NULL) { | ||||
goto error; | ||||
} | ||||
addr = string_from_char(address ? address : ""); | ||||
if (addr == NULL) { | ||||
goto error; | ||||
} | ||||
if (buffer_add_ssh_string(buffer, addr) < 0 || | ||||
buffer_add_u32(buffer, htonl(port)) < 0) { | ||||
goto error; | ||||
} | ||||
rc = global_request(session, "cancel-tcpip-forward", buffer, 1); | ||||
error: | ||||
buffer_free(buffer); | ||||
string_free(addr); | ||||
return rc; | ||||
} | ||||
/** | /** | |||
* @brief Set environement variables. | * @brief Set environement variables. | |||
* | * | |||
* @param channel The channel to set the environement variables. | * @param channel The channel to set the environement variables. | |||
* | * | |||
* @param name The name of the variable. | * @param name The name of the variable. | |||
* | * | |||
* @param value The value to set. | * @param value The value to set. | |||
* | * | |||
* @return SSH_SUCCESS on success, SSH_ERROR on error. | * @return SSH_SUCCESS on success, SSH_ERROR on error. | |||
* | * | |||
* @warning Some environement variables may be refused by security reasons. | * @warning Some environement variables may be refused by security reasons. | |||
* */ | * */ | |||
int channel_request_env(CHANNEL *channel, const char *name, const char *val | int channel_request_env(ssh_channel channel, const char *name, const char * | |||
ue) { | value) { | |||
BUFFER *buffer = NULL; | ssh_buffer buffer = NULL; | |||
STRING *str = NULL; | ssh_string str = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
buffer = buffer_new(); | buffer = buffer_new(); | |||
if (buffer == NULL) { | if (buffer == NULL) { | |||
goto error; | goto error; | |||
} | } | |||
str = string_from_char(name); | str = string_from_char(name); | |||
if (str == NULL) { | if (str == NULL) { | |||
goto error; | goto error; | |||
skipping to change at line 1310 | skipping to change at line 1640 | |||
* | * | |||
* @param channel The channel to execute the command. | * @param channel The channel to execute the command. | |||
* | * | |||
* @param cmd The command to execute | * @param cmd The command to execute | |||
* (e.g. "ls ~/ -al | grep -i reports"). | * (e.g. "ls ~/ -al | grep -i reports"). | |||
* | * | |||
* @return SSH_SUCCESS on success, SSH_ERROR on error. | * @return SSH_SUCCESS on success, SSH_ERROR on error. | |||
* | * | |||
* @see channel_request_shell() | * @see channel_request_shell() | |||
*/ | */ | |||
int channel_request_exec(CHANNEL *channel, const char *cmd) { | int channel_request_exec(ssh_channel channel, const char *cmd) { | |||
BUFFER *buffer = NULL; | ssh_buffer buffer = NULL; | |||
STRING *command = NULL; | ssh_string command = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
#ifdef WITH_SSH1 | #ifdef WITH_SSH1 | |||
if (channel->version == 1) { | if (channel->version == 1) { | |||
return channel_request_exec1(channel, cmd); | return channel_request_exec1(channel, cmd); | |||
} | } | |||
#endif | #endif | |||
buffer = buffer_new(); | buffer = buffer_new(); | |||
if (buffer == NULL) { | if (buffer == NULL) { | |||
skipping to change at line 1342 | skipping to change at line 1672 | |||
goto error; | goto error; | |||
} | } | |||
rc = channel_request(channel, "exec", buffer, 1); | rc = channel_request(channel, "exec", buffer, 1); | |||
error: | error: | |||
buffer_free(buffer); | buffer_free(buffer); | |||
string_free(command); | string_free(command); | |||
return rc; | return rc; | |||
} | } | |||
/** | ||||
* @brief Send a signal to remote process (as described in RFC 4254, sectio | ||||
n 6.9). | ||||
* | ||||
* Sends a signal 'signal' to the remote process. | ||||
* Note, that remote system may not support signals concept. | ||||
* In such a case this request will be silently ignored. | ||||
* Only SSH-v2 is supported (I'm not sure about SSH-v1). | ||||
* | ||||
* @param channel The channel to send signal. | ||||
* | ||||
* @param signal The signal to send (without SIG prefix) | ||||
* (e.g. "TERM" or "KILL"). | ||||
* | ||||
* @return SSH_SUCCESS on success, SSH_ERROR on error (including attempt to | ||||
send signal via SSH-v1 session). | ||||
* | ||||
*/ | ||||
int channel_request_send_signal(ssh_channel channel, const char *signal) { | ||||
ssh_buffer buffer = NULL; | ||||
ssh_string encoded_signal = NULL; | ||||
int rc = SSH_ERROR; | ||||
#ifdef WITH_SSH1 | ||||
if (channel->version == 1) { | ||||
return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. | ||||
} | ||||
#endif | ||||
buffer = buffer_new(); | ||||
if (buffer == NULL) { | ||||
goto error; | ||||
} | ||||
encoded_signal = string_from_char(signal); | ||||
if (encoded_signal == NULL) { | ||||
goto error; | ||||
} | ||||
if (buffer_add_ssh_string(buffer, encoded_signal) < 0) { | ||||
goto error; | ||||
} | ||||
rc = channel_request(channel, "signal", buffer, 0); | ||||
error: | ||||
buffer_free(buffer); | ||||
string_free(encoded_signal); | ||||
return rc; | ||||
} | ||||
/* TODO : fix the delayed close thing */ | /* TODO : fix the delayed close thing */ | |||
/* TODO : fix the blocking behaviours */ | /* TODO : fix the blocking behaviours */ | |||
/** | /** | |||
* @brief Read data from a channel into a buffer. | * @brief Read data from a channel into a buffer. | |||
* | * | |||
* @param channel The channel to read from. | * @param channel The channel to read from. | |||
* | * | |||
* @param buffer The buffer which will get the data. | * @param buffer The buffer which will get the data. | |||
* | * | |||
* @param count The count of bytes to be read. If it is biggerthan 0, | * @param count The count of bytes to be read. If it is biggerthan 0, | |||
* the exact size will be read, else (bytes=0) it will | * the exact size will be read, else (bytes=0) it will | |||
* return once anything is available. | * return once anything is available. | |||
* | * | |||
* @param is_stderr A boolean value to mark reading from the stderr str eam. | * @param is_stderr A boolean value to mark reading from the stderr str eam. | |||
* | * | |||
* @return The number of bytes read, 0 on end of file or SSH_ERROR on error . | * @return The number of bytes read, 0 on end of file or SSH_ERROR on error . | |||
*/ | */ | |||
int channel_read_buffer(CHANNEL *channel, BUFFER *buffer, u32 count, | int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t co unt, | |||
int is_stderr) { | int is_stderr) { | |||
SSH_SESSION *session=channel->session; | ssh_session session=channel->session; | |||
BUFFER *stdbuf = channel->stdout_buffer; | ssh_buffer stdbuf = channel->stdout_buffer; | |||
u32 maxread = count; | uint32_t maxread = count; | |||
u32 len; | uint32_t len; | |||
buffer_reinit(buffer); | buffer_reinit(buffer); | |||
enter_function(); | enter_function(); | |||
if (count == 0) { | if (count == 0) { | |||
maxread = MAX_PACKET_LEN; | maxread = MAX_PACKET_LEN; | |||
} | } | |||
if (is_stderr) { | if (is_stderr) { | |||
skipping to change at line 1465 | skipping to change at line 1843 | |||
* | * | |||
* @param channel The channel to read from. | * @param channel The channel to read from. | |||
* | * | |||
* @param dest The destination buffer which will get the data. | * @param dest The destination buffer which will get the data. | |||
* | * | |||
* @param count The count of bytes to be read. | * @param count The count of bytes to be read. | |||
* | * | |||
* @param is_stderr A boolean value to mark reading from the stderr flo w. | * @param is_stderr A boolean value to mark reading from the stderr flo w. | |||
* | * | |||
* @return The number of bytes read, 0 on end of file or SSH_ERROR on error . | * @return The number of bytes read, 0 on end of file or SSH_ERROR on error . | |||
* | * @warning This function may return less than count bytes of data, and won | |||
't | ||||
* block until count bytes have been read. | ||||
* @warning The read function using a buffer has been renamed to | * @warning The read function using a buffer has been renamed to | |||
* channel_read_buffer(). | * channel_read_buffer(). | |||
*/ | */ | |||
int channel_read(CHANNEL *channel, void *dest, u32 count, int is_stderr) { | int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_st | |||
SSH_SESSION *session = channel->session; | derr) { | |||
BUFFER *stdbuf = channel->stdout_buffer; | ssh_session session = channel->session; | |||
u32 len; | ssh_buffer stdbuf = channel->stdout_buffer; | |||
uint32_t len; | ||||
enter_function(); | enter_function(); | |||
if (count == 0) { | if (count == 0) { | |||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
} | } | |||
if (is_stderr) { | if (is_stderr) { | |||
stdbuf=channel->stderr_buffer; | stdbuf=channel->stderr_buffer; | |||
skipping to change at line 1503 | skipping to change at line 1882 | |||
channel->local_window); | channel->local_window); | |||
if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { | if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { | |||
if (grow_window(session, channel, | if (grow_window(session, channel, | |||
count - buffer_get_rest_len(stdbuf)) < 0) { | count - buffer_get_rest_len(stdbuf)) < 0) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
/* block reading if asked bytes=0 */ | /* block reading until at least one byte is read | |||
while (buffer_get_rest_len(stdbuf) == 0 || | * and ignore the trivial case count=0 | |||
buffer_get_rest_len(stdbuf) < count) { | */ | |||
while (buffer_get_rest_len(stdbuf) == 0 && count > 0) { | ||||
if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { | if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { | |||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
} | } | |||
if (channel->remote_eof) { | if (channel->remote_eof) { | |||
/* Return the resting bytes in buffer */ | /* Return the resting bytes in buffer */ | |||
break; | break; | |||
} | } | |||
skipping to change at line 1567 | skipping to change at line 1947 | |||
* | * | |||
* @param is_stderr A boolean to select the stderr stream. | * @param is_stderr A boolean to select the stderr stream. | |||
* | * | |||
* @return The number of bytes read, 0 if nothing is available or | * @return The number of bytes read, 0 if nothing is available or | |||
* SSH_ERROR on error. | * SSH_ERROR on error. | |||
* | * | |||
* @warning Don't forget to check for EOF as it would return 0 here. | * @warning Don't forget to check for EOF as it would return 0 here. | |||
* | * | |||
* @see channel_is_eof() | * @see channel_is_eof() | |||
*/ | */ | |||
int channel_read_nonblocking(CHANNEL *channel, void *dest, u32 count, | int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t coun t, | |||
int is_stderr) { | int is_stderr) { | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
u32 to_read; | uint32_t to_read; | |||
int rc; | int rc; | |||
enter_function(); | enter_function(); | |||
to_read = channel_poll(channel, is_stderr); | to_read = channel_poll(channel, is_stderr); | |||
if (to_read <= 0) { | if (to_read <= 0) { | |||
leave_function(); | leave_function(); | |||
return to_read; /* may be an error code */ | return to_read; /* may be an error code */ | |||
} | } | |||
skipping to change at line 1605 | skipping to change at line 1985 | |||
* | * | |||
* @param is_stderr A boolean to select the stderr stream. | * @param is_stderr A boolean to select the stderr stream. | |||
* | * | |||
* @return The number of bytes available for reading, 0 if nothing is avail able | * @return The number of bytes available for reading, 0 if nothing is avail able | |||
* or SSH_ERROR on error. | * or SSH_ERROR on error. | |||
* | * | |||
* @warning When the channel is in EOF state, the function returns SSH_EOF. | * @warning When the channel is in EOF state, the function returns SSH_EOF. | |||
* | * | |||
* @see channel_is_eof() | * @see channel_is_eof() | |||
*/ | */ | |||
int channel_poll(CHANNEL *channel, int is_stderr){ | int channel_poll(ssh_channel channel, int is_stderr){ | |||
SSH_SESSION *session = channel->session; | ssh_session session = channel->session; | |||
BUFFER *stdbuf = channel->stdout_buffer; | ssh_buffer stdbuf = channel->stdout_buffer; | |||
enter_function(); | enter_function(); | |||
if (is_stderr) { | if (is_stderr) { | |||
stdbuf = channel->stderr_buffer; | stdbuf = channel->stderr_buffer; | |||
} | } | |||
while (buffer_get_rest_len(stdbuf) == 0 && channel->remote_eof == 0) { | while (buffer_get_rest_len(stdbuf) == 0 && channel->remote_eof == 0) { | |||
if (ssh_handle_packets(channel->session) <= 0) { | if (ssh_handle_packets(channel->session) <= 0) { | |||
break; | break; | |||
skipping to change at line 1640 | skipping to change at line 2020 | |||
return buffer_get_rest_len(stdbuf); | return buffer_get_rest_len(stdbuf); | |||
} | } | |||
/** | /** | |||
* @brief Recover the session in which belongs a channel. | * @brief Recover the session in which belongs a channel. | |||
* | * | |||
* @param channel The channel to recover the session from. | * @param channel The channel to recover the session from. | |||
* | * | |||
* @return The session pointer. | * @return The session pointer. | |||
*/ | */ | |||
SSH_SESSION *channel_get_session(CHANNEL *channel) { | ssh_session channel_get_session(ssh_channel channel) { | |||
return channel->session; | return channel->session; | |||
} | } | |||
/** | /** | |||
* @brief Get the exit status of the channel (error code from the executed | * @brief Get the exit status of the channel (error code from the executed | |||
* instruction). | * instruction). | |||
* | * | |||
* @param channel The channel to get the status from. | * @param channel The channel to get the status from. | |||
* | * | |||
* @return -1 if no exit status has been returned or eof not sent, | * @return -1 if no exit status has been returned or eof not sent, | |||
* the exit status othewise. | * the exit status othewise. | |||
*/ | */ | |||
int channel_get_exit_status(CHANNEL *channel) { | int channel_get_exit_status(ssh_channel channel) { | |||
if (channel->local_eof == 0) { | if (channel->local_eof == 0) { | |||
return -1; | return -1; | |||
} | } | |||
while (channel->remote_eof == 0 || channel->exit_status == -1) { | while (channel->remote_eof == 0 || channel->exit_status == -1) { | |||
/* Parse every incoming packet */ | /* Parse every incoming packet */ | |||
if (packet_wait(channel->session, 0, 0) != SSH_OK) { | if (packet_wait(channel->session, 0, 0) != SSH_OK) { | |||
return -1; | return -1; | |||
} | } | |||
if (channel->open == 0) { | if (channel->open == 0) { | |||
skipping to change at line 1682 | skipping to change at line 2062 | |||
/* | /* | |||
* This function acts as a meta select. | * This function acts as a meta select. | |||
* | * | |||
* First, channels are analyzed to seek potential can-write or can-read one s, | * First, channels are analyzed to seek potential can-write or can-read one s, | |||
* then if no channel has been elected, it goes in a loop with the posix | * then if no channel has been elected, it goes in a loop with the posix | |||
* select(2). | * select(2). | |||
* This is made in two parts: protocol select and network select. The proto col | * This is made in two parts: protocol select and network select. The proto col | |||
* select does not use the network functions at all | * select does not use the network functions at all | |||
*/ | */ | |||
static int channel_protocol_select(CHANNEL **rchans, CHANNEL **wchans, | static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans | |||
CHANNEL **echans, CHANNEL **rout, CHANNEL **wout, CHANNEL **eout) { | , | |||
CHANNEL *chan; | ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel | |||
*eout) { | ||||
ssh_channel chan; | ||||
int i; | int i; | |||
int j = 0; | int j = 0; | |||
for (i = 0; rchans[i] != NULL; i++) { | for (i = 0; rchans[i] != NULL; i++) { | |||
chan = rchans[i]; | chan = rchans[i]; | |||
while (chan->open && ssh_socket_data_available(chan->session->socket)) { | while (chan->open && ssh_socket_data_available(chan->session->socket)) { | |||
ssh_handle_packets(chan->session); | ssh_handle_packets(chan->session); | |||
} | } | |||
skipping to change at line 1731 | skipping to change at line 2111 | |||
eout[j] = chan; | eout[j] = chan; | |||
j++; | j++; | |||
} | } | |||
} | } | |||
eout[j] = NULL; | eout[j] = NULL; | |||
return 0; | return 0; | |||
} | } | |||
/* Just count number of pointers in the array */ | /* Just count number of pointers in the array */ | |||
static int count_ptrs(CHANNEL **ptrs) { | static int count_ptrs(ssh_channel *ptrs) { | |||
int c; | int c; | |||
for (c = 0; ptrs[c] != NULL; c++) | for (c = 0; ptrs[c] != NULL; c++) | |||
; | ; | |||
return c; | return c; | |||
} | } | |||
/** | /** | |||
* @brief Act like the standard select(2) on channels. | * @brief Act like the standard select(2) on channels. | |||
* | * | |||
skipping to change at line 1760 | skipping to change at line 2140 | |||
* terminated by a NULL. | * terminated by a NULL. | |||
* | * | |||
* @param exceptchans A NULL pointer or an array of channel pointers, | * @param exceptchans A NULL pointer or an array of channel pointers, | |||
* terminated by a NULL. | * terminated by a NULL. | |||
* | * | |||
* @param timeout Timeout as defined by select(2). | * @param timeout Timeout as defined by select(2). | |||
* | * | |||
* @return SSH_SUCCESS operation successful\n | * @return SSH_SUCCESS operation successful\n | |||
* SSH_EINTR select(2) syscall was interrupted, relaunch the functi on | * SSH_EINTR select(2) syscall was interrupted, relaunch the functi on | |||
*/ | */ | |||
int channel_select(CHANNEL **readchans, CHANNEL **writechans, | int channel_select(ssh_channel *readchans, ssh_channel *writechans, | |||
CHANNEL **exceptchans, struct timeval * timeout) { | ssh_channel *exceptchans, struct timeval * timeout) { | |||
CHANNEL **rchans, **wchans, **echans; | ssh_channel *rchans, *wchans, *echans; | |||
CHANNEL *dummy = NULL; | ssh_channel dummy = NULL; | |||
fd_set rset; | fd_set rset; | |||
fd_set wset; | fd_set wset; | |||
fd_set eset; | fd_set eset; | |||
int fdmax = -1; | int fdmax = -1; | |||
int rc; | int rc; | |||
int i; | int i; | |||
/* don't allow NULL pointers */ | /* don't allow NULL pointers */ | |||
if (readchans == NULL) { | if (readchans == NULL) { | |||
readchans = &dummy; | readchans = &dummy; | |||
skipping to change at line 1790 | skipping to change at line 2170 | |||
if (exceptchans == NULL) { | if (exceptchans == NULL) { | |||
exceptchans = &dummy; | exceptchans = &dummy; | |||
} | } | |||
if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NU LL) { | if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NU LL) { | |||
/* No channel to poll?? Go away! */ | /* No channel to poll?? Go away! */ | |||
return 0; | return 0; | |||
} | } | |||
/* Prepare the outgoing temporary arrays */ | /* Prepare the outgoing temporary arrays */ | |||
rchans = malloc(sizeof(CHANNEL *) * (count_ptrs(readchans) + 1)); | rchans = malloc(sizeof(ssh_channel ) * (count_ptrs(readchans) + 1)); | |||
if (rchans == NULL) { | if (rchans == NULL) { | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
wchans = malloc(sizeof(CHANNEL *) * (count_ptrs(writechans) + 1)); | wchans = malloc(sizeof(ssh_channel ) * (count_ptrs(writechans) + 1)); | |||
if (wchans == NULL) { | if (wchans == NULL) { | |||
SAFE_FREE(rchans); | SAFE_FREE(rchans); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
echans = malloc(sizeof(CHANNEL *) * (count_ptrs(exceptchans) + 1)); | echans = malloc(sizeof(ssh_channel ) * (count_ptrs(exceptchans) + 1)); | |||
if (echans == NULL) { | if (echans == NULL) { | |||
SAFE_FREE(rchans); | SAFE_FREE(rchans); | |||
SAFE_FREE(wchans); | SAFE_FREE(wchans); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
/* | /* | |||
* First, try without doing network stuff then, select and redo the | * First, try without doing network stuff then, select and redo the | |||
* networkless stuff | * networkless stuff | |||
*/ | */ | |||
do { | do { | |||
channel_protocol_select(readchans, writechans, exceptchans, | channel_protocol_select(readchans, writechans, exceptchans, | |||
rchans, wchans, echans); | rchans, wchans, echans); | |||
if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) { | if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) { | |||
/* We've got one without doing any select overwrite the begining arra ys */ | /* We've got one without doing any select overwrite the begining arra ys */ | |||
memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(CHANNEL * | memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_chann | |||
)); | el )); | |||
memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(CHANNEL | memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_chan | |||
*)); | nel )); | |||
memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(CHANNEL | memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_cha | |||
*)); | nnel )); | |||
SAFE_FREE(rchans); | SAFE_FREE(rchans); | |||
SAFE_FREE(wchans); | SAFE_FREE(wchans); | |||
SAFE_FREE(echans); | SAFE_FREE(echans); | |||
return 0; | return 0; | |||
} | } | |||
/* | /* | |||
* Since we verified the invalid fd cases into the networkless select, | * Since we verified the invalid fd cases into the networkless select, | |||
* we can be sure all fd are valid ones | * we can be sure all fd are valid ones | |||
*/ | */ | |||
FD_ZERO(&rset); | FD_ZERO(&rset); | |||
End of changes. 83 change blocks. | ||||
144 lines changed or deleted | 547 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |