1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-08-07 08:02:55 +03:00

callbacks: Implement list of callbacks for channels

Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
Aris Adamantiadis
2015-07-07 00:05:29 +02:00
committed by Andreas Schneider
parent 5c67530f1e
commit bbe437dbb1
6 changed files with 286 additions and 166 deletions

View File

@@ -867,10 +867,46 @@ typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks;
* @param cb The callback structure itself. * @param cb The callback structure itself.
* *
* @return SSH_OK on success, SSH_ERROR on error. * @return SSH_OK on success, SSH_ERROR on error.
* @warning this function will not replace existing callbacks but set the
* new one atop of them.
*/ */
LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel, LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel,
ssh_channel_callbacks cb); ssh_channel_callbacks cb);
/**
* @brief Add channel callback functions
*
* This function will add channel callback functions to the channel callback
* list.
* Callbacks missing from a callback structure will be probed in the next
* on the list.
*
* @param channel The channel to set the callback structure.
*
* @param cb The callback structure itself.
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_set_channel_callbacks
*/
LIBSSH_API int ssh_add_channel_callbacks(ssh_channel channel,
ssh_channel_callbacks cb);
/**
* @brief Remove a channel callback.
*
* The channel has been added with ssh_add_channel_callbacks or
* ssh_set_channel_callbacks in this case.
*
* @param channel The channel to remove the callback structure from.
*
* @param cb The callback structure to remove
*
* @returns SSH_OK on success, SSH_ERROR on error.
*/
LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
ssh_channel_callbacks cb);
/** @} */ /** @} */
/** @group libssh_threads /** @group libssh_threads

View File

@@ -74,7 +74,8 @@ struct ssh_channel_struct {
int version; int version;
int exit_status; int exit_status;
enum ssh_channel_request_state_e request_state; enum ssh_channel_request_state_e request_state;
ssh_channel_callbacks callbacks; struct ssh_list *callbacks; /* list of ssh_channel_callbacks */
/* counters */ /* counters */
ssh_counter counter; ssh_counter counter;
}; };

View File

@@ -25,7 +25,10 @@
#include "libssh/callbacks.h" #include "libssh/callbacks.h"
#include "libssh/session.h" #include "libssh/session.h"
#include "libssh/misc.h"
#define is_callback_valid(session, cb) \
(cb->size <= 0 || cb->size > 1024 * sizeof(void *))
/* LEGACY */ /* LEGACY */
static void ssh_legacy_log_callback(int priority, static void ssh_legacy_log_callback(int priority,
@@ -47,12 +50,12 @@ int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) {
return SSH_ERROR; return SSH_ERROR;
} }
if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ if (is_callback_valid(session, cb)) {
ssh_set_error(session,SSH_FATAL, ssh_set_error(session,
"Invalid callback passed in (badly initialized)"); SSH_FATAL,
"Invalid callback passed in (badly initialized)");
return SSH_ERROR; return SSH_ERROR;
} };
session->common.callbacks = cb; session->common.callbacks = cb;
/* LEGACY */ /* LEGACY */
@@ -64,35 +67,80 @@ int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) {
return 0; return 0;
} }
int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) { static int ssh_add_set_channel_callbacks(ssh_channel channel,
ssh_session session = NULL; ssh_channel_callbacks cb,
if (channel == NULL || cb == NULL) { int prepend)
return SSH_ERROR; {
} ssh_session session = NULL;
session = channel->session; int rc;
if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ if (channel == NULL || cb == NULL) {
ssh_set_error(session,SSH_FATAL, return SSH_ERROR;
"Invalid channel callback passed in (badly initialized)"); }
session = channel->session;
return SSH_ERROR; if (is_callback_valid(session, cb)) {
} ssh_set_error(session,
channel->callbacks = cb; SSH_FATAL,
"Invalid callback passed in (badly initialized)");
return SSH_ERROR;
};
if (channel->callbacks == NULL) {
channel->callbacks = ssh_list_new();
if (channel->callbacks == NULL){
ssh_set_error_oom(session);
return SSH_ERROR;
}
}
if (prepend) {
rc = ssh_list_prepend(channel->callbacks, cb);
} else {
rc = ssh_list_append(channel->callbacks, cb);
}
return 0; return rc;
} }
int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
{
return ssh_add_set_channel_callbacks(channel, cb, 1);
}
int ssh_add_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
{
return ssh_add_set_channel_callbacks(channel, cb, 0);
}
int ssh_remove_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
{
struct ssh_iterator *it;
if (channel == NULL || channel->callbacks == NULL){
return SSH_ERROR;
}
it = ssh_list_find(channel->callbacks, cb);
if (it == NULL){
return SSH_ERROR;
}
ssh_list_remove(channel->callbacks, it);
return SSH_OK;
}
int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){ int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){
if (session == NULL || cb == NULL) { if (session == NULL || cb == NULL) {
return SSH_ERROR; return SSH_ERROR;
} }
if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ if (is_callback_valid(session, cb)) {
ssh_set_error(session,SSH_FATAL, ssh_set_error(session,
"Invalid callback passed in (badly initialized)"); SSH_FATAL,
"Invalid callback passed in (badly initialized)");
return SSH_ERROR; return SSH_ERROR;
} };
session->server_callbacks = cb; session->server_callbacks = cb;
return 0; return 0;

