From 5f88efdddc3abc5c779bc3c7c607b21eb118956c Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Tue, 28 Feb 2023 17:01:26 -0800 Subject: [PATCH] Add test to read lots of data over a channel Connects to the ssh server then downloads several MB of data. This tests the data transfer path as well as boundary cases in packet handling as data is split into smaller SSH blocks. --- tests/CMakeLists.txt | 1 + tests/Makefile.am | 3 +- tests/test_read.c | 108 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/test_read.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1e02f01d..e45a94bc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -127,6 +127,7 @@ set(TESTS keyboard_interactive_auth_fails_with_wrong_response keyboard_interactive_auth_succeeds_with_correct_response agent_forward_succeeds + read ) if(CRYPTO_BACKEND STREQUAL "OpenSSL") diff --git a/tests/Makefile.am b/tests/Makefile.am index 9dce308b..942d17da 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -31,7 +31,8 @@ INTEGRATION_TESTS = \ test_public_key_auth_succeeds_with_correct_rsa_key \ test_public_key_auth_succeeds_with_correct_rsa_openssh_key \ test_public_key_auth_succeeds_with_correct_signed_ecdsa_key \ - test_public_key_auth_succeeds_with_correct_signed_rsa_key + test_public_key_auth_succeeds_with_correct_signed_rsa_key \ + test_read # Integration tests using Docker # Enable this once the tests are working diff --git a/tests/test_read.c b/tests/test_read.c new file mode 100644 index 00000000..14d05240 --- /dev/null +++ b/tests/test_read.c @@ -0,0 +1,108 @@ +/* libssh2 test receiving large amounts of data through a channel */ + +#include "session_fixture.h" +#include "runner.h" + +#include + +#include + +/* configured in Dockerfile */ +static const char *USERNAME = "libssh2"; +static const char *KEY_FILE_PRIVATE = "key_rsa"; +static const char *KEY_FILE_PUBLIC = "key_rsa.pub"; + +/* Size and number of blocks to transfer + * This needs to be large to increase the chance of timing effects causing + * different code paths to be hit in the unframing code, but not so long that + * the integration tests take too long. 5 seconds of run time is probably a + * reasonable compromise. The block size is an odd number to increase the + * chance that various internal buffer and block boundaries are overlapped. */ +#define XFER_BS 997 +#define XFER_COUNT 140080 + +#define STRINGIFY(x) STRINGIFY2(x) +#define STRINGIFY2(x) #x + +/* command to transfer the desired amount of data */ +#define REMOTE_COMMAND "dd if=/dev/zero bs=" STRINGIFY(XFER_BS) \ + " count=" STRINGIFY(XFER_COUNT) " status=none" + +int test(LIBSSH2_SESSION *session) +{ + int rc; + long xfer_bytes = 0; + LIBSSH2_CHANNEL *channel; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if(userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if(strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), + srcdir_path(KEY_FILE_PUBLIC), srcdir_path(KEY_FILE_PRIVATE), NULL); + if(rc != 0) { + print_last_session_error("libssh2_userauth_publickey_fromfile_ex"); + return 1; + } + + /* Request a session channel on which to run a shell */ + channel = libssh2_channel_open_session(session); + if(!channel) { + fprintf(stderr, "Unable to open a session\n"); + goto shutdown; + } + + /* Send the command to transfer data */ + if(libssh2_channel_exec(channel, REMOTE_COMMAND)) { + fprintf(stderr, "Unable to request command on channel\n"); + goto shutdown; + } + + /* Read data */ + while(!libssh2_channel_eof(channel)) { + char buf[1024]; + ssize_t err = libssh2_channel_read(channel, buf, sizeof(buf)); + if(err < 0) + fprintf(stderr, "Unable to read response: %zd\n", err); + else { + int i; + for(i = 0; i < err; ++i) { + if(buf[i]) { + fprintf(stderr, "Bad data received\n"); + /* Test will fail below due to bad data length */ + break; + } + } + xfer_bytes += i; + } + } + + /* Shut down */ + if(libssh2_channel_close(channel)) + fprintf(stderr, "Unable to close channel\n"); + + if(channel) { + libssh2_channel_free(channel); + channel = NULL; + } + +shutdown: + + /* Test check */ + if(xfer_bytes != XFER_COUNT * XFER_BS) { + fprintf(stderr, "Not enough bytes received: %ld not %ld\n", + xfer_bytes, (long)XFER_COUNT * XFER_BS); + return 1; /* error */ + } + return 0; +}