1610 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1610 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
 | 
						|
 * Copyright (c) 2009 by Daniel Stenberg
 | 
						|
 * Copyright (c) 2010 Simon Josefsson <simon@josefsson.org>
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms,
 | 
						|
 * with or without modification, are permitted provided
 | 
						|
 * that the following conditions are met:
 | 
						|
 *
 | 
						|
 *   Redistributions of source code must retain the above
 | 
						|
 *   copyright notice, this list of conditions and the
 | 
						|
 *   following disclaimer.
 | 
						|
 *
 | 
						|
 *   Redistributions in binary form must reproduce the above
 | 
						|
 *   copyright notice, this list of conditions and the following
 | 
						|
 *   disclaimer in the documentation and/or other materials
 | 
						|
 *   provided with the distribution.
 | 
						|
 *
 | 
						|
 *   Neither the name of the copyright holder nor the names
 | 
						|
 *   of any other contributors may be used to endorse or
 | 
						|
 *   promote products derived from this software without
 | 
						|
 *   specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
						|
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 | 
						|
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
						|
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
						|
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
						|
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | 
						|
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | 
						|
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
						|
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | 
						|
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
						|
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 | 
						|
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 | 
						|
 * OF SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include "libssh2_priv.h"
 | 
						|
#include <errno.h>
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
#include <stdlib.h>
 | 
						|
#include <fcntl.h>
 | 
						|
 | 
						|
#ifdef HAVE_GETTIMEOFDAY
 | 
						|
#include <sys/time.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_ALLOCA_H
 | 
						|
#include <alloca.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include "transport.h"
 | 
						|
 | 
						|
/* libssh2_default_alloc
 | 
						|
 */
 | 
						|
static
 | 
						|
LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
 | 
						|
{
 | 
						|
    (void) abstract;
 | 
						|
    return malloc(count);
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_default_free
 | 
						|
 */
 | 
						|
static
 | 
						|
LIBSSH2_FREE_FUNC(libssh2_default_free)
 | 
						|
{
 | 
						|
    (void) abstract;
 | 
						|
    free(ptr);
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_default_realloc
 | 
						|
 */
 | 
						|
static
 | 
						|
LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
 | 
						|
{
 | 
						|
    (void) abstract;
 | 
						|
    return realloc(ptr, count);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * banner_receive
 | 
						|
 *
 | 
						|
 * Wait for a hello from the remote host
 | 
						|
 * Allocate a buffer and store the banner in session->remote.banner
 | 
						|
 * Returns: 0 on success, PACKET_EAGAIN if read would block, negative on failure
 | 
						|
 */
 | 
						|
static int
 | 
						|
banner_receive(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
    int banner_len;
 | 
						|
 | 
						|
    if (session->banner_TxRx_state == libssh2_NB_state_idle) {
 | 
						|
        banner_len = 0;
 | 
						|
 | 
						|
        session->banner_TxRx_state = libssh2_NB_state_created;
 | 
						|
    } else {
 | 
						|
        banner_len = session->banner_TxRx_total_send;
 | 
						|
    }
 | 
						|
 | 
						|
    while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) &&
 | 
						|
           ((banner_len == 0)
 | 
						|
            || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) {
 | 
						|
        char c = '\0';
 | 
						|
 | 
						|
        /* no incoming block yet! */
 | 
						|
        session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
 | 
						|
 | 
						|
        ret = _libssh2_recv(session->socket_fd, &c, 1,
 | 
						|
                            LIBSSH2_SOCKET_RECV_FLAGS(session));
 | 
						|
        if (ret < 0)
 | 
						|
            _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
 | 
						|
                           "Error recving %d bytes to %p: %d", 1, &c, errno);
 | 
						|
        else
 | 
						|
            _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
 | 
						|
                           "Recved %d bytes to %p", ret, &c);
 | 
						|
 | 
						|
        if (ret < 0) {
 | 
						|
            if (errno == EAGAIN) {
 | 
						|
                session->socket_block_directions =
 | 
						|
                    LIBSSH2_SESSION_BLOCK_INBOUND;
 | 
						|
                session->banner_TxRx_total_send = banner_len;
 | 
						|
                return PACKET_EAGAIN;
 | 
						|
            }
 | 
						|
 | 
						|
            /* Some kinda error */
 | 
						|
            session->banner_TxRx_state = libssh2_NB_state_idle;
 | 
						|
            session->banner_TxRx_total_send = 0;
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        if (ret == 0) {
 | 
						|
            session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
 | 
						|
            return PACKET_FAIL;
 | 
						|
        }
 | 
						|
 | 
						|
        if (c == '\0') {
 | 
						|
            /* NULLs are not allowed in SSH banners */
 | 
						|
            session->banner_TxRx_state = libssh2_NB_state_idle;
 | 
						|
            session->banner_TxRx_total_send = 0;
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        session->banner_TxRx_banner[banner_len++] = c;
 | 
						|
    }
 | 
						|
 | 
						|
    while (banner_len &&
 | 
						|
           ((session->banner_TxRx_banner[banner_len - 1] == '\n') ||
 | 
						|
            (session->banner_TxRx_banner[banner_len - 1] == '\r'))) {
 | 
						|
        banner_len--;
 | 
						|
    }
 | 
						|
 | 
						|
    /* From this point on, we are done here */
 | 
						|
    session->banner_TxRx_state = libssh2_NB_state_idle;
 | 
						|
    session->banner_TxRx_total_send = 0;
 | 
						|
 | 
						|
    if (!banner_len)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
 | 
						|
    if (!session->remote.banner) {
 | 
						|
        return libssh2_error(session, LIBSSH2_ERROR_ALLOC,
 | 
						|
                             "Error allocating space for remote banner");
 | 
						|
    }
 | 
						|
    memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len);
 | 
						|
    session->remote.banner[banner_len] = '\0';
 | 
						|
    _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Received Banner: %s",
 | 
						|
                   session->remote.banner);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * banner_send
 | 
						|
 *
 | 
						|
 * Send the default banner, or the one set via libssh2_setopt_string
 | 
						|
 *
 | 
						|
 * Returns PACKET_EAGAIN if it would block - and if it does so, you should
 | 
						|
 * call this function again as soon as it is likely that more data can be
 | 
						|
 * sent, and this function should then be called with the same argument set
 | 
						|
 * (same data pointer and same data_len) until zero or failure is returned.
 | 
						|
 */
 | 
						|
static int
 | 
						|
banner_send(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
 | 
						|
    int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
 | 
						|
    ssize_t ret;
 | 
						|
#ifdef LIBSSH2DEBUG
 | 
						|
    char banner_dup[256];
 | 
						|
#endif
 | 
						|
 | 
						|
    if (session->banner_TxRx_state == libssh2_NB_state_idle) {
 | 
						|
        if (session->local.banner) {
 | 
						|
            /* setopt_string will have given us our \r\n characters */
 | 
						|
            banner_len = strlen((char *) session->local.banner);
 | 
						|
            banner = (char *) session->local.banner;
 | 
						|
        }
 | 
						|
#ifdef LIBSSH2DEBUG
 | 
						|
        /* Hack and slash to avoid sending CRLF in debug output */
 | 
						|
        if (banner_len < 256) {
 | 
						|
            memcpy(banner_dup, banner, banner_len - 2);
 | 
						|
            banner_dup[banner_len - 2] = '\0';
 | 
						|
        } else {
 | 
						|
            memcpy(banner_dup, banner, 255);
 | 
						|
            banner[255] = '\0';
 | 
						|
        }
 | 
						|
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Sending Banner: %s",
 | 
						|
                       banner_dup);
 | 
						|
#endif
 | 
						|
 | 
						|
        session->banner_TxRx_state = libssh2_NB_state_created;
 | 
						|
    }
 | 
						|
 | 
						|
    /* no outgoing block yet! */
 | 
						|
    session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND;
 | 
						|
 | 
						|
    ret = _libssh2_send(session->socket_fd,
 | 
						|
                        banner + session->banner_TxRx_total_send,
 | 
						|
                        banner_len - session->banner_TxRx_total_send,
 | 
						|
                        LIBSSH2_SOCKET_SEND_FLAGS(session));
 | 
						|
    if (ret < 0)
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
 | 
						|
                       "Error sending %d bytes: %d",
 | 
						|
                       banner_len - session->banner_TxRx_total_send, errno);
 | 
						|
    else
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
 | 
						|
                       "Sent %d/%d bytes at %p+%d", ret,
 | 
						|
                       banner_len - session->banner_TxRx_total_send,
 | 
						|
                       banner, session->banner_TxRx_total_send);
 | 
						|
 | 
						|
    if (ret != (banner_len - session->banner_TxRx_total_send)) {
 | 
						|
        if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) {
 | 
						|
            /* the whole packet could not be sent, save the what was */
 | 
						|
            session->socket_block_directions =
 | 
						|
                LIBSSH2_SESSION_BLOCK_OUTBOUND;
 | 
						|
            session->banner_TxRx_total_send += ret;
 | 
						|
            return PACKET_EAGAIN;
 | 
						|
        }
 | 
						|
        session->banner_TxRx_state = libssh2_NB_state_idle;
 | 
						|
        session->banner_TxRx_total_send = 0;
 | 
						|
        return PACKET_FAIL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Set the state back to idle */
 | 
						|
    session->banner_TxRx_state = libssh2_NB_state_idle;
 | 
						|
    session->banner_TxRx_total_send = 0;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * session_nonblock() sets the given socket to either blocking or
 | 
						|
 * non-blocking mode based on the 'nonblock' boolean argument. This function
 | 
						|
 * is copied from the libcurl sources with permission.
 | 
						|
 */
 | 
						|
