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; +}