Hello,

To use a buffer-size bigger than 1024 byte still
has some issues. The attached testcase works
perfectly, if the buffersize is 1024, otherwise it
won't.

Thanks a lot
Marcus

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <malloc.h>
#include <alloca.h>
#include <stdlib.h>

#include <libssh2.h>
#include <libssh2_sftp.h>

#define SOCKET_TIMEOUT      60

// Buffersize - sftp bug in libssh2 1.0 und 1.2
#if LIBSSH2_VERSION_MAJOR == 0
#define LIBSSH2_BUFFERSIZE      65536
#else
//#define LIBSSH2_BUFFERSIZE      1024
#define LIBSSH2_BUFFERSIZE      32500
#endif


struct Connection
{
    int sock;
    LIBSSH2_SESSION *ssh2;
    LIBSSH2_SFTP *sftp;
};
static bool socket_connect( const char *remote_host, int remote_port, int &sock );
static void socket_disconnect( int sock );
static bool socket_select( int sock, LIBSSH2_SESSION *session );    // session == NULL -> writing
static Connection* ssh2_connect( const char *remote_host, int remote_port, const char *username, const char *password );
static Connection* sftp_connect( const char *remote_host, int remote_port, const char *username, const char *password );
static void ssh2_disconnect( Connection *connection );

bool sftp_put_file( const Connection *connection, const char *local_filename, const char *target_filename );


int main(int argc, char *argv[])
{
    Connection *connection = sftp_connect( "www.youserver.com", 22, "XXXX", "abcdef" );
    if( connection == NULL )
        return 1;
    bool status = sftp_put_file( connection, "/tmp/bigger-than-buffer", "/tmp/hugo2" );
    ssh2_disconnect( connection );
    printf( status ? "Worked\n" : "Didn't work\n" );
    return 0;
}



bool sftp_put_file( const Connection *connection, const char *local_filename, const char *target_filename )
{
    LIBSSH2_SFTP_HANDLE *sftp_handle;
    while( (sftp_handle = libssh2_sftp_open(connection->sftp, target_filename, LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644)) == NULL &&
           libssh2_session_last_error(connection->ssh2,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(connection->sock,connection->ssh2) )
            break;
    }
    FILE *file = fopen( local_filename, "rb" );
    if( file == NULL )
    {
        int rc;
        while( (rc = libssh2_sftp_close(sftp_handle)) == LIBSSH2_ERROR_EAGAIN )
        {
            if( ! socket_select(connection->sock,connection->ssh2) )
                break;
        }
        return false;
    }
    bool transfer_error = false;
    for( ;; )
    {
        char buffer[LIBSSH2_BUFFERSIZE];
        int nread = fread( buffer, 1, sizeof(buffer), file );
        if( nread <= 0 )
            break;
        char *ptr = buffer;
        int nwrite = nread;
        for( ;; )
        {
            // write data in a loop until we block
            int rc;
            while( (rc = libssh2_sftp_write(sftp_handle, ptr, nread)) == LIBSSH2_ERROR_EAGAIN )
            {
                // HERE IT TIMED OUT!
                if( ! socket_select(connection->sock,connection->ssh2) )
                    break;
            }
            if( rc <= 0 )
            {
                transfer_error = true;
                break;
            }
            ptr += rc;
            nwrite -= rc;
            if( nwrite == 0 )
                break;
        }
        if( transfer_error )
            break;
    }
    int rc;
    while( (rc = libssh2_sftp_close(sftp_handle)) == LIBSSH2_ERROR_EAGAIN )
    {
        // HERE IT TIMED OUT!
        if( ! socket_select(connection->sock,connection->ssh2) )
            break;
    }
    fclose( file );
    if( transfer_error )
        return false;
    return true;
}