static int
 | 
						|
session_nonblock(libssh2_socket_t sockfd,   /* operate on this */
 | 
						|
                 int nonblock /* TRUE or FALSE */ )
 | 
						|
{
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 0
 | 
						|
#ifdef HAVE_O_NONBLOCK
 | 
						|
    /* most recent unix versions */
 | 
						|
    int flags;
 | 
						|
 | 
						|
    flags = fcntl(sockfd, F_GETFL, 0);
 | 
						|
    if (nonblock)
 | 
						|
        return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
 | 
						|
    else
 | 
						|
        return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 1
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_FIONBIO) && (SETBLOCK == 0)
 | 
						|
    /* older unix versions */
 | 
						|
    int flags;
 | 
						|
 | 
						|
    flags = nonblock;
 | 
						|
    return ioctl(sockfd, FIONBIO, &flags);
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 2
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0)
 | 
						|
    /* Windows? */
 | 
						|
    unsigned long flags;
 | 
						|
    flags = nonblock;
 | 
						|
 | 
						|
    return ioctlsocket(sockfd, FIONBIO, &flags);
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 3
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0)
 | 
						|
    /* presumably for Amiga */
 | 
						|
    return IoctlSocket(sockfd, FIONBIO, (long) nonblock);
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 4
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0)
 | 
						|
    /* BeOS */
 | 
						|
    long b = nonblock ? 1 : 0;
 | 
						|
    return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 5
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_DISABLED_NONBLOCKING
 | 
						|
    return 0;                   /* returns success */
 | 
						|
#undef SETBLOCK
 | 
						|
#define SETBLOCK 6
 | 
						|
#endif
 | 
						|
 | 
						|
#if (SETBLOCK == 0)
 | 
						|
#error "no non-blocking method was found/used/set"
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * get_socket_nonblocking()
 | 
						|
 *
 | 
						|
 * gets the given blocking or non-blocking state of the socket.
 | 
						|
 */
 | 
						|
static int
 | 
						|
