1
1

connector: Implement ssh_connector_except()

Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
Aris Adamantiadis 2011-10-10 23:04:06 +02:00 коммит произвёл Andreas Schneider
родитель 0701745cbc
Коммит 9b3648ded0
6 изменённых файлов: 698 добавлений и 5 удалений

Просмотреть файл

@ -123,6 +123,7 @@ typedef struct ssh_scp_struct* ssh_scp;
typedef struct ssh_session_struct* ssh_session; typedef struct ssh_session_struct* ssh_session;
typedef struct ssh_string_struct* ssh_string; typedef struct ssh_string_struct* ssh_string;
typedef struct ssh_event_struct* ssh_event; typedef struct ssh_event_struct* ssh_event;
typedef struct ssh_connector_struct * ssh_connector;
typedef void* ssh_gssapi_creds; typedef void* ssh_gssapi_creds;
/* Socket type */ /* Socket type */
@ -373,6 +374,15 @@ enum ssh_scp_request_types {
SSH_SCP_REQUEST_WARNING SSH_SCP_REQUEST_WARNING
}; };
enum ssh_connector_flags_e {
/** Only the standard stream of the channel */
SSH_CONNECTOR_STDOUT = 1,
/** Only the exception stream of the channel */
SSH_CONNECTOR_STDERR = 2,
/** Merge both standard and exception streams */
SSH_CONNECTOR_BOTH = 3
};
LIBSSH_API int ssh_blocking_flush(ssh_session session, int timeout); LIBSSH_API int ssh_blocking_flush(ssh_session session, int timeout);
LIBSSH_API ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms); LIBSSH_API ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms);
LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel,int cols,int rows); LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel,int cols,int rows);
@ -422,6 +432,18 @@ LIBSSH_API uint32_t ssh_channel_window_size(ssh_channel channel);
LIBSSH_API char *ssh_basename (const char *path); LIBSSH_API char *ssh_basename (const char *path);
LIBSSH_API void ssh_clean_pubkey_hash(unsigned char **hash); LIBSSH_API void ssh_clean_pubkey_hash(unsigned char **hash);
LIBSSH_API int ssh_connect(ssh_session session); LIBSSH_API int ssh_connect(ssh_session session);
LIBSSH_API ssh_connector ssh_connector_new(ssh_session session);
LIBSSH_API void ssh_connector_free(ssh_connector connector);
LIBSSH_API int ssh_connector_set_in_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags);
LIBSSH_API int ssh_connector_set_out_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags);
LIBSSH_API void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd);
LIBSSH_API void ssh_connector_set_out_fd(ssh_connector connector, socket_t fd);
LIBSSH_API const char *ssh_copyright(void); LIBSSH_API const char *ssh_copyright(void);
LIBSSH_API void ssh_disconnect(ssh_session session); LIBSSH_API void ssh_disconnect(ssh_session session);
LIBSSH_API char *ssh_dirname (const char *path); LIBSSH_API char *ssh_dirname (const char *path);
@ -672,9 +694,11 @@ LIBSSH_API ssh_event ssh_event_new(void);
LIBSSH_API int ssh_event_add_fd(ssh_event event, socket_t fd, short events, LIBSSH_API int ssh_event_add_fd(ssh_event event, socket_t fd, short events,
ssh_event_callback cb, void *userdata); ssh_event_callback cb, void *userdata);
LIBSSH_API int ssh_event_add_session(ssh_event event, ssh_session session); LIBSSH_API int ssh_event_add_session(ssh_event event, ssh_session session);
LIBSSH_API int ssh_event_add_connector(ssh_event event, ssh_connector connector);
LIBSSH_API int ssh_event_dopoll(ssh_event event, int timeout); LIBSSH_API int ssh_event_dopoll(ssh_event event, int timeout);
LIBSSH_API int ssh_event_remove_fd(ssh_event event, socket_t fd); LIBSSH_API int ssh_event_remove_fd(ssh_event event, socket_t fd);
LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session); LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session);
LIBSSH_API int ssh_event_remove_connector(ssh_event event, ssh_connector connector);
LIBSSH_API void ssh_event_free(ssh_event event); LIBSSH_API void ssh_event_free(ssh_event event);
LIBSSH_API const char* ssh_get_clientbanner(ssh_session session); LIBSSH_API const char* ssh_get_clientbanner(ssh_session session);
LIBSSH_API const char* ssh_get_serverbanner(ssh_session session); LIBSSH_API const char* ssh_get_serverbanner(ssh_session session);

Просмотреть файл