static bool socket_connect( const char *remote_host, int remote_port, int &sock )
{
    // IP-Adresse ermitteln (zuerst IPv6 versuchen)
    struct hostent *host_addr = NULL;
    {
        host_addr = gethostbyname( remote_host );
        if( host_addr == NULL )
            return false;
    }
    if( host_addr->h_addrtype != AF_INET )
        return false;

    // Port ermitteln
    uint16_t host_port;
    if( remote_port == 0 || remote_port == -1 )
    {
        struct servent *serv = getservbyname( "ssh", "tcp" );
        if( serv )
            host_port = serv->s_port;
        else
            host_port = htons( (unsigned short)22 );
    }
    else
        host_port = htons( (unsigned short)remote_port );

    // Internet-Adresse erzeugen
    struct sockaddr_in addr4;
    addr4.sin_family = AF_INET;
    addr4.sin_port = host_port;
    memcpy( &addr4.sin_addr, host_addr->h_addr_list[0],  host_addr->h_length);

    // Socket erstellen
    sock = socket( AF_INET, SOCK_STREAM, 0 );
    if( sock == -1 )
        return false;

    // Verbinden mit einem nicht blockierenden Socket (Timeout)
    fcntl( sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK );

    // Verbinden, schlägt fehl, da Socket sonst blockieren würde
    int status;
        status = connect( sock, (const struct sockaddr*)&addr4, sizeof(addr4) );
    if( status == -1 )
    {
        if( errno != EINPROGRESS && errno != EINTR )
        {
            socket_disconnect( sock );
            return false;
        }
    }
    // Warten, bis die Verbindung fertig ist
    if( ! socket_select(sock,NULL) )
        return false;
    return true;
}
static void socket_disconnect( int sock )
{
    close( sock );
}
static bool socket_select( int sock, LIBSSH2_SESSION *session )
{
    bool read = false;
    if( session != NULL )
    {
        // now make sure we wait in the correct direction
        int direction = libssh2_session_block_directions( session );
        if( direction & LIBSSH2_SESSION_BLOCK_INBOUND )
            read = true;
        else if( direction & LIBSSH2_SESSION_BLOCK_OUTBOUND )
            read = false;
    }

    // Nun select aufrufen; entweder timeout oder erfolgreich verbunden
    fd_set fds;
    FD_ZERO( &fds );
    FD_SET( sock, &fds );

    struct timeval timeout;
    timeout.tv_sec = SOCKET_TIMEOUT;
    timeout.tv_usec = 0;

    int status = read ? select(sock + 1, &fds, NULL, NULL, &timeout) :
                        select(sock + 1, NULL, &fds, NULL, &timeout);
    if( status == -1 )
        return false;
    if( FD_ISSET(sock, &fds) == 0 )         // Timeout
        return false;
    return true;
}

static Connection* ssh2_connect( const char *remote_host, int remote_port, const char *username, const char *password )
{
    int sock;
    if( ! socket_connect(remote_host, remote_port, sock) )
        return NULL;

    // Create a session instance
    LIBSSH2_SESSION *session = libssh2_session_init();
    if( ! session )
        goto ssh2_connect_return;

    // Since we have set non-blocking, tell libssh2 we are non-blocking
    libssh2_session_set_blocking( session, 0 );

    // Start up session, this will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
    int rc;
    while( (rc = libssh2_session_startup(session, sock)) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(sock,NULL) )
            break;
    }
    if( rc != 0 )
        goto ssh2_connect_return;

    // At this point we havn't authenticated, the first thing to do is check the hostkey's fingerprint against our known hosts
    const char *fp;
    while( (fp = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5)) == NULL &&
           libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(sock,NULL) )        // must wait for writing, maybe busy waiting (what should I do...)
            break;
    }

    // check what authentication methods are available
    const char *userauthlist;
    while( (userauthlist = libssh2_userauth_list(session, username, strlen(username))) == NULL &&
           libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(sock,NULL) )        // must wait for writing, maybe busy waiting (what should I do...)
            break;
    }
    if( userauthlist == NULL )
        userauthlist = "";
    // Authenticate via password
    while( (rc = libssh2_userauth_password(session, username, password)) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(sock,NULL) )        // must wait for writing, maybe busy waiting (what should I do...)
            break;
    }
    if( rc != 0 )
        goto ssh2_connect_return;

    static Connection connection;
    connection.sock = sock;
    connection.ssh2 = session;
    connection.sftp = NULL;
    return &connection;

    // Error-Handling:
    ssh2_connect_return:

    if( session )
    {
        while( libssh2_session_disconnect(session, "Normal shutdown, thank you for playing") == LIBSSH2_ERROR_EAGAIN )
        {
            if( ! socket_select(sock,NULL) )        // must wait for writing, maybe busy waiting (what should I do...)
                break;
        }
        libssh2_session_free( session );
    }
    socket_disconnect( sock );
    return NULL;
}
static Connection* sftp_connect( const char *remote_host, int remote_port, const char *username, const char *password )
{
    Connection *connection = ssh2_connect( remote_host, remote_port, username, password );
    if( connection == NULL )
        return NULL;
    LIBSSH2_SFTP *sftp_session;
    while( (sftp_session = libssh2_sftp_init(connection->ssh2)) == NULL &&
           libssh2_session_last_error(connection->ssh2,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(connection->sock,connection->ssh2) )
            break;
    }
    if( ! sftp_session )
    {
        ssh2_disconnect( connection );
        return NULL;
    }
    connection->sftp = sftp_session;
    return connection;
}
static void ssh2_disconnect( Connection *connection )
{
    if( connection->sftp != NULL )
    {
        while( libssh2_sftp_shutdown(connection->sftp) == LIBSSH2_ERROR_EAGAIN )
        {
            if( ! socket_select(connection->sock,connection->ssh2) )
                break;
        }
    }
    while( libssh2_session_disconnect(connection->ssh2, "Normal shutdown, thank you for playing") == LIBSSH2_ERROR_EAGAIN )
    {
        if( ! socket_select(connection->sock,connection->ssh2) )
            break;
    }
    libssh2_session_free( connection->ssh2 );
    socket_disconnect( connection->sock );
}




_______________________________________________
libssh2-devel http://cool.haxx.se/cgi-bin/mailman/listinfo/libssh2-devel

Reply via email to