Blame src/connector.c

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