get_socket_nonblocking(int sockfd)
 | 
						|
{                               /* operate on this */
 | 
						|
#undef GETBLOCK
 | 
						|
#define GETBLOCK 0
 | 
						|
#ifdef HAVE_O_NONBLOCK
 | 
						|
    /* most recent unix versions */
 | 
						|
    int flags;
 | 
						|
 | 
						|
    if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
 | 
						|
        /* Assume blocking on error */
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    return (flags & O_NONBLOCK);
 | 
						|
#undef GETBLOCK
 | 
						|
#define GETBLOCK 1
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(WSAEWOULDBLOCK) && (GETBLOCK == 0)
 | 
						|
    /* Windows? */
 | 
						|
    unsigned int option_value;
 | 
						|
    socklen_t option_len = sizeof(option_value);
 | 
						|
 | 
						|
    if (getsockopt
 | 
						|
        (sockfd, SOL_SOCKET, SO_ERROR, (void *) &option_value, &option_len)) {
 | 
						|
        /* Assume blocking on error */
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    return (int) option_value;
 | 
						|
#undef GETBLOCK
 | 
						|
#define GETBLOCK 2
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0)
 | 
						|
    /* BeOS */
 | 
						|
    long b;
 | 
						|
    if (getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) {
 | 
						|
        /* Assume blocking on error */
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    return (int) b;
 | 
						|
#undef GETBLOCK
 | 
						|
#define GETBLOCK 5
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_DISABLED_NONBLOCKING
 | 
						|
    return 1;                   /* returns blocking */
 | 
						|
#undef GETBLOCK
 | 
						|
#define GETBLOCK 6
 | 
						|
#endif
 | 
						|
 | 
						|
#if (GETBLOCK == 0)
 | 
						|
#error "no non-blocking method was found/used/get"
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_banner_set
 | 
						|
 * Set the local banner
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner)
 | 
						|
{
 | 
						|
    int banner_len = banner ? strlen(banner) : 0;
 | 
						|
 | 
						|
    if (session->local.banner) {
 | 
						|
        LIBSSH2_FREE(session, session->local.banner);
 | 
						|
        session->local.banner = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!banner_len) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3);
 | 
						|
    if (!session->local.banner) {
 | 
						|
        return libssh2_error(session, LIBSSH2_ERROR_ALLOC,
 | 
						|
                             "Unable to allocate memory for local banner");
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(session->local.banner, banner, banner_len);
 | 
						|
    session->local.banner[banner_len] = '\0';
 | 
						|
    _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting local Banner: %s",
 | 
						|
                   session->local.banner);
 | 
						|
    session->local.banner[banner_len++] = '\r';
 | 
						|
    session->local.banner[banner_len++] = '\n';
 | 
						|
    session->local.banner[banner_len++] = '\0';
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_init_ex
 | 
						|
 *
 | 
						|
 * Allocate and initialize a libssh2 session structure. Allows for malloc
 | 
						|
 * callbacks in case the calling program has its own memory manager It's
 | 
						|
 * allowable (but unadvisable) to define some but not all of the malloc
 | 
						|
 * callbacks An additional pointer value may be optionally passed to be sent
 | 
						|
 * to the callbacks (so they know who's asking)
 | 
						|
 */
 | 
						|
LIBSSH2_API LIBSSH2_SESSION *
 | 
						|
libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
 | 
						|
                        LIBSSH2_FREE_FUNC((*my_free)),
 | 
						|
                        LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract)
 | 
						|
{
 | 
						|
    LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
 | 
						|
    LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
 | 
						|
    LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
 | 
						|
    LIBSSH2_SESSION *session;
 | 
						|
 | 
						|
    if (my_alloc) {
 | 
						|
        local_alloc = my_alloc;
 | 
						|
    }
 | 
						|
    if (my_free) {
 | 
						|
        local_free = my_free;
 | 
						|
    }
 | 
						|
    if (my_realloc) {
 | 
						|
        local_realloc = my_realloc;
 | 
						|
    }
 | 
						|
 | 
						|
    session = local_alloc(sizeof(LIBSSH2_SESSION), &abstract);
 | 
						|
    if (session) {
 | 
						|
        memset(session, 0, sizeof(LIBSSH2_SESSION));
 | 
						|
        session->alloc = local_alloc;
 | 
						|
        session->free = local_free;
 | 
						|
        session->realloc = local_realloc;
 | 
						|
        session->abstract = abstract;
 | 
						|
        session->api_block_mode = 1; /* blocking API by default */
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
 | 
						|
                       "New session resource allocated");
 | 
						|
        libssh2_crypto_init();
 | 
						|
    }
 | 
						|
    return session;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_callback_set
 | 
						|
 *
 | 
						|
 * Set (or reset) a callback function
 | 
						|
 * Returns the prior address
 | 
						|
 *
 | 
						|
 * FIXME: this function relies on that we can typecast function pointers
 | 
						|
 * to void pointers, which isn't allowed in ISO C!
 | 
						|
 */
 | 
						|
LIBSSH2_API void *
 | 
						|
libssh2_session_callback_set(LIBSSH2_SESSION * session,
 | 
						|
                             int cbtype, void *callback)
 | 
						|
{
 | 
						|
    void *oldcb;
 | 
						|
 | 
						|
    switch (cbtype) {
 | 
						|
    case LIBSSH2_CALLBACK_IGNORE:
 | 
						|
        oldcb = session->ssh_msg_ignore;
 | 
						|
        session->ssh_msg_ignore = callback;
 | 
						|
        return oldcb;
 | 
						|
 | 
						|
    case LIBSSH2_CALLBACK_DEBUG:
 | 
						|
        oldcb = session->ssh_msg_debug;
 | 
						|
        session->ssh_msg_debug = callback;
 | 
						|
        return oldcb;
 | 
						|
 | 
						|
    case LIBSSH2_CALLBACK_DISCONNECT:
 | 
						|
        oldcb = session->ssh_msg_disconnect;
 | 
						|
        session->ssh_msg_disconnect = callback;
 | 
						|
        return oldcb;
 | 
						|
 | 
						|
    case LIBSSH2_CALLBACK_MACERROR:
 | 
						|
        oldcb = session->macerror;
 | 
						|
        session->macerror = callback;
 | 
						|
        return oldcb;
 | 
						|
 | 
						|
    case LIBSSH2_CALLBACK_X11:
 | 
						|
        oldcb = session->x11;
 | 
						|
        session->x11 = callback;
 | 
						|
        return oldcb;
 | 
						|
 | 
						|
    }
 | 
						|
    _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting Callback %d", cbtype);
 | 
						|
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * _libssh2_wait_socket()
 | 
						|
 *
 | 
						|
 * Utility function that waits for action on the socket. Returns 0 when ready
 | 
						|
 * to run again or error on timeout.
 | 
						|
 */
 | 
						|
int _libssh2_wait_socket(LIBSSH2_SESSION *session)
 | 
						|
{
 | 
						|
    fd_set fd;
 | 
						|
    fd_set *writefd = NULL;
 | 
						|
    fd_set *readfd = NULL;
 | 
						|
    int dir;
 | 
						|
    int rc;
 | 
						|
    struct timeval tv;
 | 
						|
    int seconds_to_next;
 | 
						|
 | 
						|
    rc = libssh2_keepalive_send (session, &seconds_to_next);
 | 
						|
    if (rc < 0)
 | 
						|
        return rc;
 | 
						|
 | 
						|
    tv.tv_sec = seconds_to_next;
 | 
						|
    tv.tv_usec = 0;
 | 
						|
 | 
						|
    FD_ZERO(&fd);
 | 
						|
    FD_SET(session->socket_fd, &fd);
 | 
						|
 | 
						|
    /* now make sure we wait in the correct direction */
 | 
						|
    dir = libssh2_session_block_directions(session);
 | 
						|
 | 
						|
    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
 | 
						|
        readfd = &fd;
 | 
						|
 | 
						|
    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
 | 
						|
        writefd = &fd;
 | 
						|
 | 
						|
    /* Note that this COULD be made to use a timeout that perhaps could be
 | 
						|
       customizable by the app or something... */
 | 
						|
    rc = select(session->socket_fd + 1, readfd, writefd, NULL,
 | 
						|
                seconds_to_next ? &tv : NULL);
 | 
						|
 | 
						|
    if(rc <= 0) {
 | 
						|
        /* timeout (or error), bail out with a timeout error */
 | 
						|
        session->err_code = LIBSSH2_ERROR_TIMEOUT;
 | 
						|
        return LIBSSH2_ERROR_TIMEOUT;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0; /* ready to try again */
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_idle) {
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
 | 
						|
                       "session_startup for socket %d", sock);
 | 
						|
        if (INVALID_SOCKET == sock) {
 | 
						|
            /* Did we forget something? */
 | 
						|
            return libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE,
 | 
						|
                                 "Bad socket provided");
 | 
						|
        }
 | 
						|
        session->socket_fd = sock;
 | 
						|
 | 
						|
        session->socket_prev_blockstate =
 | 
						|
            !get_socket_nonblocking(session->socket_fd);
 | 
						|
 | 
						|
        if (session->socket_prev_blockstate) {
 | 
						|
            /* If in blocking state chang to non-blocking */
 | 
						|
            session_nonblock(session->socket_fd, 1);
 | 
						|
        }
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_created;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_created) {
 | 
						|
        rc = banner_send(session);
 | 
						|
        if (rc) {
 | 
						|
            return libssh2_error(session, rc,
 | 
						|
                                 "Failed sending banner");
 | 
						|
        }
 | 
						|
        session->startup_state = libssh2_NB_state_sent;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_sent) {
 | 
						|
        rc = banner_receive(session);
 | 
						|
        if (rc) {
 | 
						|
            return libssh2_error(session, rc,
 | 
						|
                                 "Failed getting banner");
 | 
						|
        }
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_sent1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_sent1) {
 | 
						|
        rc = libssh2_kex_exchange(session, 0, &session->startup_key_state);
 | 
						|
        if (rc) {
 | 
						|
            return libssh2_error(session, rc,
 | 
						|
                                 "Unable to exchange encryption keys");
 | 
						|
        }
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_sent2;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_sent2) {
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
 | 
						|
                       "Requesting userauth service");
 | 
						|
 | 
						|
        /* Request the userauth service */
 | 
						|
        session->startup_service[0] = SSH_MSG_SERVICE_REQUEST;
 | 
						|
        _libssh2_htonu32(session->startup_service + 1,
 | 
						|
                        sizeof("ssh-userauth") - 1);
 | 
						|
        memcpy(session->startup_service + 5, "ssh-userauth",
 | 
						|
               sizeof("ssh-userauth") - 1);
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_sent3;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_sent3) {
 | 
						|
        rc = _libssh2_transport_write(session, session->startup_service,
 | 
						|
                                      sizeof("ssh-userauth") + 5 - 1);
 | 
						|
        if (rc) {
 | 
						|
            return libssh2_error(session, rc,
 | 
						|
                                 "Unable to ask for ssh-userauth service");
 | 
						|
        }
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_sent4;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->startup_state == libssh2_NB_state_sent4) {
 | 
						|
        rc = _libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT,
 | 
						|
                                     &session->startup_data,
 | 
						|
                                     &session->startup_data_len, 0, NULL, 0,
 | 
						|
                                     &session->startup_req_state);
 | 
						|
        if (rc)
 | 
						|
            return rc;
 | 
						|
 | 
						|
        session->startup_service_length =
 | 
						|
            _libssh2_ntohu32(session->startup_data + 1);
 | 
						|
 | 
						|
        if ((session->startup_service_length != (sizeof("ssh-userauth") - 1))
 | 
						|
            || strncmp("ssh-userauth", (char *) session->startup_data + 5,
 | 
						|
                       session->startup_service_length)) {
 | 
						|
            LIBSSH2_FREE(session, session->startup_data);
 | 
						|
            session->startup_data = NULL;
 | 
						|
            return libssh2_error(session, LIBSSH2_ERROR_PROTO,
 | 
						|
                                 "Invalid response received from server");
 | 
						|
        }
 | 
						|
        LIBSSH2_FREE(session, session->startup_data);
 | 
						|
        session->startup_data = NULL;
 | 
						|
 | 
						|
        session->startup_state = libssh2_NB_state_idle;
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /* just for safety return some error */
 | 
						|
    return LIBSSH2_ERROR_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * proto libssh2_session_startup
 | 
						|
 *
 | 
						|
 * session: LIBSSH2_SESSION struct allocated and owned by the calling program
 | 
						|
 * Returns: 0 on success, or non-zero on failure
 | 
						|
 * Any memory allocated by libssh2 will use alloc/realloc/free
 | 
						|
 * callbacks in session.
 | 
						|
 * The 'sock' socket *must* be populated with an opened and connected socket.
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_startup(LIBSSH2_SESSION *session, int sock)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    BLOCK_ADJUST(rc, session, session_startup(session, sock) );
 | 
						|
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_free
 | 
						|
 *
 | 
						|
 * Frees the memory allocated to the session
 | 
						|
 * Also closes and frees any channels attached to this session
 | 
						|
 */
 | 
						|
