diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2015-07-07 00:05:29 +0200 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2016-05-02 16:56:54 +0200 |
commit | bbe437dbb163ef912f7778a1cb7ce346d687e440 (patch) | |
tree | c7475b97a6eaa2eb30bbdf047c7d7e978a371d9f | |
parent | 5c67530f1ee1e688d50ca11e8fb7620d9e142fe8 (diff) | |
download | libssh-bbe437dbb163ef912f7778a1cb7ce346d687e440.tar.gz libssh-bbe437dbb163ef912f7778a1cb7ce346d687e440.tar.xz libssh-bbe437dbb163ef912f7778a1cb7ce346d687e440.zip |
callbacks: Implement list of callbacks for channels
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r-- | include/libssh/callbacks.h | 36 | ||||
-rw-r--r-- | include/libssh/channels.h | 3 | ||||
-rw-r--r-- | src/callbacks.c | 98 | ||||
-rw-r--r-- | src/channels.c | 120 | ||||
-rw-r--r-- | src/messages.c | 175 | ||||
-rw-r--r-- | src/packet.c | 14 |
6 files changed, 283 insertions, 163 deletions
diff --git a/include/libssh/callbacks.h b/include/libssh/callbacks.h index 7ab963b2..c273cb0b 100644 --- a/include/libssh/callbacks.h +++ b/include/libssh/callbacks.h @@ -867,10 +867,46 @@ typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks; * @param cb The callback structure itself. * * @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, 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 diff --git a/include/libssh/channels.h b/include/libssh/channels.h index 44258bad..2c34bf55 100644 --- a/include/libssh/channels.h +++ b/include/libssh/channels.h @@ -74,7 +74,8 @@ struct ssh_channel_struct { int version; int exit_status; enum ssh_channel_request_state_e request_state; - ssh_channel_callbacks callbacks; + struct ssh_list *callbacks; /* list of ssh_channel_callbacks */ + /* counters */ ssh_counter counter; }; diff --git a/src/callbacks.c b/src/callbacks.c index 6f0373d8..3ed2f11c 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -25,7 +25,10 @@ #include "libssh/callbacks.h" #include "libssh/session.h" +#include "libssh/misc.h" +#define is_callback_valid(session, cb) \ + (cb->size <= 0 || cb->size > 1024 * sizeof(void *)) /* LEGACY */ 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; } - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - - return SSH_ERROR; - } + if (is_callback_valid(session, cb)) { + ssh_set_error(session, + SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + return SSH_ERROR; + }; session->common.callbacks = cb; /* LEGACY */ @@ -64,35 +67,80 @@ int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) { return 0; } -int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) { - ssh_session session = NULL; - if (channel == NULL || cb == NULL) { - return SSH_ERROR; - } - session = channel->session; +static int ssh_add_set_channel_callbacks(ssh_channel channel, + ssh_channel_callbacks cb, + int prepend) +{ + ssh_session session = NULL; + int rc; + + if (channel == NULL || cb == NULL) { + return SSH_ERROR; + } + session = channel->session; + + if (is_callback_valid(session, cb)) { + ssh_set_error(session, + 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 rc; +} - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid channel callback passed in (badly initialized)"); +int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) +{ + return ssh_add_set_channel_callbacks(channel, cb, 1); +} - return SSH_ERROR; - } - channel->callbacks = cb; +int ssh_add_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) +{ + return ssh_add_set_channel_callbacks(channel, cb, 0); +} - return 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){ if (session == NULL || cb == NULL) { return SSH_ERROR; } - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - - return SSH_ERROR; - } + if (is_callback_valid(session, cb)) { + ssh_set_error(session, + SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + return SSH_ERROR; + }; session->server_callbacks = cb; return 0; diff --git a/src/channels.c b/src/channels.c index 2a3fc45e..3b374fbb 100644 --- a/src/channels.c +++ b/src/channels.c @@ -538,31 +538,37 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ ssh_string_free(str); - if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) { - if(is_stderr) { - buf = channel->stderr_buffer; - } else { - buf = channel->stdout_buffer; + if (is_stderr) { + buf = channel->stderr_buffer; + } else { + buf = channel->stdout_buffer; + } + + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_data_function) { + if (ssh_buffer_get(buf) == 0) { + break; } - 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); + 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); } - if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { + } + 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; } @@ -585,11 +591,11 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) { /* channel->remote_window = 0; */ channel->remote_eof = 1; - if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) { - channel->callbacks->channel_eof_function(channel->session, - channel, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_eof_function, + channel->session, + channel); 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. */ - if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) { - channel->callbacks->channel_close_function(channel->session, - channel, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_close_function, + channel->session, + channel); + channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE; if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) ssh_channel_do_free(channel); @@ -668,12 +675,12 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { rc = ssh_buffer_unpack(packet, "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)) { - channel->callbacks->channel_exit_status_function(channel->session, - channel, - channel->exit_status, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_exit_status_function, + channel->session, + channel, + channel->exit_status); return SSH_PACKET_USED; } @@ -692,13 +699,13 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { SSH_LOG(SSH_LOG_PACKET, "Remote connection sent a signal SIG %s", sig); - if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { - channel->callbacks->channel_signal_function(channel->session, - channel, - sig, - channel->callbacks->userdata); - } - SAFE_FREE(sig); + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_signal_function, + channel->session, + channel, + sig); + SAFE_FREE(sig); return SSH_PACKET_USED; } @@ -728,12 +735,15 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { SSH_LOG(SSH_LOG_PACKET, "Remote connection closed by signal SIG %s %s", sig, core); - if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { - channel->callbacks->channel_exit_signal_function(channel->session, - channel, - sig, core_dumped, errmsg, lang, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_exit_signal_function, + channel->session, + channel, + sig, + core_dumped, + errmsg, + lang); SAFE_FREE(lang); SAFE_FREE(errmsg); @@ -760,10 +770,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { if (strcmp(request, "auth-agent-req@openssh.com") == 0) { SAFE_FREE(request); SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request"); - if(ssh_callbacks_exists(channel->callbacks, channel_auth_agent_req_function)) { - channel->callbacks->channel_auth_agent_req_function(channel->session, channel, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_auth_agent_req_function, + channel->session, + channel); 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->stderr_buffer); + if (channel->callbacks != NULL){ + ssh_list_free(channel->callbacks); + } /* debug trick to catch use after frees */ memset(channel, 'X', sizeof(struct ssh_channel_struct)); diff --git a/src/messages.c b/src/messages.c index 0e2b781a..2b139fbc 100644 --- a/src/messages.c +++ b/src/messages.c @@ -166,90 +166,113 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg) case SSH_REQUEST_CHANNEL: channel = msg->channel_request.channel; - if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY && - ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) { - rc = channel->callbacks->channel_pty_request_function(session, channel, - msg->channel_request.TERM, - msg->channel_request.width, msg->channel_request.height, - msg->channel_request.pxwidth, msg->channel_request.pxheight, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); + if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY){ + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_pty_request_function) { + rc = ssh_callbacks_iterate_exec(session, + channel, + msg->channel_request.TERM, + msg->channel_request.width, + msg->channel_request.height, + msg->channel_request.pxwidth, + msg->channel_request.pxheight); + 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_SHELL && - ssh_callbacks_exists(channel->callbacks, channel_shell_request_function)) { - rc = channel->callbacks->channel_shell_request_function(session, - channel, - 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_SHELL){ + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_shell_request_function) { + rc = ssh_callbacks_iterate_exec(session, channel); + 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_X11 && - ssh_callbacks_exists(channel->callbacks, channel_x11_req_function)) { - channel->callbacks->channel_x11_req_function(session, - channel, - msg->channel_request.x11_single_connection, - msg->channel_request.x11_auth_protocol, - msg->channel_request.x11_auth_cookie, - msg->channel_request.x11_screen_number, - channel->callbacks->userdata); - 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_callbacks_iterate_end(); + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11){ + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_x11_req_function) { + ssh_callbacks_iterate_exec(session, + channel, + msg->channel_request.x11_single_connection, + msg->channel_request.x11_auth_protocol, + msg->channel_request.x11_auth_cookie, + msg->channel_request.x11_screen_number); 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_ENV && - ssh_callbacks_exists(channel->callbacks, channel_env_request_function)) { - rc = channel->callbacks->channel_env_request_function(session, - channel, - msg->channel_request.var_name, msg->channel_request.var_value, - 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_WINDOW_CHANGE){ + ssh_callbacks_iterate(channel->callbacks, + ssh_channel_callbacks, + channel_pty_window_change_function) { + rc = ssh_callbacks_iterate_exec(session, + channel, + msg->channel_request.width, + msg->channel_request.height, + msg->channel_request.pxwidth, + msg->channel_request.pxheight); + 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; - } 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); + return SSH_OK; } + 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; case SSH_REQUEST_SERVICE: diff --git a/src/packet.c b/src/packet.c index 6e8e3521..2bfafdab 100644 --- a/src/packet.c +++ b/src/packet.c @@ -371,14 +371,12 @@ static void ssh_packet_socket_controlflow_callback(int code, void *userdata) it = ssh_list_get_iterator(session->channels); while (it != NULL) { channel = ssh_iterator_value(ssh_channel, it); - if (ssh_callbacks_exists(channel->callbacks, - channel_write_wontblock_function)) { - SSH_LOG(SSH_LOG_TRACE, "Executing write_wontblock callback for channel"); - channel->callbacks->channel_write_wontblock_function(session, - channel, - channel->remote_window, - channel->callbacks->userdata); - } + ssh_callbacks_execute_list(channel->callbacks, + ssh_channel_callbacks, + channel_write_wontblock_function, + session, + channel, + channel->remote_window); it = it->next; } } |