1751 lines · 52.6 KB · C
RawDownload
/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org> * Copyright (c) 2009-2011 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 */staticLIBSSH2_ALLOC_FUNC(libssh2_default_alloc){    (void) abstract;    return malloc(count);} /* libssh2_default_free */staticLIBSSH2_FREE_FUNC(libssh2_default_free){    (void) abstract;    free(ptr);} /* libssh2_default_realloc */staticLIBSSH2_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 intbanner_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 intbanner_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 intsession_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 intget_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 intlibssh2_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 intlibssh2_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 ();

Lines 1–500 of 1751.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t