aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2015-07-07 00:05:29 +0200
committerAris Adamantiadis <aris@0xbadc0de.be>2015-12-07 20:32:17 +0100
commitbc771142249f013235eee97320455a8e6557b1b8 (patch)
treed577d95732a8687a47fbdd88cec1b0a1d5b80d71
parent371a917b9b3f186be49df90d30a8a92347a0bd3d (diff)
downloadlibssh-bc771142249f013235eee97320455a8e6557b1b8.tar.gz
libssh-bc771142249f013235eee97320455a8e6557b1b8.tar.xz
libssh-bc771142249f013235eee97320455a8e6557b1b8.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.h36
-rw-r--r--include/libssh/channels.h3
-rw-r--r--src/callbacks.c98
-rw-r--r--src/channels.c120
-rw-r--r--src/messages.c175
-rw-r--r--src/packet.c14
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 b05c4c02..5e886ed7 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 d8cd1907..72866522 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 (buffer_get_rest(buf) == 0) {
+ break;
}
- rest = channel->callbacks->channel_data_function(channel->session,
- channel,
- buffer_get_rest(buf),
- buffer_get_rest_len(buf),
- is_stderr,
- channel->callbacks->userdata);
- if(rest > 0) {
- if (channel->counter != NULL) {
- channel->counter->in_bytes += rest;
- }
- buffer_pass_bytes(buf, rest);
+ rest = ssh_callbacks_iterate_exec(channel->session,
+ channel,
+ buffer_get_rest(buf),
+ buffer_get_rest_len(buf),
+ is_stderr);
+ if (rest > 0) {
+ if (channel->counter != NULL) {
+ channel->counter->in_bytes += rest;
+ }
+ buffer_pass_bytes(buf, rest);
}
- if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
+ }
+ ssh_callbacks_iterate_end();
+
+ if (channel->local_window + buffer_get_rest_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 ec538771..293e9f6f 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 3537bde6..f4ed8fac 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;
}
}