diff options
author | Håvard Pettersen <havardpe@oath.com> | 2018-10-09 10:30:24 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2018-10-09 10:32:05 +0000 |
commit | 58850f92f00b6289ff5feccf3634a34943e760d2 (patch) | |
tree | db6b2ba5b94fd2776608f4a8f2da18891330e270 /vespalib | |
parent | 71f6af23c4bccee32cc33e9a3a391b7b9cf96629 (diff) |
added simple test for various crypto sockets
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vespalib/src/tests/net/crypto_socket/CMakeLists.txt | 8 | ||||
-rw-r--r-- | vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp | 190 |
3 files changed, 199 insertions, 0 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index 58f2cbc8891..264f274db15 100644 --- a/vespalib/CMakeLists.txt +++ b/vespalib/CMakeLists.txt @@ -54,6 +54,7 @@ vespa_define_module( src/tests/make_fixture_macros src/tests/memory src/tests/net/async_resolver + src/tests/net/crypto_socket src/tests/net/selector src/tests/net/send_fd src/tests/net/socket diff --git a/vespalib/src/tests/net/crypto_socket/CMakeLists.txt b/vespalib/src/tests/net/crypto_socket/CMakeLists.txt new file mode 100644 index 00000000000..52e64abc128 --- /dev/null +++ b/vespalib/src/tests/net/crypto_socket/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_crypto_socket_test_app TEST + SOURCES + crypto_socket_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_crypto_socket_test_app COMMAND vespalib_crypto_socket_test_app) diff --git a/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp b/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp new file mode 100644 index 00000000000..91babc51476 --- /dev/null +++ b/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp @@ -0,0 +1,190 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/net/crypto_engine.h> +#include <vespa/vespalib/net/tls/tls_crypto_engine.h> +#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h> +#include <vespa/vespalib/net/crypto_socket.h> +#include <vespa/vespalib/net/selector.h> +#include <vespa/vespalib/net/server_socket.h> +#include <vespa/vespalib/net/socket_handle.h> +#include <vespa/vespalib/net/socket_spec.h> +#include <vespa/vespalib/data/smart_buffer.h> +#include <vespa/vespalib/test/make_tls_options_for_testing.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> + +using namespace vespalib; + +struct SocketPair { + SocketHandle client; + SocketHandle server; + SocketPair() : client(), server() { + int sockets[2]; + ASSERT_EQUAL(0, socketpair(AF_UNIX, SOCK_STREAM | O_NONBLOCK, 0, sockets)); + client.reset(sockets[0]); + server.reset(sockets[1]); + } +}; + +//----------------------------------------------------------------------------- + +bool is_blocked(int res) { + return ((res == -1) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))); +} + +void read(CryptoSocket &socket, SmartBuffer &buffer) { + size_t chunk_size = std::max(size_t(4096), socket.min_read_buffer_size()); + auto chunk = buffer.reserve(chunk_size); + int res = socket.read(chunk.data, chunk.size); + if (res > 0) { + buffer.commit(res); + } else { + ASSERT_TRUE(is_blocked(res)); + } +} + +void drain(CryptoSocket &socket, SmartBuffer &buffer) { + int res; + size_t chunk_size = std::max(size_t(4096), socket.min_read_buffer_size()); + do { + auto chunk = buffer.reserve(chunk_size); + res = socket.drain(chunk.data, chunk.size); + if (res > 0) { + buffer.commit(res); + } + } while (res > 0); + ASSERT_EQUAL(res, 0); +} + +void write(CryptoSocket &socket, SmartBuffer &buffer) { + auto chunk = buffer.obtain(); + auto res = socket.write(chunk.data, chunk.size); + if (res > 0) { + buffer.evict(res); + } else { + ASSERT_TRUE(is_blocked(res)); + } +} + +void flush(CryptoSocket &socket) { + int res = 1; + while (res > 0) { + res = socket.flush(); + } + ASSERT_TRUE((res == 0) || is_blocked(res)); +} + +//----------------------------------------------------------------------------- + +vespalib::string read_bytes(CryptoSocket &socket, SmartBuffer &read_buffer, size_t wanted_bytes) { + SingleFdSelector selector(socket.get_fd()); + while (read_buffer.obtain().size < wanted_bytes) { + ASSERT_TRUE(selector.wait_readable()); + read(socket, read_buffer); + drain(socket, read_buffer); + } + auto data = read_buffer.obtain(); + return vespalib::string(data.data, wanted_bytes); +} + +//----------------------------------------------------------------------------- + +void write_bytes(CryptoSocket &socket, const vespalib::string &message) { + SmartBuffer write_buffer(message.size()); + SingleFdSelector selector(socket.get_fd()); + auto data = write_buffer.reserve(message.size()); + memcpy(data.data, message.data(), message.size()); + write_buffer.commit(message.size()); + while (write_buffer.obtain().size > 0) { + ASSERT_TRUE(selector.wait_writable()); + write(socket, write_buffer); + flush(socket); + } +} + +//----------------------------------------------------------------------------- + +void verify_socket_io(CryptoSocket &socket, SmartBuffer &read_buffer, bool is_server) { + vespalib::string client_message = "please pick up, I need to talk to you"; + vespalib::string server_message = "hello, this is the server speaking"; + if(is_server) { + vespalib::string read = read_bytes(socket, read_buffer, client_message.size()); + write_bytes(socket, server_message); + EXPECT_EQUAL(client_message, read); + } else { + write_bytes(socket, client_message); + vespalib::string read = read_bytes(socket, read_buffer, server_message.size()); + EXPECT_EQUAL(server_message, read); + } +} + +//----------------------------------------------------------------------------- + +void verify_handshake(CryptoSocket &socket) { + bool done = false; + SingleFdSelector selector(socket.get_fd()); + while (!done) { + auto res = socket.handshake(); + ASSERT_TRUE(res != CryptoSocket::HandshakeResult::FAIL); + switch (res) { + case CryptoSocket::HandshakeResult::FAIL: + case CryptoSocket::HandshakeResult::DONE: + done = true; + break; + case CryptoSocket::HandshakeResult::NEED_READ: + ASSERT_TRUE(selector.wait_readable()); + break; + case CryptoSocket::HandshakeResult::NEED_WRITE: + ASSERT_TRUE(selector.wait_writable()); + break; + } + } +} + +//----------------------------------------------------------------------------- + +void verify_crypto_socket(SocketPair &sockets, CryptoEngine &engine, bool is_server) { + SocketHandle &my_handle = is_server ? sockets.server : sockets.client; + my_handle.set_blocking(false); + SmartBuffer read_buffer(4096); + CryptoSocket::UP my_socket = engine.create_crypto_socket(std::move(my_handle), is_server); + TEST_DO(verify_handshake(*my_socket)); + drain(*my_socket, read_buffer); + TEST_DO(verify_socket_io(*my_socket, read_buffer, is_server)); +} + +//----------------------------------------------------------------------------- + +TEST_MT_FFF("require that encrypted async socket io works with NullCryptoEngine", + 2, SocketPair(), NullCryptoEngine(), TimeBomb(60)) +{ + TEST_DO(verify_crypto_socket(f1, f2, (thread_id == 0))); +} + +TEST_MT_FFF("require that encrypted async socket io works with XorCryptoEngine", + 2, SocketPair(), XorCryptoEngine(), TimeBomb(60)) +{ + TEST_DO(verify_crypto_socket(f1, f2, (thread_id == 0))); +} + +TEST_MT_FFF("require that encrypted async socket io works with TlsCryptoEngine", + 2, SocketPair(), TlsCryptoEngine(vespalib::test::make_tls_options_for_testing()), TimeBomb(60)) +{ + TEST_DO(verify_crypto_socket(f1, f2, (thread_id == 0))); +} + +TEST_MT_FFF("require that encrypted async socket io works with MaybeTlsCryptoEngine(true)", + 2, SocketPair(), MaybeTlsCryptoEngine(std::make_shared<TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing()), true), TimeBomb(60)) +{ + TEST_DO(verify_crypto_socket(f1, f2, (thread_id == 0))); +} + +TEST_MT_FFF("require that encrypted async socket io works with MaybeTlsCryptoEngine(false)", + 2, SocketPair(), MaybeTlsCryptoEngine(std::make_shared<TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing()), false), TimeBomb(60)) +{ + TEST_DO(verify_crypto_socket(f1, f2, (thread_id == 0))); +} + +TEST_MAIN() { TEST_RUN_ALL(); } |