Blame src/socket.c

Packit 6c0a39
/*
Packit 6c0a39
 * socket.c - socket functions for the library
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2008-2010      by Aris Adamantiadis
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 <errno.h>
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
#include <winsock2.h>
Packit 6c0a39
#include <ws2tcpip.h>
Packit 6c0a39
#if _MSC_VER >= 1400
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 write
Packit 6c0a39
#define write _write
Packit 6c0a39
#endif /* _MSC_VER */
Packit 6c0a39
#else /* _WIN32 */
Packit 6c0a39
#include <fcntl.h>
Packit 6c0a39
#include <sys/types.h>
Packit 6c0a39
#include <sys/socket.h>
Packit 6c0a39
#include <sys/un.h>
Packit 6c0a39
#endif /* _WIN32 */
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/callbacks.h"
Packit 6c0a39
#include "libssh/socket.h"
Packit 6c0a39
#include "libssh/buffer.h"
Packit 6c0a39
#include "libssh/poll.h"
Packit 6c0a39
#include "libssh/session.h"
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @defgroup libssh_socket The SSH socket functions.
Packit 6c0a39
 * @ingroup libssh
Packit 6c0a39
 *
Packit 6c0a39
 * Functions for handling sockets.
Packit 6c0a39
 *
Packit 6c0a39
 * @{
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
enum ssh_socket_states_e {
Packit 6c0a39
	SSH_SOCKET_NONE,
Packit 6c0a39
	SSH_SOCKET_CONNECTING,
Packit 6c0a39
	SSH_SOCKET_CONNECTED,
Packit 6c0a39
	SSH_SOCKET_EOF,
Packit 6c0a39
	SSH_SOCKET_ERROR,
Packit 6c0a39
	SSH_SOCKET_CLOSED
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
struct ssh_socket_struct {
Packit 6c0a39
  socket_t fd;
Packit 6c0a39
  int fd_is_socket;
Packit 6c0a39
  int last_errno;
Packit 6c0a39
  int read_wontblock; /* reading now on socket will
Packit 6c0a39
                       not block */
Packit 6c0a39
  int write_wontblock;
Packit 6c0a39
  int data_except;
Packit 6c0a39
  enum ssh_socket_states_e state;
Packit 6c0a39
  ssh_buffer out_buffer;
Packit 6c0a39
  ssh_buffer in_buffer;
Packit 6c0a39
  ssh_session session;
Packit 6c0a39
  ssh_socket_callbacks callbacks;
Packit 6c0a39
  ssh_poll_handle poll_handle;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static int sockets_initialized = 0;
Packit 6c0a39
Packit 6c0a39
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
Packit 6c0a39
                                          void *buffer,
Packit 6c0a39
                                          uint32_t len);
Packit 6c0a39
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
Packit 6c0a39
                                           const void *buffer,
Packit 6c0a39
                                           uint32_t len);
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * \internal
Packit 6c0a39
 * \brief inits the socket system (windows specific)
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_init(void)
Packit 6c0a39
{
Packit 6c0a39
    if (sockets_initialized == 0) {
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
        struct WSAData wsaData;
Packit 6c0a39
Packit 6c0a39
        /* Initiates use of the Winsock DLL by a process. */
Packit 6c0a39
        if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
#endif
Packit 6c0a39
        ssh_poll_init();
Packit 6c0a39
Packit 6c0a39
        sockets_initialized = 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Cleanup the socket system.
Packit 6c0a39
 */
Packit 6c0a39
void ssh_socket_cleanup(void)
Packit 6c0a39
{
Packit 6c0a39
    if (sockets_initialized == 1) {
Packit 6c0a39
        ssh_poll_cleanup();
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
        WSACleanup();
Packit 6c0a39
#endif
Packit 6c0a39
        sockets_initialized = 0;
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * \internal
Packit 6c0a39
 * \brief creates a new Socket object
Packit 6c0a39
 */
Packit 6c0a39
ssh_socket ssh_socket_new(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    ssh_socket s;
Packit 6c0a39
Packit 6c0a39
    s = calloc(1, sizeof(struct ssh_socket_struct));
Packit 6c0a39
    if (s == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    s->fd = SSH_INVALID_SOCKET;
Packit 6c0a39
    s->last_errno = -1;
Packit 6c0a39
    s->fd_is_socket = 1;
Packit 6c0a39
    s->session = session;
Packit 6c0a39
    s->in_buffer = ssh_buffer_new();
Packit 6c0a39
    if (s->in_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        SAFE_FREE(s);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    s->out_buffer=ssh_buffer_new();
Packit 6c0a39
    if (s->out_buffer == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        ssh_buffer_free(s->in_buffer);
Packit 6c0a39
        SAFE_FREE(s);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    s->read_wontblock = 0;
Packit 6c0a39
    s->write_wontblock = 0;
Packit 6c0a39
    s->data_except = 0;
Packit 6c0a39
    s->poll_handle = NULL;
Packit 6c0a39
    s->state=SSH_SOCKET_NONE;
Packit 6c0a39
    return s;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Reset the state of a socket so it looks brand-new
Packit 6c0a39
 * @param[in] s socket to rest
Packit 6c0a39
 */
Packit 6c0a39
void ssh_socket_reset(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    s->fd = SSH_INVALID_SOCKET;
Packit 6c0a39
    s->last_errno = -1;
Packit 6c0a39
    s->fd_is_socket = 1;
Packit 6c0a39
    ssh_buffer_reinit(s->in_buffer);
Packit 6c0a39
    ssh_buffer_reinit(s->out_buffer);
Packit 6c0a39
    s->read_wontblock = 0;
Packit 6c0a39
    s->write_wontblock = 0;
Packit 6c0a39
    s->data_except = 0;
Packit 6c0a39
    s->poll_handle = NULL;
Packit 6c0a39
    s->state=SSH_SOCKET_NONE;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief the socket callbacks, i.e. callbacks to be called
Packit 6c0a39
 * upon a socket event.
Packit 6c0a39
 * @param s socket to set callbacks on.
Packit 6c0a39
 * @param callbacks a ssh_socket_callback object reference.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
Packit 6c0a39
{
Packit 6c0a39
    s->callbacks = callbacks;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief               SSH poll callback. This callback will be used when an event
Packit 6c0a39
 *                      caught on the socket.
Packit 6c0a39
 *
Packit 6c0a39
 * @param p             Poll object this callback belongs to.
Packit 6c0a39
 * @param fd            The raw socket.
Packit 6c0a39
 * @param revents       The current poll events on the socket.
Packit 6c0a39
 * @param userdata      Userdata to be passed to the callback function,
Packit 6c0a39
 *                      in this case the socket object.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 on success, < 0 when the poll object has been removed
Packit 6c0a39
 *                      from its poll context.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
Packit 6c0a39
                            socket_t fd,
Packit 6c0a39
                            int revents,
Packit 6c0a39
                            void *v_s)
Packit 6c0a39
{
Packit 6c0a39
    ssh_socket s = (ssh_socket)v_s;
Packit 6c0a39
    char buffer[MAX_BUF_SIZE];
Packit 6c0a39
    ssize_t nread;
Packit 6c0a39
    int rc;
Packit 6c0a39
    int err = 0;
Packit 6c0a39
    socklen_t errlen = sizeof(err);
Packit 6c0a39
Packit 6c0a39
    /* Do not do anything if this socket was already closed */
Packit 6c0a39
    if (!ssh_socket_is_open(s)) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
    SSH_LOG(SSH_LOG_TRACE, "Poll callback on socket %d (%s%s%s), out buffer %d",fd,
Packit 6c0a39
            (revents & POLLIN) ? "POLLIN ":"",
Packit 6c0a39
            (revents & POLLOUT) ? "POLLOUT ":"",
Packit 6c0a39
            (revents & POLLERR) ? "POLLERR":"",
Packit 6c0a39
            ssh_buffer_get_len(s->out_buffer));
Packit 6c0a39
    if ((revents & POLLERR) || (revents & POLLHUP)) {
Packit 6c0a39
        /* Check if we are in a connecting state */
Packit 6c0a39
        if (s->state == SSH_SOCKET_CONNECTING) {
Packit 6c0a39
            s->state = SSH_SOCKET_ERROR;
Packit 6c0a39
            rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
Packit 6c0a39
            if (rc < 0) {
Packit 6c0a39
                err = errno;
Packit 6c0a39
            }
Packit 6c0a39
            ssh_socket_close(s);
Packit 6c0a39
            /* Overwrite ssh_socket_close() error with the real socket error */
Packit 6c0a39
            s->last_errno = err;
Packit 6c0a39
            errno = err;
Packit 6c0a39
Packit 6c0a39
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
Packit 6c0a39
                s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,
Packit 6c0a39
                                        err,
Packit 6c0a39
                                        s->callbacks->userdata);
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
        /* Then we are in a more standard kind of error */
Packit 6c0a39
        /* force a read to get an explanation */
Packit 6c0a39
        revents |= POLLIN;
Packit 6c0a39
    }
Packit 6c0a39
    if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
Packit 6c0a39
        s->read_wontblock = 1;
Packit 6c0a39
        nread = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer));
Packit 6c0a39
        if (nread < 0) {
Packit 6c0a39
            if (p != NULL) {
Packit 6c0a39
                ssh_poll_remove_events(p, POLLIN);
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
Packit 6c0a39
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
Packit 6c0a39
                                        s->last_errno,
Packit 6c0a39
                                        s->callbacks->userdata);
Packit 6c0a39
            }
Packit 6c0a39
            return -2;
Packit 6c0a39
        }
Packit 6c0a39
        if (nread == 0) {
Packit 6c0a39
            if (p != NULL) {
Packit 6c0a39
                ssh_poll_remove_events(p, POLLIN);
Packit 6c0a39
            }
Packit 6c0a39
            if (s->callbacks != NULL && s->callbacks->exception != NULL) {
Packit 6c0a39
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF,
Packit 6c0a39
                                        0,
Packit 6c0a39
                                        s->callbacks->userdata);
Packit 6c0a39
            }
Packit 6c0a39
            return -2;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if (s->session->socket_counter != NULL) {
Packit 6c0a39
            s->session->socket_counter->in_bytes += nread;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* Bufferize the data and then call the callback */
Packit 6c0a39
        rc = ssh_buffer_add_data(s->in_buffer, buffer, nread);
Packit 6c0a39
        if (rc < 0) {
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
        if (s->callbacks != NULL && s->callbacks->data != NULL) {
Packit 6c0a39
            do {
Packit 6c0a39
                nread = s->callbacks->data(ssh_buffer_get(s->in_buffer),
Packit 6c0a39
                                       ssh_buffer_get_len(s->in_buffer),
Packit 6c0a39
                                       s->callbacks->userdata);
Packit 6c0a39
                ssh_buffer_pass_bytes(s->in_buffer, nread);
Packit 6c0a39
            } while ((nread > 0) && (s->state == SSH_SOCKET_CONNECTED));
Packit 6c0a39
Packit 6c0a39
            /* p may have been freed, so don't use it
Packit 6c0a39
             * anymore in this function */
Packit 6c0a39
            p = NULL;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
    if (revents & POLLOUT || revents & POLLWRNORM) {
Packit 6c0a39
#else
Packit 6c0a39
    if (revents & POLLOUT) {
Packit 6c0a39
#endif
Packit 6c0a39
        uint32_t len;
Packit 6c0a39
Packit 6c0a39
        /* First, POLLOUT is a sign we may be connected */
Packit 6c0a39
        if (s->state == SSH_SOCKET_CONNECTING) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
Packit 6c0a39
            s->state = SSH_SOCKET_CONNECTED;
Packit 6c0a39
            if (p != NULL) {
Packit 6c0a39
                ssh_poll_set_events(p, POLLOUT | POLLIN);
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            rc = ssh_socket_set_blocking(ssh_socket_get_fd(s));
Packit 6c0a39
            if (rc < 0) {
Packit 6c0a39
                return -1;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            if (s->callbacks != NULL && s->callbacks->connected != NULL) {
Packit 6c0a39
                s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
Packit 6c0a39
                                        0,
Packit 6c0a39
                                        s->callbacks->userdata);
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            return 0;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* So, we can write data */
Packit 6c0a39
        s->write_wontblock = 1;
Packit 6c0a39
        if (p != NULL) {
Packit 6c0a39
            ssh_poll_remove_events(p, POLLOUT);
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* If buffered data is pending, write it */
Packit 6c0a39
        len = ssh_buffer_get_len(s->out_buffer);
Packit 6c0a39
        if (len > 0) {
Packit 6c0a39
            ssh_socket_nonblocking_flush(s);
Packit 6c0a39
        } else if (s->callbacks != NULL && s->callbacks->controlflow != NULL) {
Packit 6c0a39
            /* Otherwise advertise the upper level that write can be done */
Packit 6c0a39
            SSH_LOG(SSH_LOG_TRACE,"sending control flow event");
Packit 6c0a39
            s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
Packit 6c0a39
                                      s->callbacks->userdata);
Packit 6c0a39
        }
Packit 6c0a39
        /* TODO: Find a way to put back POLLOUT when buffering occurs */
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Return -1 if the poll handler disappeared */
Packit 6c0a39
    if (s->poll_handle == NULL) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief returns the poll handle corresponding to the socket,
Packit 6c0a39
 * creates it if it does not exist.
Packit 6c0a39
 * @returns allocated and initialized ssh_poll_handle object
Packit 6c0a39
 */
Packit 6c0a39
ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    if (s->poll_handle) {
Packit 6c0a39
        return s->poll_handle;
Packit 6c0a39
    }
Packit 6c0a39
    s->poll_handle = ssh_poll_new(s->fd,0,ssh_socket_pollcallback,s);
Packit 6c0a39
    return s->poll_handle;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief Deletes a socket object
Packit 6c0a39
 */
Packit 6c0a39
void ssh_socket_free(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    if (s == NULL) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
    ssh_socket_close(s);
Packit 6c0a39
    ssh_buffer_free(s->in_buffer);
Packit 6c0a39
    ssh_buffer_free(s->out_buffer);
Packit 6c0a39
    SAFE_FREE(s);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
int ssh_socket_unix(ssh_socket s, const char *path)
Packit 6c0a39
{
Packit 6c0a39
    struct sockaddr_un sunaddr;
Packit 6c0a39
    socket_t fd;
Packit 6c0a39
    sunaddr.sun_family = AF_UNIX;
Packit 6c0a39
    snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
Packit 6c0a39
Packit 6c0a39
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
Packit 6c0a39
    if (fd == SSH_INVALID_SOCKET) {
Packit 6c0a39
        ssh_set_error(s->session, SSH_FATAL,
Packit 6c0a39
                      "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
Packit 6c0a39
                      strerror(errno));
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (fcntl(fd, F_SETFD, 1) == -1) {
Packit 6c0a39
        ssh_set_error(s->session, SSH_FATAL,
Packit 6c0a39
                      "Error from fcntl(fd, F_SETFD, 1): %s",
Packit 6c0a39
                      strerror(errno));
Packit 6c0a39
        close(fd);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
Packit 6c0a39
        ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
Packit 6c0a39
                      strerror(errno));
Packit 6c0a39
        close(fd);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
    ssh_socket_set_fd(s,fd);
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief closes a socket
Packit 6c0a39
 */
Packit 6c0a39
void ssh_socket_close(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    if (ssh_socket_is_open(s)) {
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
        CLOSE_SOCKET(s->fd);
Packit 6c0a39
        s->last_errno = WSAGetLastError();
Packit 6c0a39
#else
Packit 6c0a39
        CLOSE_SOCKET(s->fd);
Packit 6c0a39
        s->last_errno = errno;
Packit 6c0a39
#endif
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (s->poll_handle != NULL) {
Packit 6c0a39
        ssh_poll_free(s->poll_handle);
Packit 6c0a39
        s->poll_handle = NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    s->state = SSH_SOCKET_CLOSED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief sets the file descriptor of the socket.
Packit 6c0a39
 * @param[out] s ssh_socket to update
Packit 6c0a39
 * @param[in] fd file descriptor to set
Packit 6c0a39
 * @warning this function updates boths the input and output
Packit 6c0a39
 * file descriptors
Packit 6c0a39
 */
Packit 6c0a39
void ssh_socket_set_fd(ssh_socket s, socket_t fd)
Packit 6c0a39
{
Packit 6c0a39
    s->fd = fd;
Packit 6c0a39
Packit 6c0a39
    if (s->poll_handle) {
Packit 6c0a39
        ssh_poll_set_fd(s->poll_handle,fd);
Packit 6c0a39
    } else {
Packit 6c0a39
        s->state = SSH_SOCKET_CONNECTING;
Packit 6c0a39
Packit 6c0a39
        /* POLLOUT is the event to wait for in a nonblocking connect */
Packit 6c0a39
        ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLOUT);
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
        ssh_poll_add_events(ssh_socket_get_poll_handle(s), POLLWRNORM);
Packit 6c0a39
#endif
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief returns the input file descriptor of the socket
Packit 6c0a39
 */
Packit 6c0a39
socket_t ssh_socket_get_fd(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    return s->fd;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief returns nonzero if the socket is open
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_is_open(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    return s->fd != SSH_INVALID_SOCKET;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief read len bytes from socket into buffer
Packit 6c0a39
 */
Packit 6c0a39
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
Packit 6c0a39
                                          void *buffer,
Packit 6c0a39
                                          uint32_t len)
Packit 6c0a39
{
Packit 6c0a39
    ssize_t rc = -1;
Packit 6c0a39
Packit 6c0a39
    if (s->data_except) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
    if (s->fd_is_socket) {
Packit 6c0a39
        rc = recv(s->fd,buffer, len, 0);
Packit 6c0a39
    } else {
Packit 6c0a39
        rc = read(s->fd,buffer, len);
Packit 6c0a39
    }
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
    s->last_errno = WSAGetLastError();
Packit 6c0a39
#else
Packit 6c0a39
    s->last_errno = errno;
Packit 6c0a39
#endif
Packit 6c0a39
    s->read_wontblock = 0;
Packit 6c0a39
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        s->data_except = 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief writes len bytes from buffer to socket
Packit 6c0a39
 */
Packit 6c0a39
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
Packit 6c0a39
                                           const void *buffer,
Packit 6c0a39
                                           uint32_t len)
Packit 6c0a39
{
Packit 6c0a39
    ssize_t w = -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 (s->data_except) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (s->fd_is_socket) {
Packit 6c0a39
        w = send(s->fd, buffer, len, flags);
Packit 6c0a39
    } else {
Packit 6c0a39
        w = write(s->fd, buffer, len);
Packit 6c0a39
    }
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
    s->last_errno = WSAGetLastError();
Packit 6c0a39
#else
Packit 6c0a39
    s->last_errno = errno;
Packit 6c0a39
#endif
Packit 6c0a39
    s->write_wontblock = 0;
Packit 6c0a39
    /* Reactive the POLLOUT detector in the poll multiplexer system */
Packit 6c0a39
    if (s->poll_handle) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket");
Packit 6c0a39
        ssh_poll_set_events(s->poll_handle,ssh_poll_get_events(s->poll_handle) | POLLOUT);
Packit 6c0a39
    }
Packit 6c0a39
    if (w < 0) {
Packit 6c0a39
        s->data_except = 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return w;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief returns nonzero if the current socket is in the fd_set
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
Packit 6c0a39
{
Packit 6c0a39
    if(s->fd == SSH_INVALID_SOCKET) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
    return FD_ISSET(s->fd,set);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief sets the current fd in a fd_set and updates the max_fd
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
Packit 6c0a39
{
Packit 6c0a39
    if (s->fd == SSH_INVALID_SOCKET) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    FD_SET(s->fd,set);
Packit 6c0a39
Packit 6c0a39
    if (s->fd >= 0 &&
Packit 6c0a39
        s->fd >= *max_fd &&
Packit 6c0a39
        s->fd != SSH_INVALID_SOCKET) {
Packit 6c0a39
        *max_fd = s->fd + 1;
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief buffered write of data
Packit 6c0a39
 * \returns SSH_OK, or SSH_ERROR
Packit 6c0a39
 * \warning has no effect on socket before a flush
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_write(ssh_socket s, const void *buffer, int len)
Packit 6c0a39
{
Packit 6c0a39
    if (len > 0) {
Packit 6c0a39
        if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
Packit 6c0a39
            ssh_set_error_oom(s->session);
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
        ssh_socket_nonblocking_flush(s);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
/** \internal
Packit 6c0a39
 * \brief starts a nonblocking flush of the output buffer
Packit 6c0a39
 *
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_nonblocking_flush(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    ssh_session session = s->session;
Packit 6c0a39
    uint32_t len;
Packit 6c0a39
Packit 6c0a39
    if (!ssh_socket_is_open(s)) {
Packit 6c0a39
        session->alive = 0;
Packit 6c0a39
        if (s->callbacks && s->callbacks->exception) {
Packit 6c0a39
            s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
Packit 6c0a39
                                    s->last_errno,
Packit 6c0a39
                                    s->callbacks->userdata);
Packit 6c0a39
        } else {
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_FATAL,
Packit 6c0a39
                          "Writing packet: error on socket (or connection "
Packit 6c0a39
                          "closed): %s",
Packit 6c0a39
                          strerror(s->last_errno));
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    len = ssh_buffer_get_len(s->out_buffer);
Packit 6c0a39
    if (!s->write_wontblock && s->poll_handle && len > 0) {
Packit 6c0a39
        /* force the poll system to catch pollout events */
Packit 6c0a39
        ssh_poll_add_events(s->poll_handle, POLLOUT);
Packit 6c0a39
Packit 6c0a39
        return SSH_AGAIN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (s->write_wontblock && len > 0) {
Packit 6c0a39
        ssize_t bwritten;
Packit 6c0a39
Packit 6c0a39
        bwritten = ssh_socket_unbuffered_write(s,
Packit 6c0a39
                                               ssh_buffer_get(s->out_buffer),
Packit 6c0a39
                                               len);
Packit 6c0a39
        if (bwritten < 0) {
Packit 6c0a39
            session->alive = 0;
Packit 6c0a39
            ssh_socket_close(s);
Packit 6c0a39
Packit 6c0a39
            if (s->callbacks && s->callbacks->exception) {
Packit 6c0a39
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
Packit 6c0a39
                                        s->last_errno,
Packit 6c0a39
                                        s->callbacks->userdata);
Packit 6c0a39
            } else {
Packit 6c0a39
                ssh_set_error(session,
Packit 6c0a39
                              SSH_FATAL,
Packit 6c0a39
                              "Writing packet: error on socket (or connection "
Packit 6c0a39
                              "closed): %s",
Packit 6c0a39
                              strerror(s->last_errno));
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        ssh_buffer_pass_bytes(s->out_buffer, bwritten);
Packit 6c0a39
        if (s->session->socket_counter != NULL) {
Packit 6c0a39
            s->session->socket_counter->out_bytes += bwritten;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Is there some data pending? */
Packit 6c0a39
    len = ssh_buffer_get_len(s->out_buffer);
Packit 6c0a39
    if (s->poll_handle && len > 0) {
Packit 6c0a39
        /* force the poll system to catch pollout events */
Packit 6c0a39
        ssh_poll_add_events(s->poll_handle, POLLOUT);
Packit 6c0a39
Packit 6c0a39
        return SSH_AGAIN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* all data written */
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
void ssh_socket_set_write_wontblock(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    s->write_wontblock = 1;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
void ssh_socket_set_read_wontblock(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    s->read_wontblock = 1;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
void ssh_socket_set_except(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    s->data_except = 1;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_socket_data_available(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    return s->read_wontblock;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_socket_data_writable(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    return s->write_wontblock;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief returns the number of outgoing bytes currently buffered
Packit 6c0a39
 * @param s the socket
Packit 6c0a39
 * @returns numbers of bytes buffered, or 0 if the socket isn't connected
Packit 6c0a39
 */
Packit 6c0a39
int ssh_socket_buffered_write_bytes(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    if (s==NULL || s->out_buffer == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return ssh_buffer_get_len(s->out_buffer);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
int ssh_socket_get_status(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    int r = 0;
Packit 6c0a39
Packit 6c0a39
    if (ssh_buffer_get_len(s->in_buffer) > 0) {
Packit 6c0a39
        r |= SSH_READ_PENDING;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (ssh_buffer_get_len(s->out_buffer) > 0) {
Packit 6c0a39
        r |= SSH_WRITE_PENDING;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (s->data_except) {
Packit 6c0a39
        r |= SSH_CLOSED_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return r;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_socket_get_poll_flags(ssh_socket s)
Packit 6c0a39
{
Packit 6c0a39
    int r = 0;
Packit 6c0a39
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLIN) > 0) {
Packit 6c0a39
        r |= SSH_READ_PENDING;
Packit 6c0a39
    }
Packit 6c0a39
    if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLOUT) > 0) {
Packit 6c0a39
        r |= SSH_WRITE_PENDING;
Packit 6c0a39
    }
Packit 6c0a39
    return r;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
int ssh_socket_set_nonblocking(socket_t fd)
Packit 6c0a39
{
Packit 6c0a39
    u_long nonblocking = 1;
Packit 6c0a39
    return ioctlsocket(fd, FIONBIO, &nonblocking);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_socket_set_blocking(socket_t fd)
Packit 6c0a39
{
Packit 6c0a39
    u_long nonblocking = 0;
Packit 6c0a39
    return ioctlsocket(fd, FIONBIO, &nonblocking);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#else /* _WIN32 */
Packit 6c0a39
int ssh_socket_set_nonblocking(socket_t fd)
Packit 6c0a39
{
Packit 6c0a39
    return fcntl(fd, F_SETFL, O_NONBLOCK);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_socket_set_blocking(socket_t fd)
Packit 6c0a39
{
Packit 6c0a39
    return fcntl(fd, F_SETFL, 0);
Packit 6c0a39
}
Packit 6c0a39
#endif /* _WIN32 */
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Launches a socket connection
Packit 6c0a39
 * If a the socket connected callback has been defined and
Packit 6c0a39
 * a poll object exists, this call will be non blocking.
Packit 6c0a39
 * @param s    socket to connect.
Packit 6c0a39
 * @param host hostname or ip address to connect to.
Packit 6c0a39
 * @param port port number to connect to.
Packit 6c0a39
 * @param bind_addr address to bind to, or NULL for default.
Packit 6c0a39
 * @returns SSH_OK socket is being connected.
Packit 6c0a39
 * @returns SSH_ERROR error while connecting to remote host.
Packit 6c0a39
 * @bug It only tries connecting to one of the available AI's
Packit 6c0a39
 * which is problematic for hosts having DNS fail-over.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
int
Packit 6c0a39
ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr)
Packit 6c0a39
{
Packit 6c0a39
    socket_t fd;
Packit 6c0a39
    
Packit 6c0a39
    if (s->state != SSH_SOCKET_NONE) {
Packit 6c0a39
        ssh_set_error(s->session, SSH_FATAL,
Packit 6c0a39
                      "ssh_socket_connect called on socket not unconnected");
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "Nonblocking connection socket: %d", fd);
Packit 6c0a39
    if (fd == SSH_INVALID_SOCKET) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    ssh_socket_set_fd(s,fd);
Packit 6c0a39
    
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief executes a command and redirect input and outputs
Packit 6c0a39
 * @param command command to execute
Packit 6c0a39
 * @param in input file descriptor
Packit 6c0a39
 * @param out output file descriptor
Packit 6c0a39
 */
Packit 6c0a39
void
Packit 6c0a39
ssh_execute_command(const char *command, socket_t in, socket_t out)
Packit 6c0a39
{
Packit 6c0a39
    const char *args[] = {"/bin/sh", "-c", command, NULL};
Packit 6c0a39
    /* Prepare /dev/null socket for the stderr redirection */
Packit 6c0a39
    int devnull = open("/dev/null", O_WRONLY);
Packit 6c0a39
    if (devnull == -1) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARNING, "Failed to open stderr");
Packit 6c0a39
        exit(1);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* redirect in and out to stdin, stdout */
Packit 6c0a39
    dup2(in, 0);
Packit 6c0a39
    dup2(out, 1);
Packit 6c0a39
    /* Ignore anything on the stderr */
Packit 6c0a39
    dup2(devnull, STDERR_FILENO);
Packit 6c0a39
    close(in);
Packit 6c0a39
    close(out);
Packit 6c0a39
    execv(args[0], (char * const *)args);
Packit 6c0a39
    exit(1);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Open a socket on a ProxyCommand
Packit 6c0a39
 * This call will always be nonblocking.
Packit 6c0a39
 * @param s    socket to connect.
Packit 6c0a39
 * @param command Command to execute.
Packit 6c0a39
 * @returns SSH_OK socket is being connected.
Packit 6c0a39
 * @returns SSH_ERROR error while executing the command.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
int
Packit 6c0a39
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
Packit 6c0a39
{
Packit 6c0a39
    socket_t pair[2];
Packit 6c0a39
    int pid;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (s->state != SSH_SOCKET_NONE) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "Executing proxycommand '%s'", command);
Packit 6c0a39
    pid = fork();
Packit 6c0a39
    if(pid == 0) {
Packit 6c0a39
        ssh_execute_command(command, pair[0], pair[0]);
Packit 6c0a39
    }
Packit 6c0a39
    close(pair[0]);
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
Packit 6c0a39
    ssh_socket_set_fd(s, pair[1]);
Packit 6c0a39
    s->state=SSH_SOCKET_CONNECTED;
Packit 6c0a39
    s->fd_is_socket=0;
Packit 6c0a39
    /* POLLOUT is the event to wait for in a nonblocking connect */
Packit 6c0a39
    ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLIN | POLLOUT);
Packit 6c0a39
    if (s->callbacks && s->callbacks->connected) {
Packit 6c0a39
        s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#endif /* _WIN32 */
Packit 6c0a39
/** @} */