/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ #pragma once #include "base/io-engine.hpp" #include "base/tlsstream.hpp" #include "test/remote-certificate-fixture.hpp" #include #include namespace icinga { /** * Creates a pair of TLS Streams on a random unused port. */ struct TlsStreamFixture : CertificateFixture { TlsStreamFixture() { using namespace boost::asio::ip; using handshake_type = boost::asio::ssl::stream_base::handshake_type; auto serverCert = EnsureCertFor("server"); auto clientCert = EnsureCertFor("client"); auto& io = IoEngine::Get().GetIoContext(); m_ClientSslContext = SetupSslContext(clientCert.crtFile, clientCert.keyFile, m_CaCrtFile.string(), "", DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); client = Shared::Make(io, *m_ClientSslContext); m_ServerSslContext = SetupSslContext(serverCert.crtFile, serverCert.keyFile, m_CaCrtFile.string(), "", DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); server = Shared::Make(io, *m_ServerSslContext); std::promise p; tcp::acceptor acceptor{io, tcp::endpoint{address_v4::loopback(), 0}}; acceptor.listen(); acceptor.async_accept(server->lowest_layer(), [&](const boost::system::error_code& ec) { if (ec) { BOOST_TEST_MESSAGE("Server Accept Error: " + ec.message()); p.set_exception(std::make_exception_ptr(boost::system::system_error{ec})); return; } server->next_layer().async_handshake(handshake_type::server, [&](const boost::system::error_code& ec) { if (ec) { BOOST_TEST_MESSAGE("Server Handshake Error: " + ec.message()); p.set_exception(std::make_exception_ptr(boost::system::system_error{ec})); return; } if (!server->next_layer().IsVerifyOK()) { p.set_exception(std::make_exception_ptr(std::runtime_error{"Verify failed on server-side."})); } p.set_value(); }); }); boost::system::error_code ec; if (client->lowest_layer().connect(acceptor.local_endpoint(), ec)) { BOOST_TEST_MESSAGE("Client Connect error: " + ec.message()); BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); } if (client->next_layer().handshake(handshake_type::client, ec)) { BOOST_TEST_MESSAGE("Client Handshake error: " + ec.message()); BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); } if (!client->next_layer().IsVerifyOK()) { throw std::runtime_error{"Verify failed on client-side."}; } auto f = p.get_future(); f.get(); } auto Shutdown(const Shared::Ptr& stream, std::optional yc = {}) { boost::system::error_code ec; if (yc) { stream->next_layer().async_shutdown((*yc)[ec]); } else { stream->next_layer().shutdown(ec); } #if BOOST_VERSION < 107000 /* On boost versions < 1.70, the end-of-file condition was propagated as an error, * even in case of a successful shutdown. This is information can be found in the * changelog for the boost Asio 1.14.0 / Boost 1.70 release. */ if (ec == boost::asio::error::eof) { BOOST_TEST_MESSAGE("Shutdown completed successfully with 'boost::asio::error::eof'."); return boost::test_tools::assertion_result{true}; } #endif boost::test_tools::assertion_result ret{!ec}; ret.message() << "Error: " << ec.message(); return ret; } Shared::Ptr client; Shared::Ptr server; private: Shared::Ptr m_ClientSslContext; Shared::Ptr m_ServerSslContext; }; } // namespace icinga