@ -259,6 +259,10 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen);
/* match.c */ /* match.c */
int match_hostname(const char *host, const char *pattern, unsigned int len); int match_hostname(const char *host, const char *pattern, unsigned int len);
/* connector.c */
int ssh_connector_set_event(ssh_connector connector, ssh_event event);
int ssh_connector_remove_event(ssh_connector connector);
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif #endif

Просмотреть файл

@ -115,6 +115,7 @@ set(libssh_SRCS
client.c client.c
config.c config.c
connect.c connect.c
connector.c
curve25519.c curve25519.c
dh.c dh.c
ecdh.c ecdh.c

Просмотреть файл

@ -1363,6 +1363,23 @@ error:
return SSH_ERROR; return SSH_ERROR;
} }
/**
* @brief Get the remote window size.
*
* This is the maximum amounts of bytes the remote side expects us to send
* before growing the window again.
*
* @param[in] channel The channel to query.
*
* @return The remote window size
*
* @warning A nonzero return value does not guarantee the socket is ready
* to send that much data. Buffering may happen in the local SSH
* packet buffer, so beware of really big window sizes.
*
* @warning A zero return value means ssh_channel_write (default settings)
* will block until the window grows back.
*/
uint32_t ssh_channel_window_size(ssh_channel channel) { uint32_t ssh_channel_window_size(ssh_channel channel) {
return channel->remote_window; return channel->remote_window;
} }

619
src/connector.c Обычный файл
Просмотреть файл

