diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-08-29 18:49:16 +0200 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-08-29 18:49:16 +0200 |
commit | b8f09498388d4d167ec0add5ec27c3c75551a940 (patch) | |
tree | 9fb3375d39c593a9347052958d4886e2dda46a73 | |
parent | e116d8c1197735cb9a7b853edb2e784d42bd6ae6 (diff) | |
download | cssh-b8f09498388d4d167ec0add5ec27c3c75551a940.tar.gz cssh-b8f09498388d4d167ec0add5ec27c3c75551a940.tar.bz2 |
handling receiving of files in SCP mode
-rw-r--r-- | src/cssh.c | 235 |
1 files changed, 152 insertions, 83 deletions
@@ -16,6 +16,8 @@ #include "options.h" #include "version.h" +#define BUFSIZE 4096 + static int parse_options_and_arguments( int argc, char *argv[], struct gengetopt_args_info *args_info ) { cmdline_parser_init( args_info ); @@ -276,7 +278,7 @@ static int read_hosts_file( const char *hosts_file, unsigned short default_port, return 0; } -static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_scp **scp, char **host, unsigned short *port, const int nof_sessions, bool verbose ) +static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_scp **scp, char ***buf, char **host, unsigned short *port, const int nof_sessions, bool verbose ) { for( unsigned int i = 0; i < nof_sessions; i++ ) { if( ssh_is_connected( (*session)[i] ) ) { @@ -294,6 +296,9 @@ static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_ ssh_scp_close( (*scp)[i] ); ssh_scp_free( (*scp)[i] ); } + if( buf != NULL ) { + free( (*buf)[i] ); + } ssh_disconnect( (*session)[i] ); if( verbose ) { fprintf( stderr, "Disconnected from '%s', port %d..\n", host[i], port[i] ); @@ -307,6 +312,9 @@ static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_ if( scp != NULL ) { free( *scp ); } + if( buf != NULL ) { + free( *buf ); + } free( *session ); free( *host ); free( port ); @@ -461,7 +469,7 @@ int main( int argc, char *argv[] ) } else { fprintf( stderr, "ERROR: error connecting to '%s', port '%d': %s\n", host[i], port[i], ssh_get_error( session[i] ) ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -503,7 +511,7 @@ int main( int argc, char *argv[] ) if( verify_knownhost( session[i] ) < 0 ) { fprintf( stderr, "ERROR: closing connection to '%s', port '%d' due to security reasons\n", host[i], port[i] ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } auth_state[i] = CSSH_AUTH_VERIFY_HOST_DONE; @@ -524,7 +532,7 @@ int main( int argc, char *argv[] ) } else { fprintf( stderr, "ERROR: ssh_userauth_none to '%s', port '%d' failed: %s\n", host[i], port[i], ssh_get_error( session[i] ) ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } break; @@ -549,7 +557,7 @@ int main( int argc, char *argv[] ) } else if( rc == SSH_AUTH_DENIED ) { auth_state[i] = CSSH_AUTH_PUBKEY_DONE_FAILED; } else { - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } break; @@ -568,7 +576,7 @@ int main( int argc, char *argv[] ) } else if( rc == SSH_AUTH_DENIED ) { auth_state[i] = CSSH_AUTH_PASSWORD_DONE_FAILED; } else { - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } break; @@ -588,7 +596,7 @@ int main( int argc, char *argv[] ) case CSSH_AUTH_DONE_FAILED: // one authentication failed, bail out for now fprintf( stderr, "ERROR: authentication failed for one host ('%s'), aborting now", host[i] ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -611,7 +619,7 @@ int main( int argc, char *argv[] ) memset( channel, 0, nof_sessions + 1 ); if( channel == NULL ) { fprintf( stderr, "ERROR: Memory allocation failed for ssh_channels" ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } for( unsigned int i = 0; i < nof_sessions; i++ ) { @@ -619,7 +627,7 @@ int main( int argc, char *argv[] ) if( channel[i] == NULL ) { fprintf( stderr, "ERROR: Unable to open SSH channel: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -627,7 +635,7 @@ int main( int argc, char *argv[] ) for( unsigned int i = 0; i < nof_sessions; i++ ) { rc = ssh_channel_open_session( channel[i] ); if( rc != SSH_OK ) { - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -645,7 +653,7 @@ int main( int argc, char *argv[] ) } if( cmd[0] == '\0' ) { fprintf( stderr, "ERROR: Empty command, no interactive CLI supported currently\n" ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } @@ -655,7 +663,7 @@ int main( int argc, char *argv[] ) if( rc != SSH_OK ) { fprintf( stderr, "ERROR: Executing SSH command '%s' failed: %s\n", cmd, ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -693,12 +701,12 @@ int main( int argc, char *argv[] ) } for( unsigned int i = 0; i < nof_sessions; i++ ) { - char buffer[4096]; + char buffer[BUFSIZE]; rc = ssh_channel_poll( channel[i], 0 ); if( rc == SSH_ERROR ) { fprintf( stderr, "ERROR: ssh_channel_poll on stdout failed: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } @@ -707,7 +715,7 @@ int main( int argc, char *argv[] ) if( nread == SSH_ERROR ) { fprintf( stderr, "ERROR: ssh_channel_read_nonblocking on stdout failed: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } @@ -720,14 +728,14 @@ int main( int argc, char *argv[] ) if( wrc < 0 ) { fprintf( stderr, "ERROR: while writing to stdout: %s\n", strerror( errno ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } if( wrc != nread ) { fprintf( stderr, "ERROR: Write mismatch on stdout (%zu != %d)\n", wrc, nread ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -737,7 +745,7 @@ int main( int argc, char *argv[] ) if( rc == SSH_ERROR ) { fprintf( stderr, "ERROR: ssh_channel_poll on stderr failed: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } @@ -746,7 +754,7 @@ int main( int argc, char *argv[] ) if( nread == SSH_ERROR ) { fprintf( stderr, "ERROR: ssh_channel_read_nonblocking on stderr failed: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } @@ -755,14 +763,14 @@ int main( int argc, char *argv[] ) if( wrc < 0 ) { fprintf( stderr, "ERROR: while writting to stderr: %s\n", strerror( errno ) ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } if( wrc != nread ) { fprintf( stderr, "ERROR: Write mismatch on stderr (%zu != %d)\n", wrc, nread ); - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -776,7 +784,7 @@ int main( int argc, char *argv[] ) cssh_msleep( 1 ); } - cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); } break; @@ -787,15 +795,13 @@ int main( int argc, char *argv[] ) // TODO: host destination can be explicit (one without -H hosts // parameter) or it is a placeholder host @HOST@:/dira/dirb copy_direction_e copy_direction = CSSH_COPY_DIRECTION_DOWNLOAD; - const char *local_directory = "x"; - - fprintf( stderr, "Copy mode\n" ); + const char *local_directory = "adir"; ssh_scp *scp = (ssh_scp *)malloc( ( nof_sessions + 1 ) * sizeof( ssh_scp ) ); memset( scp, 0, nof_sessions + 1 ); if( scp == NULL ) { fprintf( stderr, "ERROR: Memory allocation failed for ssh_scp" ); - cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } for( unsigned int i = 0; i < nof_sessions; i++ ) { @@ -806,7 +812,7 @@ int main( int argc, char *argv[] ) if( scp[i] == NULL ) { fprintf( stderr, "ERROR: Unable to open SCP channel: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } @@ -816,72 +822,135 @@ int main( int argc, char *argv[] ) if( rc != SSH_OK ) { fprintf( stderr, "ERROR: Unable to initialize SCP sessions: %s\n", ssh_get_error( session[i] ) ); - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); } } - switch( copy_direction ) { - case CSSH_COPY_DIRECTION_UPLOAD: { - } break; + char **buf = (char **)malloc( ( nof_sessions + 1 ) * sizeof( char * ) ); + if( buf == NULL ) { + fprintf( stderr, "ERROR: Memory allocation failed for receiving buffers of ssh_scp" ); + cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + } + memset( buf, 0, ( nof_sessions + 1 ) * sizeof( char * ) ); + for( unsigned int i = 0; i < nof_sessions; i++ ) { + buf[i] = (char *)malloc( BUFSIZE ); + if( buf[i] == NULL ) { + fprintf( stderr, "ERROR: Memory allocation failed for receiving buffers of ssh_scp" ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + } + } + + bool all_eof = false; + bool *is_eof = (bool *)malloc( ( nof_sessions + 1 ) * sizeof( bool ) ); + if( is_eof == NULL ) { + fprintf( stderr, "ERROR: Memory allocation failed for 'is_eof'" ); + return -1; + } + memset( is_eof, false, ( nof_sessions + 1 ) * sizeof( bool ) ); + + while( !all_eof ) { - case CSSH_COPY_DIRECTION_DOWNLOAD: { - // TODO: we receive files from N hosts, so we should - // have a method to create separate directories or - // files, e.g. file.host1, file.host2, etc. - // or dirs host1/file host2/file. Maybe this depends - // also on whether we specify recursive mode or not - for( unsigned int i = 0; i < nof_sessions; i++ ) { - rc = ssh_scp_pull_request( scp[i] ); - switch( rc ) { - case SSH_SCP_REQUEST_NEWDIR: - // TODO: mkdir in host[i] workspace - break; - - case SSH_SCP_REQUEST_ENDDIR: - // TODO: leave current workspace - break; - - case SSH_SCP_REQUEST_NEWFILE: { - const char *filename = ssh_scp_request_get_filename( scp[i] ); - int mode = ssh_scp_request_get_permissions( scp[i] ); - uint64_t size = ssh_scp_request_get_size64( scp[i] ); - fprintf( stderr, "Receiving file '%s' with permissions '0%o' of size '%"PRIu64"'\n", - filename, mode, size ); - rc = ssh_scp_accept_request( scp[i] ); - if( rc != SSH_OK ) { - fprintf( stderr, "ERROR: accepting request for file '%s' failed: %s\n", - filename, ssh_get_error( scp[i] ) ); - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); + switch( copy_direction ) { + case CSSH_COPY_DIRECTION_UPLOAD: { + } break; + + case CSSH_COPY_DIRECTION_DOWNLOAD: { + // TODO: we receive files from N hosts, so we should + // have a method to create separate directories or + // files, e.g. file.host1, file.host2, etc. + // or dirs host1/file host2/file. Maybe this depends + // also on whether we specify recursive mode or not + for( unsigned int i = 0; i < nof_sessions; i++ ) { + rc = ssh_scp_pull_request( scp[i] ); + switch( rc ) { + case SSH_SCP_REQUEST_NEWDIR: { + // TODO: mkdir in host[i] workspace + const char *filename = ssh_scp_request_get_filename( scp[i] ); + int mode = ssh_scp_request_get_permissions( scp[i] ); + fprintf( stderr, "Receiving directory '%s' with permissions '0%o'\n", + filename, mode ); + rc = ssh_scp_accept_request( scp[i] ); + if( rc != SSH_OK ) { + fprintf( stderr, "ERROR: accepting request for directory '%s' failed: %s\n", + filename, ssh_get_error( session[i] ) ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + } + } break; + + case SSH_SCP_REQUEST_ENDDIR: + // TODO: leave current workspace + // TODO: we need a stack per client connection + // to remember the current directory we are in + break; + + case SSH_SCP_REQUEST_NEWFILE: { + const char *filename = ssh_scp_request_get_filename( scp[i] ); + int mode = ssh_scp_request_get_permissions( scp[i] ); + uint64_t size = ssh_scp_request_get_size64( scp[i] ); + fprintf( stderr, "Receiving file '%s' with permissions '0%o' of size '%"PRIu64"'\n", + filename, mode, size ); + rc = ssh_scp_accept_request( scp[i] ); + if( rc != SSH_OK ) { + fprintf( stderr, "ERROR: accepting request for file '%s' failed: %s\n", + filename, ssh_get_error( session[i] ) ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + } + // TODO: we receive a buffer of data < 65k, so we must introduce a + // state machine here so we know we still have to read or initiate the accept, + // this is also quite handy as we can show some progress bar here or some ETA + // TODO: write buffer to received file in workdir of host[i] + // TODO: slurping it all for one connection is not fair to other connections, + // we should read a buffer and then check the states of the other and hit the + // loop again, this needs a state machine.. + uint64_t bytesReceived = 0; + while( bytesReceived < size ) { + rc = ssh_scp_read( scp[i], buf[i], BUFSIZE ); + if( rc == SSH_ERROR ) { + fprintf( stderr, "ERROR: reading data for file '%s' failed: %s\n", + filename, ssh_get_error( session[i] ) ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + } + bytesReceived += rc; + } + } break; + + case SSH_SCP_REQUEST_EOF: + is_eof[i] = true; + all_eof = true; + for( unsigned int i = 0; i < nof_sessions; i++ ) { + if( !is_eof[i] ) { + all_eof = false; + break; + } + } + break; + + case SSH_ERROR: + fprintf( stderr, "ERROR: error from remote host '%s': %s\n", + host[i], ssh_get_error( session[i] ) ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); exit( EXIT_FAILURE ); - } - // TODO: receive file in workdir of host[i] - } break; - - case SSH_SCP_REQUEST_EOF: - // TODO: keep array of eof flags and terminate - // loop if all hosts have terminated - break; - - case SSH_ERROR: - fprintf( stderr, "ERROR: error from remote host '%s': %s\n", - host[i], ssh_get_error( session[i] ) ); - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); - exit( EXIT_FAILURE ); - break; + break; - case SSH_SCP_REQUEST_WARNING: - fprintf( stderr, "WARNING: remote host '%s': %s\n", - host[i], ssh_scp_request_get_warning( scp[i] ) ); - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); - exit( EXIT_FAILURE ); - break; + case SSH_SCP_REQUEST_WARNING: + fprintf( stderr, "WARNING: remote host '%s': %s\n", + host[i], ssh_scp_request_get_warning( scp[i] ) ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); + exit( EXIT_FAILURE ); + break; + } } - } - } break; + } break; + } } - cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 ); + cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 ); } break; |