diff options
Diffstat (limited to 'src/tests/unit_asio_stream.cpp')
-rw-r--r-- | src/tests/unit_asio_stream.cpp | 775 |
1 files changed, 775 insertions, 0 deletions
diff --git a/src/tests/unit_asio_stream.cpp b/src/tests/unit_asio_stream.cpp new file mode 100644 index 000000000..ee80cdba4 --- /dev/null +++ b/src/tests/unit_asio_stream.cpp @@ -0,0 +1,775 @@ +/* +* TLS ASIO Stream Unit Tests +* (C) 2018-2019 Jack Lloyd +* 2018-2019 Hannes Rantzsch, Tim Oesterreich, Rene Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) + +#include <botan/asio_stream.h> +#include <botan/tls_callbacks.h> + +// first boost version to include boost/beast/experimental/test/stream.hpp +#include <boost/version.hpp> +#if BOOST_VERSION >= 106800 + +#include <boost/beast/experimental/test/stream.hpp> +#include <boost/bind.hpp> + +namespace Botan_Tests { + +namespace net = boost::asio; +using error_code = boost::system::error_code; + +constexpr uint8_t TEST_DATA[] = "The story so far: In the beginning the Universe was created. " + "This has made a lot of people very angry and been widely regarded as a bad move."; +constexpr std::size_t TEST_DATA_SIZE = 142; +static_assert(sizeof(TEST_DATA) == TEST_DATA_SIZE, "size of TEST_DATA must match TEST_DATA_SIZE"); + +/** + * Mocked Botan::TLS::Channel. Pretends to perform TLS operations and triggers appropriate callbacks in StreamCore. + */ +class MockChannel + { + public: + MockChannel(Botan::TLS::Callbacks& core) + : m_callbacks(core) + , m_bytes_till_complete_record(TEST_DATA_SIZE) + , m_active(false) {} + + public: + std::size_t received_data(const uint8_t[], std::size_t buf_size) + { + if(m_bytes_till_complete_record <= buf_size) + { + m_callbacks.tls_record_received(0, TEST_DATA, TEST_DATA_SIZE); + m_active = true; // claim to be active once a full record has been received (for handshake test) + return 0; + } + m_bytes_till_complete_record -= buf_size; + return m_bytes_till_complete_record; + } + + void send(const uint8_t buf[], std::size_t buf_size) { m_callbacks.tls_emit_data(buf, buf_size); } + + bool is_active() { return m_active; } + + protected: + Botan::TLS::Callbacks& m_callbacks; + std::size_t m_bytes_till_complete_record; // number of bytes still to read before tls record is completed + bool m_active; + + Botan::TLS::Session_Manager_Noop m_session_manager; + Botan::Null_RNG m_rng; + Botan::TLS::Default_Policy m_policy; + }; + +class ThrowingMockChannel : public MockChannel + { + public: + static boost::system::error_code expected_ec() + { + return Botan::TLS::Alert::UNEXPECTED_MESSAGE; + } + + ThrowingMockChannel(Botan::TLS::Callbacks& core) : MockChannel(core) + { + } + + std::size_t received_data(const uint8_t[], std::size_t) + { + throw Botan::TLS::Unexpected_Message("test_error"); + } + + void send(const uint8_t[], std::size_t) + { + throw Botan::TLS::Unexpected_Message("test_error"); + } + }; + +using TestStream = boost::beast::test::stream; +using FailCount = boost::beast::test::fail_count; + +class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel> + { + public: + template <typename... Args> + AsioStream(Botan::TLS::Context& context, Args&& ... args) + : Stream(context, args...) + { + m_native_handle = std::unique_ptr<MockChannel>(new MockChannel(m_core)); + } + + virtual ~AsioStream() = default; + }; + +class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel> + { + public: + template <typename... Args> + ThrowingAsioStream(Botan::TLS::Context& context, Args&& ... args) + : Stream(context, args...) + { + m_native_handle = std::unique_ptr<ThrowingMockChannel>(new ThrowingMockChannel(m_core)); + } + + virtual ~ThrowingAsioStream() = default; + }; + +/** + * Synchronous tests for Botan::Stream. + * + * This test validates the asynchronous behavior Botan::Stream, including its utility classes StreamCore and Async_*_Op. + * The stream's channel, i.e. TLS_Client or TLS_Server, is mocked and pretends to perform TLS operations (noop) and + * provides the test data to the stream. + * The underlying network socket, claiming it read / wrote a number of bytes. + */ +class Asio_Stream_Tests final : public Test + { + // use memcmp to check if the data in a is a prefix of the data in b + bool contains(const void* a, const void* b, const std::size_t size) { return memcmp(a, b, size) == 0; } + + boost::string_view test_data() const + { + return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE); + } + + void test_sync_handshake(std::vector<Test::Result>& results) + { + net::io_context ioc; + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + + ssl.handshake(Botan::TLS::CLIENT); + + Test::Result result("sync TLS handshake"); + result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true); + results.push_back(result); + } + + void test_sync_handshake_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + ssl.next_layer().connect(remote); + + // mimic handshake initialization + ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE); + + error_code ec; + ssl.handshake(Botan::TLS::CLIENT, ec); + + Test::Result result("sync TLS handshake error"); + result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false); + result.confirm("propagates error code", ec == net::error::eof); + results.push_back(result); + } + + void test_sync_handshake_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc, test_data()); + ssl.next_layer().connect(remote); + + error_code ec; + ssl.handshake(Botan::TLS::CLIENT, ec); + + Test::Result result("sync TLS handshake error"); + result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + results.push_back(result); + } + + void test_async_handshake(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + ssl.next_layer().connect(remote); + + // mimic handshake initialization + ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE); + + Test::Result result("async TLS handshake"); + + auto handler = [&](const error_code&) + { + result.confirm("reads from socket", ssl.next_layer().nread() > 0); + result.confirm("writes from socket", ssl.next_layer().nwrite() > 0); + result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true); + }; + + ssl.async_handshake(Botan::TLS::CLIENT, handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_async_handshake_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + ssl.next_layer().connect(remote); + + // mimic handshake initialization + ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE); + + Test::Result result("async TLS handshake error"); + + auto handler = [&](const error_code &ec) + { + result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false); + result.confirm("propagates error code", ec == net::error::eof); + }; + + ssl.async_handshake(Botan::TLS::CLIENT, handler); + + ioc.run(); + results.push_back(result); + } + + void test_async_handshake_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc, test_data()); + ssl.next_layer().connect(remote); + + Test::Result result("async TLS handshake throw"); + + auto handler = [&](const error_code &ec) + { + result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + }; + + ssl.async_handshake(Botan::TLS::CLIENT, handler); + + ioc.run(); + results.push_back(result); + } + + void test_sync_read_some_success(std::vector<Test::Result>& results) + { + net::io_context ioc; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + + const std::size_t buf_size = 128; + uint8_t buf[buf_size]; + error_code ec; + + auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec); + + Test::Result result("sync read_some success"); + result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size)); + result.test_eq("reads the correct amount of data", bytes_transferred, buf_size); + result.confirm("does not report an error", !ec); + + results.push_back(result); + } + + void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results) + { + net::io_context ioc; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + error_code ec; + + std::vector<net::mutable_buffer> data; + uint8_t buf1[TEST_DATA_SIZE/2]; + uint8_t buf2[TEST_DATA_SIZE/2]; + data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE/2)); + data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE/2)); + + auto bytes_transferred = net::read(ssl, data, ec); + + Test::Result result("sync read_some buffer sequence"); + + result.confirm("reads the correct data", + contains(buf1, TEST_DATA, TEST_DATA_SIZE/2) && + contains(buf2, TEST_DATA+TEST_DATA_SIZE/2, TEST_DATA_SIZE/2)); + result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE); + result.confirm("does not report an error", !ec); + + results.push_back(result); + } + + void test_sync_read_some_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + ssl.next_layer().connect(remote); + + uint8_t buf[128]; + error_code ec; + + auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec); + + Test::Result result("sync read_some error"); + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == net::error::eof); + + results.push_back(result); + } + + void test_sync_read_some_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc, test_data()); + ssl.next_layer().connect(remote); + + uint8_t buf[128]; + error_code ec; + + auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec); + + Test::Result result("sync read_some throw"); + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + + results.push_back(result); + } + + void test_sync_read_zero_buffer(std::vector<Test::Result>& results) + { + net::io_context ioc; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + + const std::size_t buf_size = 128; + uint8_t buf[buf_size]; + error_code ec; + + auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, std::size_t(0)), ec); + + Test::Result result("sync read_some into zero-size buffer"); + result.test_eq("reads the correct amount of data", bytes_transferred, 0); + // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream + // would block here. TestStream sets error_code::eof. + result.confirm("does not report an error", !ec); + + results.push_back(result); + } + + void test_async_read_some_success(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + uint8_t data[TEST_DATA_SIZE]; + + Test::Result result("async read_some success"); + + auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE)); + result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE); + result.confirm("does not report an error", !ec); + }; + + net::mutable_buffer buf {data, TEST_DATA_SIZE}; + net::async_read(ssl, buf, read_handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results) + { + net::io_context ioc; + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, test_data()); + + std::vector<net::mutable_buffer> data; + uint8_t buf1[TEST_DATA_SIZE/2]; + uint8_t buf2[TEST_DATA_SIZE/2]; + data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE/2)); + data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE/2)); + + Test::Result result("async read_some buffer sequence"); + + auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.confirm("reads the correct data", + contains(buf1, TEST_DATA, TEST_DATA_SIZE/2) && + contains(buf2, TEST_DATA+TEST_DATA_SIZE/2, TEST_DATA_SIZE/2)); + result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE); + result.confirm("does not report an error", !ec); + }; + + net::async_read(ssl, data, read_handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_async_read_some_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + uint8_t data[TEST_DATA_SIZE]; + + Test::Result result("async read_some error"); + + auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == net::error::eof); + }; + + net::mutable_buffer buf {data, TEST_DATA_SIZE}; + net::async_read(ssl, buf, read_handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_async_read_some_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc, test_data()); + uint8_t data[TEST_DATA_SIZE]; + + Test::Result result("async read_some throw"); + + auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + }; + + net::mutable_buffer buf {data, TEST_DATA_SIZE}; + net::async_read(ssl, buf, read_handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_async_read_zero_buffer(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + uint8_t data[TEST_DATA_SIZE]; + + Test::Result result("async read_some into zero-size buffer"); + + auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.test_eq("reads the correct amount of data", bytes_transferred, 0); + // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream + // would block here. TestStream sets error_code::eof. + result.confirm("does not report an error", !ec); + }; + + net::mutable_buffer buf {data, std::size_t(0)}; + net::async_read(ssl, buf, read_handler); + + ssl.next_layer().close_remote(); + ioc.run(); + results.push_back(result); + } + + void test_sync_write_some_success(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + error_code ec; + + auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec); + + Test::Result result("sync write_some success"); + result.confirm("writes the correct data", remote.str() == test_data()); + result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE); + result.confirm("does not report an error", !ec); + + results.push_back(result); + } + + void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + error_code ec; + + // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1 + std::array<uint8_t, 17 * 1024 + 1> random_data; + random_data.fill('4'); // chosen by fair dice roll + random_data.back() = '5'; + + std::vector<net::const_buffer> data; + data.emplace_back(net::const_buffer(random_data.data(), 1)); + for(std::size_t i = 1; i < random_data.size(); i += 1024) + { + data.emplace_back(net::const_buffer(random_data.data() + i, 1024)); + } + + auto bytes_transferred = net::write(ssl, data, ec); + + Test::Result result("sync write_some buffer sequence"); + + result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()", + Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size()); + + result.confirm("writes the correct data", + contains(remote.buffer().data().data(), random_data.data(), random_data.size())); + result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size()); + result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2); + result.confirm("does not report an error", !ec); + + results.push_back(result); + } + + void test_sync_write_some_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + ssl.next_layer().connect(remote); + + error_code ec; + + auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec); + + Test::Result result("sync write_some error"); + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == net::error::eof); + + results.push_back(result); + } + + void test_sync_write_some_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + error_code ec; + + auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec); + + Test::Result result("sync write_some throw"); + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + + results.push_back(result); + } + + void test_async_write_some_success(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + + Test::Result result("async write_some success"); + + auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.confirm("writes the correct data", remote.str() == test_data()); + result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE); + result.confirm("does not report an error", !ec); + }; + + net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler); + + ioc.run(); + results.push_back(result); + } + + void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + + // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1 + std::array<uint8_t, 17 * 1024 + 1> random_data; + random_data.fill('4'); // chosen by fair dice roll + random_data.back() = '5'; + + std::vector<net::const_buffer> src; + src.emplace_back(net::const_buffer(random_data.data(), 1)); + for(std::size_t i = 1; i < random_data.size(); i += 1024) + { + src.emplace_back(net::const_buffer(random_data.data() + i, 1024)); + } + + Test::Result result("async write_some buffer sequence"); + + result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()", + Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size()); + + auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.confirm("writes the correct data", + contains(remote.buffer().data().data(), random_data.data(), random_data.size())); + result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size()); + result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2); + result.confirm("does not report an error", !ec); + }; + + net::async_write(ssl, src, write_handler); + + ioc.run(); + results.push_back(result); + } + + void test_async_write_some_error(std::vector<Test::Result>& results) + { + net::io_context ioc; + // fail right away + FailCount fc{0, net::error::eof}; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + AsioStream ssl(ctx, ioc, fc); + ssl.next_layer().connect(remote); + + Test::Result result("async write_some error"); + + auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.test_eq("committed some bytes to the core", bytes_transferred, TEST_DATA_SIZE); + result.confirm("propagates error code", ec == net::error::eof); + }; + + net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler); + + ioc.run(); + results.push_back(result); + } + + void test_async_write_throw(std::vector<Test::Result>& results) + { + net::io_context ioc; + TestStream remote{ioc}; + + Botan::TLS::Context ctx; + ThrowingAsioStream ssl(ctx, ioc); + ssl.next_layer().connect(remote); + + Test::Result result("async write_some throw"); + + auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred) + { + result.test_eq("didn't transfer anything", bytes_transferred, 0); + result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec()); + }; + + net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler); + + ioc.run(); + results.push_back(result); + } + + public: + std::vector<Test::Result> run() override + { + std::vector<Test::Result> results; + + test_sync_handshake(results); + test_sync_handshake_error(results); + test_sync_handshake_throw(results); + + test_async_handshake(results); + test_async_handshake_error(results); + test_async_handshake_throw(results); + + test_sync_read_some_success(results); + test_sync_read_some_buffer_sequence(results); + test_sync_read_some_error(results); + test_sync_read_some_throw(results); + test_sync_read_zero_buffer(results); + + test_async_read_some_success(results); + test_async_read_some_buffer_sequence(results); + test_async_read_some_error(results); + test_async_read_some_throw(results); + test_async_read_zero_buffer(results); + + test_sync_write_some_success(results); + test_sync_write_some_buffer_sequence(results); + test_sync_write_some_error(results); + test_sync_write_some_throw(results); + + test_async_write_some_success(results); + test_async_write_some_buffer_sequence(results); + test_async_write_some_error(results); + test_async_write_throw(results); + + return results; + } + }; + +BOTAN_REGISTER_TEST("tls_asio_stream", Asio_Stream_Tests); + +} // namespace Botan_Tests + +#endif // BOOST_VERSION +#endif // BOTAN_HAS_TLS && BOTAN_HAS_BOOST_ASIO |