@ -0,0 +1,619 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2015 by Aris Adamantiadis <aris@badcode.be>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* 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
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "libssh/priv.h"
#include "libssh/poll.h"
#include "libssh/callbacks.h"
#include "libssh/session.h"
#include <stdlib.h>
#define CHUNKSIZE 4096
struct ssh_connector_struct {
ssh_session session;
ssh_channel in_channel;
ssh_channel out_channel;
socket_t in_fd;
socket_t out_fd;
ssh_poll_handle in_poll;
ssh_poll_handle out_poll;
ssh_event event;
int in_available;
int out_wontblock;
struct ssh_channel_callbacks_struct in_channel_cb;
struct ssh_channel_callbacks_struct out_channel_cb;
enum ssh_connector_flags_e in_flags;
enum ssh_connector_flags_e out_flags;
};
static int ssh_connector_channel_data_cb(ssh_session session,
ssh_channel channel,
void *data,
uint32_t len,
int is_stderr,
void *userdata);
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
size_t bytes,
void *userdata);
ssh_connector ssh_connector_new(ssh_session session)
{
ssh_connector connector;
connector = calloc(1, sizeof(struct ssh_connector_struct));
if (connector == NULL){
ssh_set_error_oom(session);
return NULL;
}
connector->session = session;
connector->in_fd = SSH_INVALID_SOCKET;
connector->out_fd = SSH_INVALID_SOCKET;
ssh_callbacks_init(&connector->in_channel_cb);
ssh_callbacks_init(&connector->out_channel_cb);
connector->in_channel_cb.userdata = connector;
connector->in_channel_cb.channel_data_function = ssh_connector_channel_data_cb;
connector->out_channel_cb.userdata = connector;
connector->out_channel_cb.channel_write_wontblock_function =
ssh_connector_channel_write_wontblock_cb;
return connector;
}
void ssh_connector_free (ssh_connector connector)
{
if (connector->event != NULL){
ssh_connector_remove_event(connector);
}
if (connector->in_poll != NULL) {
ssh_poll_free(connector->in_poll);
connector->in_poll = NULL;
}
if (connector->out_poll != NULL) {
ssh_poll_free(connector->out_poll);
connector->out_poll = NULL;
}
if (connector->in_channel != NULL) {
ssh_remove_channel_callbacks(connector->in_channel,
&connector->in_channel_cb);
}
if (connector->out_channel != NULL) {
ssh_remove_channel_callbacks(connector->out_channel,
&connector->out_channel_cb);
}
free(connector);
}
int ssh_connector_set_in_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
{
connector->in_channel = channel;
connector->in_fd = SSH_INVALID_SOCKET;
connector->in_flags = flags;
/* Fallback to default value for invalid flags */
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
connector->in_flags = SSH_CONNECTOR_STDOUT;
}
return ssh_add_channel_callbacks(channel, &connector->in_channel_cb);
}
int ssh_connector_set_out_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
{
connector->out_channel = channel;
connector->out_fd = SSH_INVALID_SOCKET;
connector->out_flags = flags;
/* Fallback to default value for invalid flags */
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
connector->in_flags = SSH_CONNECTOR_STDOUT;
}
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
}
void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
{
connector->in_fd = fd;
connector->in_channel = NULL;
}
void ssh_connector_set_out_fd(ssh_connector connector, socket_t fd)
{
connector->out_fd = fd;
connector->out_channel = NULL;
}
/* TODO */
static void ssh_connector_except(ssh_connector connector, socket_t fd)
{
(void) connector;
(void) fd;
}
/* TODO */
static void ssh_connector_except_channel(ssh_connector connector,
ssh_channel channel)
{
(void) connector;
(void) channel;
}
/**
* @internal
*
* @brief Reset the poll events to be followed for each file descriptors.
*/
static void ssh_connector_reset_pollevents(ssh_connector connector)
{
if (connector->in_fd != SSH_INVALID_SOCKET) {
if (connector->in_available) {
ssh_poll_remove_events(connector->in_poll, POLLIN);
} else {
ssh_poll_add_events(connector->in_poll, POLLIN);
}
}
if (connector->out_fd != SSH_INVALID_SOCKET) {
if (connector->out_wontblock) {
ssh_poll_remove_events(connector->out_poll, POLLOUT);
} else {
ssh_poll_add_events(connector->out_poll, POLLOUT);
}
}
}
/**
* @internal
*
* @brief Callback called when a poll event is received on an input fd.
*/
static void ssh_connector_fd_in_cb(ssh_connector connector)
{
unsigned char buffer[CHUNKSIZE];
int r;
int toread = CHUNKSIZE;
int w;
int total = 0;
int rc;
SSH_LOG(SSH_LOG_TRACE, "connector POLLIN event for fd %d", connector->in_fd);
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
size_t size = ssh_channel_window_size(connector->out_channel);
/* Don't attempt reading more than the window */
toread = MIN(size, CHUNKSIZE);
}
r = read(connector->in_fd, buffer, toread);
if (r < 0) {
ssh_connector_except(connector, connector->in_fd);
return;
}
if (connector->out_channel != NULL) {
if (r == 0) {
rc = ssh_channel_send_eof(connector->out_channel);
(void)rc; /* TODO Handle rc? */
} else if (r> 0) {
/* loop around ssh_channel_write in case our window reduced due to a race */
while (total != r){
if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
w = ssh_channel_write(connector->out_channel,
buffer + total,
r - total);
} else {
w = ssh_channel_write_stderr(connector->out_channel,
buffer + total,
r - total);
}
if (w == SSH_ERROR) {
return;
}
total += w;
}
}
} else if (connector->out_fd != SSH_INVALID_SOCKET) {
if (r == 0){
close (connector->out_fd);
connector->out_fd = SSH_INVALID_SOCKET;
} else {
/*
* Loop around write in case the write blocks even for CHUNKSIZE
* bytes
*/
while (total != r) {
w = write(connector->out_fd, buffer + total, r - total);
if (w < 0){
ssh_connector_except(connector, connector->out_fd);
return;
}
total += w;
}
}
} else {
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
return;
}
connector->out_wontblock = 0;
connector->in_available = 0;
} else {
connector->in_available = 1;
}
}
/** @internal
* @brief Callback called when a poll event is received on an output fd
*/
static void ssh_connector_fd_out_cb(ssh_connector connector){
unsigned char buffer[CHUNKSIZE];
int r;
int w;
int total = 0;
SSH_LOG(SSH_LOG_TRACE, "connector POLLOUT event for fd %d", connector->out_fd);
if(connector->in_available){
if (connector->in_channel != NULL){
r = ssh_channel_read_nonblocking(connector->in_channel, buffer, CHUNKSIZE, 0);
if(r == SSH_ERROR){
ssh_connector_except_channel(connector, connector->in_channel);
return;
} else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
close(connector->out_fd);
connector->out_fd = SSH_INVALID_SOCKET;
return;
} else if(r>0) {
/* loop around write in case the write blocks even for CHUNKSIZE bytes */
while (total != r){
w = write(connector->out_fd, buffer + total, r - total);
if (w < 0){
ssh_connector_except(connector, connector->out_fd);
return;
}
total += w;
}
}
} else if (connector->in_fd != SSH_INVALID_SOCKET){
/* fallback on the socket input callback */
connector->out_wontblock = 1;
ssh_connector_fd_in_cb(connector);
} else {
ssh_set_error(connector->session,
SSH_FATAL,
"Output socket or channel closed");
return;
}
connector->in_available = 0;
connector->out_wontblock = 0;
} else {
connector->out_wontblock = 1;
}
}
/**
* @internal
*
* @brief Callback called when a poll event is received on a file descriptor.
*
* This is for (input or output.
*
* @param[in] fd file descriptor receiving the event
*
* @param[in] revents received Poll(2) events
*
* @param[in] userdata connector
*
* @returns 0
*/
static int ssh_connector_fd_cb(ssh_poll_handle p,
socket_t fd,
int revents,
void *userdata)
{
ssh_connector connector = userdata;
(void)p;
if (revents & POLLERR) {
ssh_connector_except(connector, fd);
} else if((revents & POLLIN) && fd == connector->in_fd) {
ssh_connector_fd_in_cb(connector);
} else if((revents & POLLOUT) && fd == connector->out_fd) {
ssh_connector_fd_out_cb(connector);
}
ssh_connector_reset_pollevents(connector);
return 0;
}
/**
* @internal
*
* @brief Callback called when data is received on channel.
*
* @param[in] data Pointer to the data
*
* @param[in] len Length of data
*
* @param[in] is_stderr Set to 1 if the data are out of band
*
* @param[in] userdata The ssh connector
*
* @returns Amount of data bytes consumed
*/
static int ssh_connector_channel_data_cb(ssh_session session,
ssh_channel channel,
void *data,
uint32_t len,
int is_stderr,
void *userdata)
{
ssh_connector connector = userdata;
int w;
size_t window;
(void) session;
(void) channel;
(void) is_stderr;
SSH_LOG(SSH_LOG_TRACE,"connector data on channel");
if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) {
/* ignore stderr */
return 0;
} else if (!is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDOUT)) {
/* ignore stdout */
return 0;
}
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
int window_len;
window = ssh_channel_window_size(connector->out_channel);
window_len = MIN(window, len);
/* Route the data to the right exception channel */
if (is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR)) {
w = ssh_channel_write_stderr(connector->out_channel,
data,
window_len);
} else if (!is_stderr &&
(connector->out_flags & SSH_CONNECTOR_STDOUT)) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
} else if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
} else {
w = ssh_channel_write_stderr(connector->out_channel,
data,
window_len);
}
if (w == SSH_ERROR) {
ssh_connector_except_channel(connector, connector->out_channel);
}
} else if (connector->out_fd != SSH_INVALID_SOCKET) {
w = write(connector->out_fd, data, len);
if (w < 0)
ssh_connector_except(connector, connector->out_fd);
} else {
ssh_set_error(session, SSH_FATAL, "output socket or channel closed");
return SSH_ERROR;
}
connector->out_wontblock = 0;
connector->in_available = 0;
if ((unsigned int)w < len) {
connector->in_available = 1;
}
ssh_connector_reset_pollevents(connector);
return w;
} else {
connector->in_available = 1;
return 0;
}
}
/**
* @internal
*
* @brief Callback called when the channel is free to write.
*
* @param[in] bytes Amount of bytes that can be written without blocking
*
* @param[in] userdata The ssh connector
*
* @returns Amount of data bytes consumed
*/
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
size_t bytes,
void *userdata)
{
ssh_connector connector = userdata;
uint8_t buffer[CHUNKSIZE];
int r, w;
(void) channel;
SSH_LOG(SSH_LOG_TRACE, "Channel write won't block");
if (connector->in_available) {
if (connector->in_channel != NULL) {
size_t len = MIN(CHUNKSIZE, bytes);
r = ssh_channel_read_nonblocking(connector->in_channel,
buffer,
len,
0);
if (r == SSH_ERROR) {
ssh_connector_except_channel(connector, connector->in_channel);
} else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
ssh_channel_send_eof(connector->out_channel);
} else if (r > 0) {
w = ssh_channel_write(connector->out_channel, buffer, r);
if (w == SSH_ERROR) {
ssh_connector_except_channel(connector,
connector->out_channel);
}
}
} else if (connector->in_fd != SSH_INVALID_SOCKET) {
/* fallback on on the socket input callback */
connector->out_wontblock = 1;
ssh_connector_fd_in_cb(connector);
ssh_connector_reset_pollevents(connector);
} else {
ssh_set_error(session,
SSH_FATAL,
"Output socket or channel closed");
return 0;
}
connector->in_available = 0;
connector->out_wontblock = 0;
} else {
connector->out_wontblock = 1;
}
return 0;
}
int ssh_connector_set_event(ssh_connector connector, ssh_event event)
{
int rc = SSH_OK;
if ((connector->in_fd == SSH_INVALID_SOCKET &&
connector->in_channel == NULL)
|| (connector->out_fd == SSH_INVALID_SOCKET &&
connector->out_channel == NULL)) {
rc = SSH_ERROR;
ssh_set_error(connector->session,SSH_FATAL,"Connector not complete");
goto error;
}
connector->event = event;
if (connector->in_fd != SSH_INVALID_SOCKET) {
if (connector->in_poll == NULL) {
connector->in_poll = ssh_poll_new(connector->in_fd,
POLLIN|POLLERR,
ssh_connector_fd_cb,
connector);
}
rc = ssh_event_add_poll(event, connector->in_poll);
if (rc != SSH_OK) {
goto error;
}
}
if (connector->out_fd != SSH_INVALID_SOCKET) {
if (connector->out_poll == NULL) {
connector->out_poll = ssh_poll_new(connector->out_fd,
POLLOUT|POLLERR,
ssh_connector_fd_cb,
connector);
}
rc = ssh_event_add_poll(event, connector->out_poll);
if (rc != SSH_OK) {
goto error;
}
}
if (connector->in_channel != NULL) {
rc = ssh_event_add_session(event,
ssh_channel_get_session(connector->in_channel));
if (rc != SSH_OK)
goto error;
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0){
connector->in_available = 1;
}
}
if(connector->out_channel != NULL) {
ssh_session session = ssh_channel_get_session(connector->out_channel);
rc = ssh_event_add_session(event, session);
if (rc != SSH_OK) {
goto error;
}
if (ssh_channel_window_size(connector->out_channel) > 0) {
connector->out_wontblock = 1;
}
}
error:
return rc;
}
int ssh_connector_remove_event(ssh_connector connector) {
ssh_session session;
if (connector->in_poll != NULL) {
ssh_event_remove_poll(connector->event, connector->in_poll);
ssh_poll_free(connector->in_poll);
connector->in_poll = NULL;
}
if (connector->out_poll != NULL) {
ssh_event_remove_poll(connector->event, connector->out_poll);
ssh_poll_free(connector->out_poll);
connector->out_poll = NULL;
}
if (connector->in_channel != NULL) {
session = ssh_channel_get_session(connector->in_channel);
ssh_event_remove_session(connector->event, session);
connector->in_channel = NULL;
}
if (connector->out_channel != NULL) {
session = ssh_channel_get_session(connector->out_channel);
ssh_event_remove_session(connector->event, session);
connector->out_channel = NULL;
}
connector->event = NULL;
return SSH_OK;
}

