summaryrefslogtreecommitdiff
path: root/libs/libssh2/src/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libssh2/src/session.c')
-rw-r--r--libs/libssh2/src/session.c1794
1 files changed, 1794 insertions, 0 deletions
diff --git a/libs/libssh2/src/session.c b/libs/libssh2/src/session.c
new file mode 100644
index 0000000000..6352d12ee7
--- /dev/null
+++ b/libs/libssh2/src/session.c
@@ -0,0 +1,1794 @@
+/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2015 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"
+#include "session.h"
+#include "channel.h"
+#include "mac.h"
+#include "misc.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, LIBSSH2_ERROR_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, &c, 1,
+ LIBSSH2_SOCKET_RECV_FLAGS(session));
+ if (ret < 0) {
+ if(session->api_block_mode || (ret != -EAGAIN))
+ /* ignore EAGAIN when non-blocking */
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error recving %d bytes: %d", 1, -ret);
+ }
+ else
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Recved %d bytes banner", ret);
+
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ session->banner_TxRx_total_send = banner_len;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ /* Some kinda error */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ if (ret == 0) {
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+ }
+
+ 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 LIBSSH2_ERROR_BANNER_RECV;
+ }
+
+ 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 LIBSSH2_ERROR_BANNER_RECV;
+
+ 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 LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * banner_send
+ *
+ * Send the default banner, or the one set via libssh2_setopt_string
+ *
+ * Returns LIBSSH2_ERROR_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,
+ 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, -ret);
+ 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 == -EAGAIN) {
+ /* the whole packet could not be sent, save the what was */
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ if (ret > 0)
+ session->banner_TxRx_total_send += ret;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ /* 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 and VMS*/
+ 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
+
+#if defined(SO_STATE) && defined( __VMS ) && (GETBLOCK == 0)
+
+ /* VMS TCP/IP Services */
+
+ size_t sockstat = 0;
+ int callstat = 0;
+ size_t size = sizeof( int );
+
+ callstat = getsockopt(sockfd, SOL_SOCKET, SO_STATE,
+ (char *)&sockstat, &size);
+ if ( callstat == -1 ) return(0);
+ if ( (sockstat&SS_NBIO) )return(1);
+ return(0);
+
+#undef GETBLOCK
+#define GETBLOCK 6
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 1; /* returns blocking */
+#undef GETBLOCK
+#define GETBLOCK 7
+#endif
+
+#if (GETBLOCK == 0)
+#error "no non-blocking method was found/used/get"
+#endif
+}
+
+/* libssh2_session_banner_set
+ * Set the local banner to use in the server handshake.
+ */
+LIBSSH2_API int
+libssh2_session_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ size_t 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);
+
+ /* first zero terminate like this so that the debug output is nice */
+ 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_banner_set
+ * Set the local banner. DEPRECATED VERSION
+ */
+LIBSSH2_API int
+libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ return libssh2_session_banner_set(session, banner);
+}
+
+/*
+ * 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->send = _libssh2_send;
+ session->recv = _libssh2_recv;
+ session->abstract = abstract;
+ session->api_timeout = 0; /* timeout-free API by default */
+ session->api_block_mode = 1; /* blocking API by default */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "New session resource allocated");
+ _libssh2_init_if_needed ();
+ }
+ 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;
+
+ case LIBSSH2_CALLBACK_SEND:
+ oldcb = session->send;
+ session->send = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_RECV:
+ oldcb = session->recv;
+ session->recv = 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, time_t start_time)
+{
+ int rc;
+ int seconds_to_next;
+ int dir;
+ int has_timeout;
+ long ms_to_next = 0;
+ long elapsed_ms;
+
+ /* since libssh2 often sets EAGAIN internally before this function is
+ called, we can decrease some amount of confusion in user programs by
+ resetting the error code in this function to reduce the risk of EAGAIN
+ being stored as error when a blocking function has returned */
+ session->err_code = LIBSSH2_ERROR_NONE;
+
+ rc = libssh2_keepalive_send (session, &seconds_to_next);
+ if (rc < 0)
+ return rc;
+
+ ms_to_next = seconds_to_next * 1000;
+
+ /* figure out what to wait for */
+ dir = libssh2_session_block_directions(session);
+
+ if(!dir) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Nothing to wait for in wait_socket");
+ /* To avoid that we hang below just because there's nothing set to
+ wait for, we timeout on 1 second to also avoid busy-looping
+ during this condition */
+ ms_to_next = 1000;
+ }
+
+ if (session->api_timeout > 0 &&
+ (seconds_to_next == 0 ||
+ ms_to_next > session->api_timeout)) {
+ time_t now = time (NULL);
+ elapsed_ms = (long)(1000*difftime(now, start_time));
+ if (elapsed_ms > session->api_timeout) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "API timeout expired");
+ }
+ ms_to_next = (session->api_timeout - elapsed_ms);
+ has_timeout = 1;
+ }
+ else if (ms_to_next > 0) {
+ has_timeout = 1;
+ }
+ else
+ has_timeout = 0;
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd sockets[1];
+
+ sockets[0].fd = session->socket_fd;
+ sockets[0].events = 0;
+ sockets[0].revents = 0;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
+ sockets[0].events |= POLLIN;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
+ sockets[0].events |= POLLOUT;
+
+ rc = poll(sockets, 1, has_timeout?ms_to_next: -1);
+ }
+#else
+ {
+ fd_set rfd;
+ fd_set wfd;
+ fd_set *writefd = NULL;
+ fd_set *readfd = NULL;
+ struct timeval tv;
+
+ tv.tv_sec = ms_to_next / 1000;
+ tv.tv_usec = (ms_to_next - tv.tv_sec*1000) * 1000;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) {
+ FD_ZERO(&rfd);
+ FD_SET(session->socket_fd, &rfd);
+ readfd = &rfd;
+ }
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ FD_ZERO(&wfd);
+ FD_SET(session->socket_fd, &wfd);
+ writefd = &wfd;
+ }
+
+ rc = select(session->socket_fd + 1, readfd, writefd, NULL,
+ has_timeout ? &tv : NULL);
+ }
+#endif
+ if(rc == 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting on socket");
+ }
+ if(rc < 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Error waiting on socket");
+ }
+
+ 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 (LIBSSH2_INVALID_SOCKET == sock) {
+ /* Did we forget something? */
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET,
+ "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 change to non-blocking */
+ rc = session_nonblock(session->socket_fd, 1);
+ if (rc) {
+ return _libssh2_error(session, rc,
+ "Failed changing socket's "
+ "blocking state to non-blocking");
+ }
+ }
+
+ 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;
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent) {
+ do {
+ rc = banner_receive(session);
+ if (rc)
+ return _libssh2_error(session, rc,
+ "Failed getting banner");
+ } while(strncmp("SSH-", (char *)session->remote.banner, 4));
+
+ 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_send(session, session->startup_service,
+ sizeof("ssh-userauth") + 5 - 1,
+ NULL, 0);
+ 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;
+}
+
+/*
+ * libssh2_session_handshake()
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session, session_startup(session, sock) );
+
+ return rc;
+}
+
+/*
+ * libssh2_session_startup()
+ *
+ * DEPRECATED. Use libssh2_session_handshake() instead! This function is not
+ * portable enough.
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_startup(LIBSSH2_SESSION *session, int sock)
+{
+ return libssh2_session_handshake(session, (libssh2_socket_t) sock);
+}
+
+/*
+ * 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;
+ int packets_left = 0;
+
+ if (session->free_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Freeing session resource",
+ session->remote.banner);
+
+ session->free_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 == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_state = libssh2_NB_state_sent;
+ }
+
+ if (session->free_state == libssh2_NB_state_sent) {
+ while ((l = _libssh2_list_first(&session->listeners))) {
+ rc = _libssh2_channel_forward_cancel(l);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_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.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+ 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.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ }
+ 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->kexinit_data) {
+ LIBSSH2_FREE(session, session->kexinit_data);
+ }
+ if (session->startup_data) {
+ LIBSSH2_FREE(session, session->startup_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->sftpInit_sftp) {
+ LIBSSH2_FREE(session, session->sftpInit_sftp);
+ }
+
+ /* Free payload buffer */
+ if (session->packet.total_num) {
+ LIBSSH2_FREE(session, session->packet.payload);
+ }
+
+ /* Cleanup all remaining packets */
+ while ((pkg = _libssh2_list_first(&session->packets))) {
+ packets_left++;
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "packet left with id %d", pkg->data[0]);
+ /* unlink the node */
+ _libssh2_list_remove(&pkg->node);
+
+ /* free */
+ LIBSSH2_FREE(session, pkg->data);
+ LIBSSH2_FREE(session, pkg);
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Extra packets left %d", packets_left);
+
+ if(session->socket_prev_blockstate) {
+ /* if the socket was previously blocking, put it back so */
+ rc = session_nonblock(session->socket_fd, 0);
+ if (rc) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unable to reset socket's blocking state");
+ }
+ }
+
+ if (session->server_hostkey) {
+ LIBSSH2_FREE(session, session->server_hostkey);
+ }
+
+ /* error string */
+ if (session->err_msg && ((session->err_flags & LIBSSH2_ERR_FLAG_DUP) != 0)) {
+ LIBSSH2_FREE(session, (char *)session->err_msg);
+ }
+
+ 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);
+
+ if(descr_len > 256)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "too long description");
+
+ /* 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;
+
+ *(s++) = SSH_MSG_DISCONNECT;
+ _libssh2_store_u32(&s, reason);
+ _libssh2_store_str(&s, description, descr_len);
+ /* store length only, lang is sent separately */
+ _libssh2_store_u32(&s, lang_len);
+
+ session->disconnect_state = libssh2_NB_state_created;
+ }
+
+ rc = _libssh2_transport_send(session, session->disconnect_data,
+ session->disconnect_data_len,
+ (unsigned char *)lang, lang_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ 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_set_last_error
+ *
+ * Sets the internal error code for the session.
+ *
+ * This function is available specifically to be used by high level
+ * language wrappers (i.e. Python or Perl) that may extend the library
+ * features while still relying on its error reporting mechanism.
+ */
+LIBSSH2_API int
+libssh2_session_set_last_error(LIBSSH2_SESSION* session,
+ int errcode,
+ const char* errmsg)
+{
+ return _libssh2_error_flags(session, errcode, errmsg,
+ LIBSSH2_ERR_FLAG_DUP);
+}
+
+/* Libssh2_session_flag
+ *
+ * Set/Get session flags
+ *
+ * Return error code.
+ */
+LIBSSH2_API int
+libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value)
+{
+ switch(flag) {
+ case LIBSSH2_FLAG_SIGPIPE:
+ session->flag.sigpipe = value;
+ break;
+ case LIBSSH2_FLAG_COMPRESS:
+ session->flag.compress = value;
+ break;
+ default:
+ /* unknown flag */
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/* _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_session_set_timeout
+ *
+ * Set a session's timeout (in msec) for blocking mode,
+ * or 0 to disable timeouts.
+ */
+LIBSSH2_API void
+libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout)
+{
+ session->api_timeout = timeout;
+}
+
+/* libssh2_session_get_timeout
+ *
+ * Returns a session's timeout, or 0 if disabled
+ */
+LIBSSH2_API long
+libssh2_session_get_timeout(LIBSSH2_SESSION * session)
+{
+ return session->api_timeout;
+}
+
+/*
+ * 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;
+ LIBSSH2_PACKET *packet;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = channel->session;
+ 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 structure 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_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = poll(sockets, nfds, timeout_remaining);
+ _libssh2_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_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ _libssh2_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;
+}
+
+/* libssh2_session_banner_get
+ * Get the remote banner (server ID string)
+ */
+
+LIBSSH2_API const char *
+libssh2_session_banner_get(LIBSSH2_SESSION *session)
+{
+ /* to avoid a coredump when session is NULL */
+ if (NULL == session)
+ return NULL;
+
+ if (NULL==session->remote.banner)
+ return NULL;
+
+ return (const char *) session->remote.banner;
+}