View File

@@ -538,31 +538,37 @@ SSH_PACKET_CALLBACK(channel_rcv_data){
ssh_string_free(str); ssh_string_free(str);
if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) { if (is_stderr) {
if(is_stderr) { buf = channel->stderr_buffer;
buf = channel->stderr_buffer; } else {
} else { buf = channel->stdout_buffer;
buf = channel->stdout_buffer;
}
rest = channel->callbacks->channel_data_function(channel->session,
channel,
ssh_buffer_get(buf),
ssh_buffer_get_len(buf),
is_stderr,
channel->callbacks->userdata);
if(rest > 0) {
if (channel->counter != NULL) {
channel->counter->in_bytes += rest;
}
ssh_buffer_pass_bytes(buf, rest);
}
if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
if (grow_window(session, channel, 0) < 0) {
return -1;
}
}
} }
ssh_callbacks_iterate(channel->callbacks,
ssh_channel_callbacks,
channel_data_function) {
if (ssh_buffer_get(buf) == 0) {
break;
}
rest = ssh_callbacks_iterate_exec(channel->session,
channel,
ssh_buffer_get(buf),
ssh_buffer_get_len(buf),
is_stderr);
if (rest > 0) {
if (channel->counter != NULL) {
channel->counter->in_bytes += rest;
}
ssh_buffer_pass_bytes(buf, rest);
}
}
ssh_callbacks_iterate_end();
if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
if (grow_window(session, channel, 0) < 0) {
return -1;
}
}
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
@@ -585,11 +591,11 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) {
/* channel->remote_window = 0; */ /* channel->remote_window = 0; */
channel->remote_eof = 1; channel->remote_eof = 1;
if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_eof_function(channel->session, ssh_channel_callbacks,
channel, channel_eof_function,
channel->callbacks->userdata); channel->session,
} channel);
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
@@ -629,11 +635,12 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
* buffer because the eof is ignored until the buffer is empty. * buffer because the eof is ignored until the buffer is empty.
*/ */
if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_close_function(channel->session, ssh_channel_callbacks,
channel, channel_close_function,
channel->callbacks->userdata); channel->session,
} channel);
channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE; channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
ssh_channel_do_free(channel); ssh_channel_do_free(channel);
@@ -668,12 +675,12 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
rc = ssh_buffer_unpack(packet, "d", &channel->exit_status); rc = ssh_buffer_unpack(packet, "d", &channel->exit_status);
SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status);
if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_exit_status_function(channel->session, ssh_channel_callbacks,
channel, channel_exit_status_function,
channel->exit_status, channel->session,
channel->callbacks->userdata); channel,
} channel->exit_status);
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
@@ -692,13 +699,13 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
SSH_LOG(SSH_LOG_PACKET, SSH_LOG(SSH_LOG_PACKET,
"Remote connection sent a signal SIG %s", sig); "Remote connection sent a signal SIG %s", sig);
if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_signal_function(channel->session, ssh_channel_callbacks,
channel, channel_signal_function,
sig, channel->session,
channel->callbacks->userdata); channel,
} sig);
SAFE_FREE(sig); SAFE_FREE(sig);
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
@@ -728,12 +735,15 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
SSH_LOG(SSH_LOG_PACKET, SSH_LOG(SSH_LOG_PACKET,
"Remote connection closed by signal SIG %s %s", sig, core); "Remote connection closed by signal SIG %s %s", sig, core);
if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_exit_signal_function(channel->session, ssh_channel_callbacks,
channel, channel_exit_signal_function,
sig, core_dumped, errmsg, lang, channel->session,
channel->callbacks->userdata); channel,
} sig,
core_dumped,
errmsg,
lang);
SAFE_FREE(lang); SAFE_FREE(lang);
SAFE_FREE(errmsg); SAFE_FREE(errmsg);
@@ -760,10 +770,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
if (strcmp(request, "auth-agent-req@openssh.com") == 0) { if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
SAFE_FREE(request); SAFE_FREE(request);
SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request"); SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request");
if(ssh_callbacks_exists(channel->callbacks, channel_auth_agent_req_function)) { ssh_callbacks_execute_list(channel->callbacks,
channel->callbacks->channel_auth_agent_req_function(channel->session, channel, ssh_channel_callbacks,
channel->callbacks->userdata); channel_auth_agent_req_function,
} channel->session,
channel);
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
@@ -1028,6 +1039,9 @@ void ssh_channel_do_free(ssh_channel channel){
} }
ssh_buffer_free(channel->stdout_buffer); ssh_buffer_free(channel->stdout_buffer);
ssh_buffer_free(channel->stderr_buffer); ssh_buffer_free(channel->stderr_buffer);
if (channel->callbacks != NULL){
ssh_list_free(channel->callbacks);
}
/* 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));

View File

@@ -166,90 +166,113 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
case SSH_REQUEST_CHANNEL: case SSH_REQUEST_CHANNEL:
channel = msg->channel_request.channel; channel = msg->channel_request.channel;
if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY && if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY){
ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) { ssh_callbacks_iterate(channel->callbacks,
rc = channel->callbacks->channel_pty_request_function(session, channel, ssh_channel_callbacks,
msg->channel_request.TERM, channel_pty_request_function) {
msg->channel_request.width, msg->channel_request.height, rc = ssh_callbacks_iterate_exec(session,
msg->channel_request.pxwidth, msg->channel_request.pxheight, channel,
channel->callbacks->userdata); msg->channel_request.TERM,
if (rc == 0) { msg->channel_request.width,
ssh_message_channel_request_reply_success(msg); msg->channel_request.height,
} else { msg->channel_request.pxwidth,
ssh_message_reply_default(msg); msg->channel_request.pxheight);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} }
ssh_callbacks_iterate_end();
return SSH_OK; } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SHELL){
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SHELL && ssh_callbacks_iterate(channel->callbacks,
ssh_callbacks_exists(channel->callbacks, channel_shell_request_function)) { ssh_channel_callbacks,
rc = channel->callbacks->channel_shell_request_function(session, channel_shell_request_function) {
channel, rc = ssh_callbacks_iterate_exec(session, channel);
channel->callbacks->userdata); if (rc == 0) {
if (rc == 0) { ssh_message_channel_request_reply_success(msg);
ssh_message_channel_request_reply_success(msg); } else {
} else { ssh_message_reply_default(msg);
ssh_message_reply_default(msg); }
return SSH_OK;
} }
ssh_callbacks_iterate_end();
return SSH_OK; } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11){
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11 && ssh_callbacks_iterate(channel->callbacks,
ssh_callbacks_exists(channel->callbacks, channel_x11_req_function)) { ssh_channel_callbacks,
channel->callbacks->channel_x11_req_function(session, channel_x11_req_function) {
channel, ssh_callbacks_iterate_exec(session,
msg->channel_request.x11_single_connection, channel,
msg->channel_request.x11_auth_protocol, msg->channel_request.x11_single_connection,
msg->channel_request.x11_auth_cookie, msg->channel_request.x11_auth_protocol,
msg->channel_request.x11_screen_number, msg->channel_request.x11_auth_cookie,
channel->callbacks->userdata); msg->channel_request.x11_screen_number);
ssh_message_channel_request_reply_success(msg);
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_WINDOW_CHANGE &&
ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) {
rc = channel->callbacks->channel_pty_window_change_function(session,
channel,
msg->channel_request.width, msg->channel_request.height,
msg->channel_request.pxwidth, msg->channel_request.pxheight,
channel->callbacks->userdata);
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC &&
ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) {
rc = channel->callbacks->channel_exec_request_function(session,
channel,
msg->channel_request.command,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg); ssh_message_channel_request_reply_success(msg);
} else { return SSH_OK;
ssh_message_reply_default(msg);
} }
ssh_callbacks_iterate_end();
return SSH_OK; } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_WINDOW_CHANGE){
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_ENV && ssh_callbacks_iterate(channel->callbacks,
ssh_callbacks_exists(channel->callbacks, channel_env_request_function)) { ssh_channel_callbacks,
rc = channel->callbacks->channel_env_request_function(session, channel_pty_window_change_function) {
channel, rc = ssh_callbacks_iterate_exec(session,
msg->channel_request.var_name, msg->channel_request.var_value, channel,
channel->callbacks->userdata); msg->channel_request.width,
if (rc == 0) { msg->channel_request.height,
ssh_message_channel_request_reply_success(msg); msg->channel_request.pxwidth,
} else { msg->channel_request.pxheight);
ssh_message_reply_default(msg); return SSH_OK;
} }
ssh_callbacks_iterate_end();
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC){
ssh_callbacks_iterate(channel->callbacks,
ssh_channel_callbacks,
channel_exec_request_function) {
rc = ssh_callbacks_iterate_exec(session,
channel,
msg->channel_request.command);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK; return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SUBSYSTEM &&
ssh_callbacks_exists(channel->callbacks, channel_subsystem_request_function)) {
rc = channel->callbacks->channel_subsystem_request_function(session,
channel,
msg->channel_request.subsystem,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
} }
ssh_callbacks_iterate_end();
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_ENV){
ssh_callbacks_iterate(channel->callbacks,
ssh_channel_callbacks,
channel_env_request_function) {
rc = ssh_callbacks_iterate_exec(session,
channel,
msg->channel_request.var_name,
msg->channel_request.var_value);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
}
ssh_callbacks_iterate_end();
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SUBSYSTEM){
ssh_callbacks_iterate(channel->callbacks,
ssh_channel_callbacks,
channel_subsystem_request_function) {
rc = ssh_callbacks_iterate_exec(session,
channel,
msg->channel_request.subsystem);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK; return SSH_OK;
}
ssh_callbacks_iterate_end();
} }
break; break;
case SSH_REQUEST_SERVICE: case SSH_REQUEST_SERVICE:

View File

@@ -371,14 +371,12 @@ static void ssh_packet_socket_controlflow_callback(int code, void *userdata)
it = ssh_list_get_iterator(session->channels); it = ssh_list_get_iterator(session->channels);
while (it != NULL) { while (it != NULL) {
channel = ssh_iterator_value(ssh_channel, it); channel = ssh_iterator_value(ssh_channel, it);
if (ssh_callbacks_exists(channel->callbacks, ssh_callbacks_execute_list(channel->callbacks,
channel_write_wontblock_function)) { ssh_channel_callbacks,
SSH_LOG(SSH_LOG_TRACE, "Executing write_wontblock callback for channel"); channel_write_wontblock_function,
channel->callbacks->channel_write_wontblock_function(session, session,
channel, channel,
channel->remote_window, channel->remote_window);
channel->callbacks->userdata);
}
it = it->next; it = it->next;
} }
} }