static int
 | 
						|
session_free(LIBSSH2_SESSION *session)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
    LIBSSH2_PACKET *pkg;
 | 
						|
    LIBSSH2_CHANNEL *ch;
 | 
						|
    LIBSSH2_LISTENER *l;
 | 
						|
    struct transportpacket *p = &session->packet;
 | 
						|
 | 
						|
    if (session->free_state == libssh2_NB_state_idle) {
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Freeing session resource",
 | 
						|
                       session->remote.banner);
 | 
						|
 | 
						|
        session->state = libssh2_NB_state_created;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->free_state == libssh2_NB_state_created) {
 | 
						|
        while ((ch = _libssh2_list_first(&session->channels))) {
 | 
						|
 | 
						|
            rc = libssh2_channel_free(ch);
 | 
						|
            if (rc == PACKET_EAGAIN)
 | 
						|
                return rc;
 | 
						|
#if 0
 | 
						|
            /* Daniel's note: I'm leaving this code here right now since it
 | 
						|
               looks so weird I'm stumped. Why would libssh2_channel_free()
 | 
						|
               fail and forces this to be done? */
 | 
						|
            if (tmp == session->channels.head) {
 | 
						|
                /* channel_free couldn't do it's job, perform a messy cleanup */
 | 
						|
                tmp = session->channels.head;
 | 
						|
 | 
						|
                /* unlink */
 | 
						|
                session->channels.head = tmp->next;
 | 
						|
 | 
						|
                /* free */
 | 
						|
                LIBSSH2_FREE(session, tmp);
 | 
						|
 | 
						|
                /* reverse linking isn't important here, we're killing the
 | 
						|
                 * structure */
 | 
						|
            }
 | 
						|
#endif
 | 
						|
        }
 | 
						|
 | 
						|
        session->state = libssh2_NB_state_sent;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->state == libssh2_NB_state_sent) {
 | 
						|
        while ((l = _libssh2_list_first(&session->listeners))) {
 | 
						|
            rc = libssh2_channel_forward_cancel(l);
 | 
						|
            if (rc == PACKET_EAGAIN)
 | 
						|
                return rc;
 | 
						|
        }
 | 
						|
 | 
						|
        session->state = libssh2_NB_state_sent1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->state & LIBSSH2_STATE_NEWKEYS) {
 | 
						|
        /* hostkey */
 | 
						|
        if (session->hostkey && session->hostkey->dtor) {
 | 
						|
            session->hostkey->dtor(session, &session->server_hostkey_abstract);
 | 
						|
        }
 | 
						|
 | 
						|
        /* Client to Server */
 | 
						|
        /* crypt */
 | 
						|
        if (session->local.crypt && session->local.crypt->dtor) {
 | 
						|
            session->local.crypt->dtor(session,
 | 
						|
                                       &session->local.crypt_abstract);
 | 
						|
        }
 | 
						|
        /* comp */
 | 
						|
        if (session->local.comp && session->local.comp->dtor) {
 | 
						|
            session->local.comp->dtor(session, 1,
 | 
						|
                                      &session->local.comp_abstract);
 | 
						|
        }
 | 
						|
        /* mac */
 | 
						|
        if (session->local.mac && session->local.mac->dtor) {
 | 
						|
            session->local.mac->dtor(session, &session->local.mac_abstract);
 | 
						|
        }
 | 
						|
 | 
						|
        /* Server to Client */
 | 
						|
        /* crypt */
 | 
						|
        if (session->remote.crypt && session->remote.crypt->dtor) {
 | 
						|
            session->remote.crypt->dtor(session,
 | 
						|
                                        &session->remote.crypt_abstract);
 | 
						|
        }
 | 
						|
        /* comp */
 | 
						|
        if (session->remote.comp && session->remote.comp->dtor) {
 | 
						|
            session->remote.comp->dtor(session, 0,
 | 
						|
                                       &session->remote.comp_abstract);
 | 
						|
        }
 | 
						|
        /* mac */
 | 
						|
        if (session->remote.mac && session->remote.mac->dtor) {
 | 
						|
            session->remote.mac->dtor(session, &session->remote.mac_abstract);
 | 
						|
        }
 | 
						|
 | 
						|
        /* session_id */
 | 
						|
        if (session->session_id) {
 | 
						|
            LIBSSH2_FREE(session, session->session_id);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Free banner(s) */
 | 
						|
    if (session->remote.banner) {
 | 
						|
        LIBSSH2_FREE(session, session->remote.banner);
 | 
						|
    }
 | 
						|
    if (session->local.banner) {
 | 
						|
        LIBSSH2_FREE(session, session->local.banner);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Free preference(s) */
 | 
						|
    if (session->kex_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->kex_prefs);
 | 
						|
    }
 | 
						|
    if (session->hostkey_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->hostkey_prefs);
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->local.crypt_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->local.crypt_prefs);
 | 
						|
    }
 | 
						|
    if (session->local.mac_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->local.mac_prefs);
 | 
						|
    }
 | 
						|
    if (session->local.comp_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->local.comp_prefs);
 | 
						|
    }
 | 
						|
    if (session->local.lang_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->local.lang_prefs);
 | 
						|
    }
 | 
						|
 | 
						|
    if (session->remote.crypt_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->remote.crypt_prefs);
 | 
						|
    }
 | 
						|
    if (session->remote.mac_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->remote.mac_prefs);
 | 
						|
    }
 | 
						|
    if (session->remote.comp_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->remote.comp_prefs);
 | 
						|
    }
 | 
						|
    if (session->remote.lang_prefs) {
 | 
						|
        LIBSSH2_FREE(session, session->remote.lang_prefs);
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Make sure all memory used in the state variables are free
 | 
						|
     */
 | 
						|
    if (session->startup_data) {
 | 
						|
        LIBSSH2_FREE(session, session->startup_data);
 | 
						|
    }
 | 
						|
    if (session->disconnect_data) {
 | 
						|
        LIBSSH2_FREE(session, session->disconnect_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_list_data) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_list_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_pswd_data) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_pswd_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_pswd_newpw) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_pswd_newpw);
 | 
						|
    }
 | 
						|
    if (session->userauth_host_packet) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_host_packet);
 | 
						|
    }
 | 
						|
    if (session->userauth_host_method) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_host_method);
 | 
						|
    }
 | 
						|
    if (session->userauth_host_data) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_host_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_pblc_data) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_pblc_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_pblc_packet) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_pblc_packet);
 | 
						|
    }
 | 
						|
    if (session->userauth_pblc_method) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_pblc_method);
 | 
						|
    }
 | 
						|
    if (session->userauth_kybd_data) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_kybd_data);
 | 
						|
    }
 | 
						|
    if (session->userauth_kybd_packet) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_kybd_packet);
 | 
						|
    }
 | 
						|
    if (session->userauth_kybd_auth_instruction) {
 | 
						|
        LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
 | 
						|
    }
 | 
						|
    if (session->open_packet) {
 | 
						|
        LIBSSH2_FREE(session, session->open_packet);
 | 
						|
    }
 | 
						|
    if (session->open_data) {
 | 
						|
        LIBSSH2_FREE(session, session->open_data);
 | 
						|
    }
 | 
						|
    if (session->direct_message) {
 | 
						|
        LIBSSH2_FREE(session, session->direct_message);
 | 
						|
    }
 | 
						|
    if (session->fwdLstn_packet) {
 | 
						|
        LIBSSH2_FREE(session, session->fwdLstn_packet);
 | 
						|
    }
 | 
						|
    if (session->pkeyInit_data) {
 | 
						|
        LIBSSH2_FREE(session, session->pkeyInit_data);
 | 
						|
    }
 | 
						|
    if (session->scpRecv_command) {
 | 
						|
        LIBSSH2_FREE(session, session->scpRecv_command);
 | 
						|
    }
 | 
						|
    if (session->scpSend_command) {
 | 
						|
        LIBSSH2_FREE(session, session->scpSend_command);
 | 
						|
    }
 | 
						|
    if (session->scpRecv_err_msg) {
 | 
						|
        LIBSSH2_FREE(session, session->scpRecv_err_msg);
 | 
						|
    }
 | 
						|
    if (session->scpSend_err_msg) {
 | 
						|
        LIBSSH2_FREE(session, session->scpSend_err_msg);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Cleanup all remaining packets */
 | 
						|
    while ((pkg = _libssh2_list_first(&session->packets))) {
 | 
						|
        /* unlink the node */
 | 
						|
        _libssh2_list_remove(&pkg->node);
 | 
						|
 | 
						|
        /* free */
 | 
						|
        LIBSSH2_FREE(session, pkg->data);
 | 
						|
        LIBSSH2_FREE(session, pkg);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Cleanup remaining outgoing packet buffer */
 | 
						|
    if (p->outbuf) {
 | 
						|
        LIBSSH2_FREE(session, p->outbuf);
 | 
						|
    }
 | 
						|
 | 
						|
    if(session->socket_prev_blockstate)
 | 
						|
        /* if the socket was previously blocking, put it back so */
 | 
						|
        session_nonblock(session->socket_fd, 0);
 | 
						|
 | 
						|
    if (session->server_hostkey) {
 | 
						|
        LIBSSH2_FREE(session, session->server_hostkey);
 | 
						|
    }
 | 
						|
 | 
						|
    LIBSSH2_FREE(session, session);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_free
 | 
						|
 *
 | 
						|
 * Frees the memory allocated to the session
 | 
						|
 * Also closes and frees any channels attached to this session
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_free(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    BLOCK_ADJUST(rc, session, session_free(session) );
 | 
						|
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_disconnect_ex
 | 
						|
 */
 | 
						|
static int
 | 
						|
session_disconnect(LIBSSH2_SESSION *session, int reason,
 | 
						|
                   const char *description, const char *lang)
 | 
						|
{
 | 
						|
    unsigned char *s;
 | 
						|
    unsigned long descr_len = 0, lang_len = 0;
 | 
						|
    int rc;
 | 
						|
 | 
						|
    if (session->disconnect_state == libssh2_NB_state_idle) {
 | 
						|
        _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
 | 
						|
                       "Disconnecting: reason=%d, desc=%s, lang=%s", reason,
 | 
						|
                       description, lang);
 | 
						|
        if (description) {
 | 
						|
            descr_len = strlen(description);
 | 
						|
        }
 | 
						|
        if (lang) {
 | 
						|
            lang_len = strlen(lang);
 | 
						|
        }
 | 
						|
        /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */
 | 
						|
        session->disconnect_data_len = descr_len + lang_len + 13;
 | 
						|
 | 
						|
        s = session->disconnect_data =
 | 
						|
            LIBSSH2_ALLOC(session, session->disconnect_data_len);
 | 
						|
        if (!session->disconnect_data) {
 | 
						|
            session->disconnect_state = libssh2_NB_state_idle;
 | 
						|
            return libssh2_error(session, LIBSSH2_ERROR_ALLOC,
 | 
						|
                                 "Unable to allocate memory for "
 | 
						|
                                 "disconnect packet");
 | 
						|
        }
 | 
						|
 | 
						|
        *(s++) = SSH_MSG_DISCONNECT;
 | 
						|
        _libssh2_htonu32(s, reason);
 | 
						|
        s += 4;
 | 
						|
 | 
						|
        _libssh2_htonu32(s, descr_len);
 | 
						|
        s += 4;
 | 
						|
        if (description) {
 | 
						|
            memcpy(s, description, descr_len);
 | 
						|
            s += descr_len;
 | 
						|
        }
 | 
						|
 | 
						|
        _libssh2_htonu32(s, lang_len);
 | 
						|
        s += 4;
 | 
						|
        if (lang) {
 | 
						|
            memcpy(s, lang, lang_len);
 | 
						|
            s += lang_len;
 | 
						|
        }
 | 
						|
 | 
						|
        session->disconnect_state = libssh2_NB_state_created;
 | 
						|
    }
 | 
						|
 | 
						|
    rc = _libssh2_transport_write(session, session->disconnect_data,
 | 
						|
                                  session->disconnect_data_len);
 | 
						|
    if (rc == PACKET_EAGAIN) {
 | 
						|
        return rc;
 | 
						|
    }
 | 
						|
 | 
						|
    LIBSSH2_FREE(session, session->disconnect_data);
 | 
						|
    session->disconnect_data = NULL;
 | 
						|
    session->disconnect_state = libssh2_NB_state_idle;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_disconnect_ex
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason,
 | 
						|
                              const char *desc, const char *lang)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    BLOCK_ADJUST(rc, session,
 | 
						|
                 session_disconnect(session, reason, desc, lang));
 | 
						|
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_methods
 | 
						|
 *
 | 
						|
 * Return the currently active methods for method_type
 | 
						|
 *
 | 
						|
 * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string
 | 
						|
 * regardless of actual negotiation Strings should NOT be freed
 | 
						|
 */
 | 
						|
LIBSSH2_API const char *
 | 
						|
libssh2_session_methods(LIBSSH2_SESSION * session, int method_type)
 | 
						|
{
 | 
						|
    /* All methods have char *name as their first element */
 | 
						|
    const LIBSSH2_KEX_METHOD *method = NULL;
 | 
						|
 | 
						|
    switch (method_type) {
 | 
						|
    case LIBSSH2_METHOD_KEX:
 | 
						|
        method = session->kex;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_HOSTKEY:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->hostkey;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_CRYPT_CS:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->local.crypt;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_CRYPT_SC:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->remote.crypt;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_MAC_CS:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->local.mac;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_MAC_SC:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->remote.mac;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_COMP_CS:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->local.comp;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_COMP_SC:
 | 
						|
        method = (LIBSSH2_KEX_METHOD *) session->remote.comp;
 | 
						|
        break;
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_LANG_CS:
 | 
						|
        return "";
 | 
						|
 | 
						|
    case LIBSSH2_METHOD_LANG_SC:
 | 
						|
        return "";
 | 
						|
 | 
						|
    default:
 | 
						|
        libssh2_error(session, LIBSSH2_ERROR_INVAL,
 | 
						|
                      "Invalid parameter specified for method_type");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!method) {
 | 
						|
        libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
 | 
						|
                      "No method negotiated");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return method->name;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_abstract
 | 
						|
 * Retrieve a pointer to the abstract property
 | 
						|
 */
 | 
						|
LIBSSH2_API void **
 | 
						|
libssh2_session_abstract(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    return &session->abstract;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_last_error
 | 
						|
 *
 | 
						|
 * Returns error code and populates an error string into errmsg If want_buf is
 | 
						|
 * non-zero then the string placed into errmsg must be freed by the calling
 | 
						|
 * program. Otherwise it is assumed to be owned by libssh2
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg,
 | 
						|
                           int *errmsg_len, int want_buf)
 | 
						|
{
 | 
						|
    size_t msglen = 0;
 | 
						|
 | 
						|
    /* No error to report */
 | 
						|
    if (!session->err_code) {
 | 
						|
        if (errmsg) {
 | 
						|
            if (want_buf) {
 | 
						|
                *errmsg = LIBSSH2_ALLOC(session, 1);
 | 
						|
                if (*errmsg) {
 | 
						|
                    **errmsg = 0;
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                *errmsg = (char *) "";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (errmsg_len) {
 | 
						|
            *errmsg_len = 0;
 | 
						|
        }
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (errmsg) {
 | 
						|
        const char *error = session->err_msg ? session->err_msg : "";
 | 
						|
 | 
						|
        msglen = strlen(error);
 | 
						|
 | 
						|
        if (want_buf) {
 | 
						|
            /* Make a copy so the calling program can own it */
 | 
						|
            *errmsg = LIBSSH2_ALLOC(session, msglen + 1);
 | 
						|
            if (*errmsg) {
 | 
						|
                memcpy(*errmsg, error, msglen);
 | 
						|
                (*errmsg)[msglen] = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
            *errmsg = (char *)error;
 | 
						|
    }
 | 
						|
 | 
						|
    if (errmsg_len) {
 | 
						|
        *errmsg_len = msglen;
 | 
						|
    }
 | 
						|
 | 
						|
    return session->err_code;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_last_errno
 | 
						|
 *
 | 
						|
 * Returns error code
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_last_errno(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    return session->err_code;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_flag
 | 
						|
 *
 | 
						|
 * Set/Get session flags
 | 
						|
 *
 | 
						|
 * Passing flag==0 will avoid changing session->flags while still returning
 | 
						|
 * its current value
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value)
 | 
						|
{
 | 
						|
    if (value) {
 | 
						|
        session->flags |= flag;
 | 
						|
    } else {
 | 
						|
        session->flags &= ~flag;
 | 
						|
    }
 | 
						|
 | 
						|
    return session->flags;
 | 
						|
}
 | 
						|
 | 
						|
/* _libssh2_session_set_blocking
 | 
						|
 *
 | 
						|
 * Set a session's blocking mode on or off, return the previous status when
 | 
						|
 * this function is called. Note this function does not alter the state of the
 | 
						|
 * actual socket involved.
 | 
						|
 */
 | 
						|
int
 | 
						|
_libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
 | 
						|
{
 | 
						|
    int bl = session->api_block_mode;
 | 
						|
    _libssh2_debug(session, LIBSSH2_TRACE_CONN,
 | 
						|
                   "Setting blocking mode %s", blocking?"ON":"OFF");
 | 
						|
    session->api_block_mode = blocking;
 | 
						|
 | 
						|
    return bl;
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_set_blocking
 | 
						|
 *
 | 
						|
 * Set a channel's blocking mode on or off, similar to a socket's
 | 
						|
 * fcntl(fd, F_SETFL, O_NONBLOCK); type command
 | 
						|
 */
 | 
						|
LIBSSH2_API void
 | 
						|
libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking)
 | 
						|
{
 | 
						|
    (void) _libssh2_session_set_blocking(session, blocking);
 | 
						|
}
 | 
						|
 | 
						|
/* libssh2_session_get_blocking
 | 
						|
 *
 | 
						|
 * Returns a session's blocking mode on or off
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_get_blocking(LIBSSH2_SESSION * session)
 | 
						|
{
 | 
						|
    return session->api_block_mode;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_poll_channel_read
 | 
						|
 *
 | 
						|
 * Returns 0 if no data is waiting on channel,
 | 
						|
 * non-0 if data is available
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_poll_channel_read(LIBSSH2_CHANNEL * channel, int extended)
 | 
						|
{
 | 
						|
    LIBSSH2_SESSION *session = channel->session;
 | 
						|
    LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets);
 | 
						|
 | 
						|
    while (packet) {
 | 
						|
        if ( channel->local.id == _libssh2_ntohu32(packet->data + 1)) {
 | 
						|
            if ( extended == 1 &&
 | 
						|
                 (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA
 | 
						|
                  || packet->data[0] == SSH_MSG_CHANNEL_DATA )) {
 | 
						|
                return 1;
 | 
						|
            } else if ( extended == 0 &&
 | 
						|
                        packet->data[0] == SSH_MSG_CHANNEL_DATA) {
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
            /* else - no data of any type is ready to be read */
 | 
						|
        }
 | 
						|
        packet = _libssh2_list_next(&packet->node);
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * poll_channel_write
 | 
						|
 *
 | 
						|
 * Returns 0 if writing to channel would block,
 | 
						|
 * non-0 if data can be written without blocking
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
poll_channel_write(LIBSSH2_CHANNEL * channel)
 | 
						|
{
 | 
						|
    return channel->local.window_size ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
/* poll_listener_queued
 | 
						|
 *
 | 
						|
 * Returns 0 if no connections are waiting to be accepted
 | 
						|
 * non-0 if one or more connections are available
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
poll_listener_queued(LIBSSH2_LISTENER * listener)
 | 
						|
{
 | 
						|
    return _libssh2_list_first(&listener->queue) ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_poll
 | 
						|
 *
 | 
						|
 * Poll sockets, channels, and listeners for activity
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
 | 
						|
{
 | 
						|
    long timeout_remaining;
 | 
						|
    unsigned int i, active_fds;
 | 
						|
#ifdef HAVE_POLL
 | 
						|
    LIBSSH2_SESSION *session = NULL;
 | 
						|
#ifdef HAVE_ALLOCA
 | 
						|
    struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds);
 | 
						|
#else
 | 
						|
    struct pollfd sockets[256];
 | 
						|
 | 
						|
    if (nfds > 256)
 | 
						|
        /* systems without alloca use a fixed-size array, this can be fixed
 | 
						|
           if we really want to, at least if the compiler is a C99 capable one */
 | 
						|
        return -1;
 | 
						|
#endif
 | 
						|
    /* Setup sockets for polling */
 | 
						|
    for(i = 0; i < nfds; i++) {
 | 
						|
        fds[i].revents = 0;
 | 
						|
        switch (fds[i].type) {
 | 
						|
        case LIBSSH2_POLLFD_SOCKET:
 | 
						|
            sockets[i].fd = fds[i].fd.socket;
 | 
						|
            sockets[i].events = fds[i].events;
 | 
						|
            sockets[i].revents = 0;
 | 
						|
            break;
 | 
						|
 | 
						|
        case LIBSSH2_POLLFD_CHANNEL:
 | 
						|
            sockets[i].fd = fds[i].fd.channel->session->socket_fd;
 | 
						|
            sockets[i].events = POLLIN;
 | 
						|
            sockets[i].revents = 0;
 | 
						|
            if (!session)
 | 
						|
                session = fds[i].fd.channel->session;
 | 
						|
            break;
 | 
						|
 | 
						|
        case LIBSSH2_POLLFD_LISTENER:
 | 
						|
            sockets[i].fd = fds[i].fd.listener->session->socket_fd;
 | 
						|
            sockets[i].events = POLLIN;
 | 
						|
            sockets[i].revents = 0;
 | 
						|
            if (!session)
 | 
						|
                session = fds[i].fd.listener->session;
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            if (session)
 | 
						|
                libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
 | 
						|
                              "Invalid descriptor passed to libssh2_poll()");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
#elif defined(HAVE_SELECT)
 | 
						|
    LIBSSH2_SESSION *session = NULL;
 | 
						|
    libssh2_socket_t maxfd = 0;
 | 
						|
    fd_set rfds, wfds;
 | 
						|
    struct timeval tv;
 | 
						|
 | 
						|
    FD_ZERO(&rfds);
 | 
						|
    FD_ZERO(&wfds);
 | 
						|
    for(i = 0; i < nfds; i++) {
 | 
						|
        fds[i].revents = 0;
 | 
						|
        switch (fds[i].type) {
 | 
						|
        case LIBSSH2_POLLFD_SOCKET:
 | 
						|
            if (fds[i].events & LIBSSH2_POLLFD_POLLIN) {
 | 
						|
                FD_SET(fds[i].fd.socket, &rfds);
 | 
						|
                if (fds[i].fd.socket > maxfd)
 | 
						|
                    maxfd = fds[i].fd.socket;
 | 
						|
            }
 | 
						|
            if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) {
 | 
						|
                FD_SET(fds[i].fd.socket, &wfds);
 | 
						|
                if (fds[i].fd.socket > maxfd)
 | 
						|
                    maxfd = fds[i].fd.socket;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case LIBSSH2_POLLFD_CHANNEL:
 | 
						|
            FD_SET(fds[i].fd.channel->session->socket_fd, &rfds);
 | 
						|
            if (fds[i].fd.channel->session->socket_fd > maxfd)
 | 
						|
                maxfd = fds[i].fd.channel->session->socket_fd;
 | 
						|
            if (!session)
 | 
						|
                session = fds[i].fd.channel->session;
 | 
						|
            break;
 | 
						|
 | 
						|
        case LIBSSH2_POLLFD_LISTENER:
 | 
						|
            FD_SET(fds[i].fd.listener->session->socket_fd, &rfds);
 | 
						|
            if (fds[i].fd.listener->session->socket_fd > maxfd)
 | 
						|
                maxfd = fds[i].fd.listener->session->socket_fd;
 | 
						|
            if (!session)
 | 
						|
                session = fds[i].fd.listener->session;
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            if (session)
 | 
						|
                libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
 | 
						|
                              "Invalid descriptor passed to libssh2_poll()");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
#else
 | 
						|
    /* No select() or poll()
 | 
						|
     * no sockets sturcture to setup
 | 
						|
     */
 | 
						|
 | 
						|
    timeout = 0;
 | 
						|
#endif /* HAVE_POLL or HAVE_SELECT */
 | 
						|
 | 
						|
    timeout_remaining = timeout;
 | 
						|
    do {
 | 
						|
#if defined(HAVE_POLL) || defined(HAVE_SELECT)
 | 
						|
        int sysret;
 | 
						|
#endif
 | 
						|
 | 
						|
        active_fds = 0;
 | 
						|
 | 
						|
        for(i = 0; i < nfds; i++) {
 | 
						|
            if (fds[i].events != fds[i].revents) {
 | 
						|
                switch (fds[i].type) {
 | 
						|
                case LIBSSH2_POLLFD_CHANNEL:
 | 
						|
                    if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
 | 
						|
                        /* Want to be ready for read */
 | 
						|
                        ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
 | 
						|
                        /* Not yet known to be ready for read */
 | 
						|
                        fds[i].revents |=
 | 
						|
                            libssh2_poll_channel_read(fds[i].fd.channel,
 | 
						|
                                                      0) ?
 | 
						|
                            LIBSSH2_POLLFD_POLLIN : 0;
 | 
						|
                    }
 | 
						|
                    if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) &&
 | 
						|
                        /* Want to be ready for extended read */
 | 
						|
                        ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) {
 | 
						|
                        /* Not yet known to be ready for extended read */
 | 
						|
                        fds[i].revents |=
 | 
						|
                            libssh2_poll_channel_read(fds[i].fd.channel,
 | 
						|
                                                      1) ?
 | 
						|
                            LIBSSH2_POLLFD_POLLEXT : 0;
 | 
						|
                    }
 | 
						|
                    if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) &&
 | 
						|
                        /* Want to be ready for write */
 | 
						|
                        ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) {
 | 
						|
                        /* Not yet known to be ready for write */
 | 
						|
                        fds[i].revents |=
 | 
						|
                            poll_channel_write(fds[i].fd. channel) ?
 | 
						|
                            LIBSSH2_POLLFD_POLLOUT : 0;
 | 
						|
                    }
 | 
						|
                    if (fds[i].fd.channel->remote.close
 | 
						|
                        || fds[i].fd.channel->local.close) {
 | 
						|
                        fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED;
 | 
						|
                    }
 | 
						|
                    if (fds[i].fd.channel->session->socket_state ==
 | 
						|
                        LIBSSH2_SOCKET_DISCONNECTED) {
 | 
						|
                        fds[i].revents |=
 | 
						|
                            LIBSSH2_POLLFD_CHANNEL_CLOSED |
 | 
						|
                            LIBSSH2_POLLFD_SESSION_CLOSED;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case LIBSSH2_POLLFD_LISTENER:
 | 
						|
                    if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
 | 
						|
                        /* Want a connection */
 | 
						|
                        ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
 | 
						|
                        /* No connections known of yet */
 | 
						|
                        fds[i].revents |=
 | 
						|
                            poll_listener_queued(fds[i].fd. listener) ?
 | 
						|
                            LIBSSH2_POLLFD_POLLIN : 0;
 | 
						|
                    }
 | 
						|
                    if (fds[i].fd.listener->session->socket_state ==
 | 
						|
                        LIBSSH2_SOCKET_DISCONNECTED) {
 | 
						|
                        fds[i].revents |=
 | 
						|
                            LIBSSH2_POLLFD_LISTENER_CLOSED |
 | 
						|
                            LIBSSH2_POLLFD_SESSION_CLOSED;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (fds[i].revents) {
 | 
						|
                active_fds++;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (active_fds) {
 | 
						|
            /* Don't block on the sockets if we have channels/listeners which
 | 
						|
               are ready */
 | 
						|
            timeout_remaining = 0;
 | 
						|
        }
 | 
						|
#ifdef HAVE_POLL
 | 
						|
 | 
						|
#ifdef HAVE_GETTIMEOFDAY
 | 
						|
        {
 | 
						|
            struct timeval tv_begin, tv_end;
 | 
						|
 | 
						|
            gettimeofday((struct timeval *) &tv_begin, NULL);
 | 
						|
            sysret = poll(sockets, nfds, timeout_remaining);
 | 
						|
            gettimeofday((struct timeval *) &tv_end, NULL);
 | 
						|
            timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
 | 
						|
            timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        /* If the platform doesn't support gettimeofday,
 | 
						|
         * then just make the call non-blocking and walk away
 | 
						|
         */
 | 
						|
        sysret = poll(sockets, nfds, timeout_remaining);
 | 
						|
        timeout_remaining = 0;
 | 
						|
#endif /* HAVE_GETTIMEOFDAY */
 | 
						|
 | 
						|
        if (sysret > 0) {
 | 
						|
            for(i = 0; i < nfds; i++) {
 | 
						|
                switch (fds[i].type) {
 | 
						|
                case LIBSSH2_POLLFD_SOCKET:
 | 
						|
                    fds[i].revents = sockets[i].revents;
 | 
						|
                    sockets[i].revents = 0; /* In case we loop again, be nice */
 | 
						|
                    if (fds[i].revents) {
 | 
						|
                        active_fds++;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                case LIBSSH2_POLLFD_CHANNEL:
 | 
						|
                    if (sockets[i].events & POLLIN) {
 | 
						|
                        /* Spin session until no data available */
 | 
						|
                        while (_libssh2_transport_read(fds[i].fd.channel->session)
 | 
						|
                               > 0);
 | 
						|
                    }
 | 
						|
                    if (sockets[i].revents & POLLHUP) {
 | 
						|
                        fds[i].revents |=
 | 
						|
                            LIBSSH2_POLLFD_CHANNEL_CLOSED |
 | 
						|
                            LIBSSH2_POLLFD_SESSION_CLOSED;
 | 
						|
                    }
 | 
						|
                    sockets[i].revents = 0;
 | 
						|
                    break;
 | 
						|
                case LIBSSH2_POLLFD_LISTENER:
 | 
						|
                    if (sockets[i].events & POLLIN) {
 | 
						|
                        /* Spin session until no data available */
 | 
						|
                        while (_libssh2_transport_read(fds[i].fd.listener->session)
 | 
						|
                               > 0);
 | 
						|
                    }
 | 
						|
                    if (sockets[i].revents & POLLHUP) {
 | 
						|
                        fds[i].revents |=
 | 
						|
                            LIBSSH2_POLLFD_LISTENER_CLOSED |
 | 
						|
                            LIBSSH2_POLLFD_SESSION_CLOSED;
 | 
						|
                    }
 | 
						|
                    sockets[i].revents = 0;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
#elif defined(HAVE_SELECT)
 | 
						|
        tv.tv_sec = timeout_remaining / 1000;
 | 
						|
        tv.tv_usec = (timeout_remaining % 1000) * 1000;
 | 
						|
#ifdef HAVE_GETTIMEOFDAY
 | 
						|
        {
 | 
						|
            struct timeval tv_begin, tv_end;
 | 
						|
 | 
						|
            gettimeofday((struct timeval *) &tv_begin, NULL);
 | 
						|
            sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
 | 
						|
            gettimeofday((struct timeval *) &tv_end, NULL);
 | 
						|
 | 
						|
            timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
 | 
						|
            timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        /* If the platform doesn't support gettimeofday,
 | 
						|
         * then just make the call non-blocking and walk away
 | 
						|
         */
 | 
						|
        sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
 | 
						|
        timeout_remaining = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
        if (sysret > 0) {
 | 
						|
            for(i = 0; i < nfds; i++) {
 | 
						|
                switch (fds[i].type) {
 | 
						|
                case LIBSSH2_POLLFD_SOCKET:
 | 
						|
                    if (FD_ISSET(fds[i].fd.socket, &rfds)) {
 | 
						|
                        fds[i].revents |= LIBSSH2_POLLFD_POLLIN;
 | 
						|
                    }
 | 
						|
                    if (FD_ISSET(fds[i].fd.socket, &wfds)) {
 | 
						|
                        fds[i].revents |= LIBSSH2_POLLFD_POLLOUT;
 | 
						|
                    }
 | 
						|
                    if (fds[i].revents) {
 | 
						|
                        active_fds++;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case LIBSSH2_POLLFD_CHANNEL:
 | 
						|
                    if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) {
 | 
						|
                        /* Spin session until no data available */
 | 
						|
                        while (_libssh2_transport_read(fds[i].fd.channel->session)
 | 
						|
                               > 0);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case LIBSSH2_POLLFD_LISTENER:
 | 
						|
                    if (FD_ISSET
 | 
						|
                        (fds[i].fd.listener->session->socket_fd, &rfds)) {
 | 
						|
                        /* Spin session until no data available */
 | 
						|
                        while (_libssh2_transport_read(fds[i].fd.listener->session)
 | 
						|
                               > 0);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
#endif /* else no select() or poll() -- timeout (and by extension
 | 
						|
        * timeout_remaining) will be equal to 0 */
 | 
						|
    } while ((timeout_remaining > 0) && !active_fds);
 | 
						|
 | 
						|
    return active_fds;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * libssh2_session_block_directions
 | 
						|
 *
 | 
						|
 * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN
 | 
						|
 * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked
 | 
						|
 * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked
 | 
						|
 */
 | 
						|
LIBSSH2_API int
 | 
						|
libssh2_session_block_directions(LIBSSH2_SESSION *session)
 | 
						|
{
 | 
						|
    return session->socket_block_directions;
 | 
						|
}
 | 
						|
 |