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