Просмотреть файл

@ -850,17 +850,34 @@ int ssh_event_add_session(ssh_event event, ssh_session session) {
} }
/** /**
* @brief Poll all the sockets and sessions associated through an event object. * @brief Add a connector to the SSH event loop
* If any of the events are set after the poll, the *
* call back functions of the sessions or sockets will be called. * @param[in] event The SSH event loop
* This function should be called once within the programs main loop. *
* @param[in] connector The connector object
*
* @return SSH_OK
*
* @return SSH_ERROR in case of error
*/
int ssh_event_add_connector(ssh_event event, ssh_connector connector){
return ssh_connector_set_event(connector, event);
}
/**
* @brief Poll all the sockets and sessions associated through an event object.i
*
* If any of the events are set after the poll, the call back functions of the
* sessions or sockets will be called.
* This function should be called once within the programs main loop.
* *
* @param event The ssh_event object to poll. * @param event The ssh_event object to poll.
*
* @param timeout An upper limit on the time for which the poll will * @param timeout An upper limit on the time for which the poll will
* block, in milliseconds. Specifying a negative value * block, in milliseconds. Specifying a negative value
* means an infinite timeout. This parameter is passed to * means an infinite timeout. This parameter is passed to
* the poll() function. * the poll() function.
* @returns SSH_OK No error. * @returns SSH_OK on success.
* SSH_ERROR Error happened during the poll. * SSH_ERROR Error happened during the poll.
*/ */
int ssh_event_dopoll(ssh_event event, int timeout) { int ssh_event_dopoll(ssh_event event, int timeout) {
@ -966,6 +983,17 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
return rc; return rc;
} }
/** @brief Remove a connector from an event context
* @param[in] event The ssh_event object.
* @param[in] connector connector object to remove
* @return SSH_OK on success
* @return SSH_ERROR on failure
*/
int ssh_event_remove_connector(ssh_event event, ssh_connector connector){
(void)event;
return ssh_connector_remove_event(connector);
}
/** /**
* @brief Free an event context. * @brief Free an event context.
* *