Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ned14/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2022-03-17 16:10:32 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2022-04-06 18:43:45 +0300
commit6fa9b70d22823c49e7885af092ba86c6761b70a4 (patch)
tree25db34313f0d319505436ce3de34a856ad6a5806
parentc5b52a6436878e04958d37fe36c93825bd455038 (diff)
networking: Test authenticated and non-blocking TLS connections.
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/byte_socket_handle.hpp11
-rw-r--r--include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp109
-rw-r--r--include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp32
-rw-r--r--include/llfio/v2.0/tls_socket_handle.hpp2
-rw-r--r--test/tests/tls_socket_handle.cpp185
6 files changed, 250 insertions, 95 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index 996e3062..91700c56 100644
--- a/include/llfio/revision.hpp
+++ b/include/llfio/revision.hpp
@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
-#define LLFIO_PREVIOUS_COMMIT_REF 6fb38a70ae35a3599d2b5de86663bb2f1d47e5d2
-#define LLFIO_PREVIOUS_COMMIT_DATE "2022-02-23 11:09:43 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE 6fb38a70
+#define LLFIO_PREVIOUS_COMMIT_REF 5720755175521bdd85f7361210de2038419b5628
+#define LLFIO_PREVIOUS_COMMIT_DATE "2022-03-17 13:10:37 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE 57207551
diff --git a/include/llfio/v2.0/byte_socket_handle.hpp b/include/llfio/v2.0/byte_socket_handle.hpp
index c4862e89..d0d3d31d 100644
--- a/include/llfio/v2.0/byte_socket_handle.hpp
+++ b/include/llfio/v2.0/byte_socket_handle.hpp
@@ -599,8 +599,11 @@ public:
flag flags = flag::none) noexcept;
//! \brief Convenience function defaulting `flag::multiplexable` set.
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<byte_socket_handle>
- multiplexable_byte_socket(ip::family family, mode _mode = mode::write, caching _caching = caching::all, flag flags = flag::multiplexable) noexcept;
+ static result<byte_socket_handle> multiplexable_byte_socket(ip::family family, mode _mode = mode::write, caching _caching = caching::all,
+ flag flags = flag::multiplexable) noexcept
+ {
+ return byte_socket(family, _mode, _caching, flags);
+ }
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~byte_socket_handle() override
{
@@ -1102,8 +1105,8 @@ public:
caching _caching = caching::all, flag flags = flag::none) noexcept;
//! \brief Convenience function defaulting `flag::multiplexable` set.
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle>
- multiplexable_listening_socket(ip::family _family, mode _mode = mode::write, caching _caching = caching::all, flag flags = flag::multiplexable) noexcept
+ static result<listening_socket_handle> multiplexable_listening_socket(ip::family _family, mode _mode = mode::write, caching _caching = caching::all,
+ flag flags = flag::multiplexable) noexcept
{
return listening_socket(_family, _mode, _caching, flags);
}
diff --git a/include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp b/include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp
index a868650e..e7d3e017 100644
--- a/include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp
+++ b/include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp
@@ -24,16 +24,22 @@ Distributed under the Boost Software License, Version 1.0.
#include "../../../tls_socket_handle.hpp"
-#define LLFIO_OPENSSL_ENABLE_DEBUG_PRINTING 0
+#define LLFIO_OPENSSL_ENABLE_DEBUG_PRINTING 1
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
+#include <openssl/x509.h>
#if LLFIO_OPENSSL_ENABLE_DEBUG_PRINTING
#include <iostream>
#endif
+#ifdef _WIN32
+#include <cryptuiapi.h>
+#pragma comment(lib, "cryptui.lib")
+#endif
+
LLFIO_V2_NAMESPACE_BEGIN
namespace detail
@@ -396,8 +402,14 @@ namespace detail
static struct openssl_default_ctxs_t
{
SSL_CTX *unverified{nullptr}, *verified{nullptr};
+ X509_STORE *certstore{nullptr};
~openssl_default_ctxs_t()
{
+ if(certstore != nullptr)
+ {
+ X509_STORE_free(certstore);
+ certstore = nullptr;
+ }
if(verified != nullptr)
{
SSL_CTX_free(verified);
@@ -417,8 +429,42 @@ namespace detail
QUICKCPPLIB_NAMESPACE::configurable_spinlock::lock_guard<QUICKCPPLIB_NAMESPACE::configurable_spinlock::spinlock<unsigned>> g(lock);
if(verified == nullptr)
{
- auto make_ctx = [](bool verify_peer) -> result<SSL_CTX *>
+#ifdef _WIN32
+ // Create an OpenSSL certificate store made up of the certs from the Windows certificate store
+ HCERTSTORE winstore = CertOpenSystemStoreW(NULL, L"ROOT");
+ if(!winstore)
+ {
+ return win32_error();
+ }
+ auto unwinstore = make_scope_exit([&]() noexcept { CertCloseStore(winstore, 0); });
+ certstore = X509_STORE_new();
+ if(!certstore)
{
+ return openssl_error(nullptr);
+ }
+ PCCERT_CONTEXT context = nullptr;
+ auto uncontext = make_scope_exit([&]() noexcept {
+ if(context != nullptr)
+ {
+ CertFreeCertificateContext(context);
+ }
+ });
+ while((context = CertEnumCertificatesInStore(winstore, context)) != nullptr)
+ {
+ const unsigned char *in = (const unsigned char *) context->pbCertEncoded;
+ X509 *x509 = d2i_X509(nullptr, &in, context->cbCertEncoded);
+ if(!x509)
+ {
+ return openssl_error(nullptr);
+ }
+ auto unx509 = make_scope_exit([&]() noexcept { X509_free(x509); });
+ if(X509_STORE_add_cert(certstore, x509) <= 0)
+ {
+ return openssl_error(nullptr);
+ }
+ }
+#endif
+ auto make_ctx = [certstore = certstore](bool verify_peer) -> result<SSL_CTX *> {
SSL_CTX *_ctx = SSL_CTX_new(TLS_method());
if(_ctx == nullptr)
{
@@ -434,13 +480,17 @@ namespace detail
}
else
{
+ if(certstore != nullptr)
+ {
+ SSL_CTX_set1_cert_store(_ctx, certstore);
+ }
if(!SSL_CTX_set_cipher_list(_ctx, openssl_verified_cipher_list))
{
return openssl_error(nullptr).as_failure();
}
SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, nullptr);
SSL_CTX_set_verify_depth(_ctx, 4);
- if(!SSL_CTX_set_default_verify_paths(_ctx))
+ if(SSL_CTX_set_default_verify_paths(_ctx) <= 0)
{
return openssl_error(nullptr).as_failure();
}
@@ -670,7 +720,7 @@ protected:
LLFIO_DEADLINE_TO_SLEEP_INIT(d);
if(_ssl_bio == nullptr)
{
- OUTCOME_TRY(_init(true, _authentication_certificates_path && !_authentication_certificates_path->empty()));
+ OUTCOME_TRY(_init(true, _authentication_certificates_path));
}
if(!(_v.behaviour & native_handle_type::disposition::_is_connected))
{
@@ -747,9 +797,10 @@ public:
this->_v.behaviour = (sock->native_handle().behaviour & ~(native_handle_type::disposition::kernel_handle)) | native_handle_type::disposition::is_pointer;
}
- result<void> _init(bool is_client, bool verify_peer) noexcept
+ result<void> _init(bool is_client, const optional<filesystem::path> &certpath) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
+ const bool verify_peer = (is_client && (!certpath.has_value() || !certpath->empty())) || (!is_client && (certpath.has_value() || !certpath->empty()));
OUTCOME_TRY(detail::openssl_default_ctxs.init());
assert(_ssl_bio == nullptr);
_ssl_bio = BIO_new_ssl(verify_peer ? detail::openssl_default_ctxs.verified : detail::openssl_default_ctxs.unverified, is_client);
@@ -757,6 +808,23 @@ public:
{
return openssl_error(this).as_failure();
}
+ if(certpath.has_value() && !certpath->empty())
+ {
+ SSL *ssl{nullptr};
+ BIO_get_ssl(_ssl_bio, &ssl);
+ if(ssl == nullptr)
+ {
+ return openssl_error(this).as_failure();
+ }
+ if(SSL_use_certificate_file(ssl, certpath->string().c_str(), SSL_FILETYPE_PEM) <= 0)
+ {
+ return openssl_error(this).as_failure();
+ }
+ if(SSL_use_PrivateKey_file(ssl, certpath->string().c_str(), SSL_FILETYPE_PEM) <= 0)
+ {
+ return openssl_error(this).as_failure();
+ }
+ }
_self_bio = BIO_new(detail::openssl_custom_bio.method);
if(_self_bio == nullptr)
{
@@ -962,28 +1030,28 @@ public:
_connect_hostname_port.assign(host.data(), host.size());
_connect_hostname_port.push_back(':');
_connect_hostname_port.append(std::to_string(port));
- auto res = BIO_set_conn_hostname(_ssl_bio, _connect_hostname_port.c_str());
- if(res != 1)
- {
- return openssl_error(this).as_failure();
- }
if(_ctx == nullptr)
{
- OUTCOME_TRY(_init(true, true));
+ OUTCOME_TRY(_init(true, _authentication_certificates_path));
}
+ auto res = BIO_set_conn_hostname(_ssl_bio, _connect_hostname_port.c_str());
+ /* if(res != 1)
+ {
+ return openssl_error(this).as_failure();
+ }*/
SSL *ssl{nullptr};
BIO_get_ssl(_ssl_bio, &ssl);
if(ssl == nullptr)
{
return openssl_error(this).as_failure();
}
- auto hostname = _connect_hostname_port.substr(0, _connect_hostname_port.rfind(':'));
+ std::string hostname(host);
res = SSL_set_tlsext_host_name(ssl, hostname.c_str());
if(res != 1)
{
return openssl_error(this).as_failure();
}
- return _connect_hostname_port;
+ return string_view(_connect_hostname_port).substr(host.size() + 1);
}
catch(...)
{
@@ -995,13 +1063,11 @@ public:
int _bread(BIO *bio, char *buffer, size_t bytes, size_t *read)
{
LLFIO_LOG_FUNCTION_CALL(this);
- auto ret = [=]() mutable
- {
+ auto ret = [=]() mutable {
assert(_lock_holder.owns_lock());
*read = 0;
BIO_clear_retry_flags(bio);
- auto copy_out = [&]
- {
+ auto copy_out = [&] {
while(!_toread_source_empty() && bytes > 0)
{
auto s = _toread_source();
@@ -1036,7 +1102,7 @@ public:
}
return 1;
}
- auto remaining = (size_t) (((*s.first)->data() + (*s.first)->size()) - (s.second->data() + s.second->size()));
+ auto remaining = (size_t)(((*s.first)->data() + (*s.first)->size()) - (s.second->data() + s.second->size()));
byte_socket_handle::buffer_type b{s.second->data() + s.second->size(), remaining};
auto &began_steady = _read_deadline_began_steady;
deadline nd;
@@ -1096,8 +1162,7 @@ public:
int _bwrite(BIO *bio, const char *buffer, size_t bytes, size_t *written)
{
LLFIO_LOG_FUNCTION_CALL(this);
- auto ret = [=]() mutable
- {
+ auto ret = [=]() mutable {
assert(_lock_holder.owns_lock());
*written = 0;
BIO_clear_retry_flags(bio);
@@ -1205,7 +1270,7 @@ protected:
}
req.buffers.connected_socket() = {tls_socket_handle_ptr(p), read.connected_socket().second};
OUTCOME_TRY(p->set_registered_buffer_chunk_size(_registered_buffer_chunk_size));
- OUTCOME_TRY(p->_init(false, !_authentication_certificates_path || !_authentication_certificates_path->empty()));
+ OUTCOME_TRY(p->_init(false, _authentication_certificates_path));
return {std::move(req.buffers)};
}
@@ -1225,7 +1290,7 @@ protected:
}
req.buffers.connected_socket() = {tls_socket_handle_ptr(p), read.connected_socket().second};
OUTCOME_TRY(p->set_registered_buffer_chunk_size(_registered_buffer_chunk_size));
- OUTCOME_TRY(p->_init(false, !_authentication_certificates_path || !_authentication_certificates_path->empty()));
+ OUTCOME_TRY(p->_init(false, _authentication_certificates_path));
return {std::move(req.buffers)};
}
diff --git a/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp b/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp
index 620d9aa6..a7f57e66 100644
--- a/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp
@@ -88,6 +88,7 @@ namespace ip
OVERLAPPED ol;
::ADDRINFOEXW *res{nullptr};
HANDLE ophandle{nullptr};
+ bool done{false};
resolver_impl() { clear(); }
@@ -107,12 +108,13 @@ namespace ip
res = nullptr;
}
ophandle = nullptr;
+ done = false;
}
// Returns 0 for not ready yet, -1 for already processed, +1 for just processed
int check(DWORD millis)
{
- if(0 != WaitForSingleObject(ol.hEvent, millis))
+ if(!done && 0 != WaitForSingleObject(ol.hEvent, millis))
{
return 0;
}
@@ -120,6 +122,7 @@ namespace ip
{
return -1;
}
+ done = true;
auto unaddrinfo = make_scope_exit(
[&]() noexcept
{
@@ -188,7 +191,7 @@ namespace ip
void resolver_deleter::operator()(resolver *_p) const
{
auto *p = static_cast<resolver_impl *>(_p);
- if(0 != WaitForSingleObject(p->ol.hEvent, 0))
+ if(!p->done && 0 != WaitForSingleObject(p->ol.hEvent, 0))
{
GetAddrInfoExCancel(&p->ophandle);
WaitForSingleObject(p->ol.hEvent, INFINITE);
@@ -220,7 +223,7 @@ namespace ip
bool resolver::incomplete() const noexcept
{
auto *self = static_cast<const detail::resolver_impl *>(this);
- return 0 != WaitForSingleObject(self->ol.hEvent, 0);
+ return !self->done && 0 != WaitForSingleObject(self->ol.hEvent, 0);
}
result<span<address>> resolver::get() noexcept
{
@@ -326,9 +329,15 @@ namespace ip
_timeout.tv_usec = (long) (diff % 1000000);
}
timeout = &_timeout;
+ // Can't combine blocking and timeouts
+ flags &= ~resolve_flag::blocking;
}
p->hints.ai_socktype = SOCK_STREAM;
- p->hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
+ p->hints.ai_flags = AI_ADDRCONFIG;
+ if(_family != family::v4)
+ {
+ p->hints.ai_flags |= AI_V4MAPPED;
+ }
if(flags & resolve_flag::passive)
{
p->hints.ai_flags |= AI_PASSIVE;
@@ -343,24 +352,29 @@ namespace ip
return ntkernel_error(ntstat);
}
std::wstring ret;
- ret.resize(written);
+ ret.resize(written / sizeof(wchar_t));
written = 0;
// Do the conversion UTF-8 to UTF-16
- ntstat = RtlUTF8ToUnicodeN(const_cast<wchar_t *>(ret.data()), static_cast<ULONG>(ret.size()), &written, str.c_str(), static_cast<ULONG>(str.size()));
+ ntstat = RtlUTF8ToUnicodeN(const_cast<wchar_t *>(ret.data()), static_cast<ULONG>(ret.size() * sizeof(wchar_t)), &written, str.c_str(),
+ static_cast<ULONG>(str.size()));
if(ntstat < 0)
{
return ntkernel_error(ntstat);
}
- ret.resize(written);
+ ret.resize(written / sizeof(wchar_t));
return ret;
};
OUTCOME_TRY(auto &&_name, to_wstring(p->name));
OUTCOME_TRY(auto &&_service, to_wstring(p->service));
auto errcode = GetAddrInfoExW(_name.c_str(), _service.c_str(), NS_ALL, nullptr, &p->hints, &p->res, timeout,
(flags & resolve_flag::blocking) ? nullptr : &p->ol, nullptr, (flags & resolve_flag::blocking) ? nullptr : &p->ophandle);
- if(NO_ERROR != errcode && WSA_IO_PENDING != errcode)
+ if(NO_ERROR == errcode)
+ {
+ p->done = true;
+ }
+ else if(WSA_IO_PENDING != errcode)
{
- SetEvent(p->ol.hEvent);
+ p->done = true;
return win32_error(errcode);
}
p->check(0);
diff --git a/include/llfio/v2.0/tls_socket_handle.hpp b/include/llfio/v2.0/tls_socket_handle.hpp
index 418ea068..3e87705a 100644
--- a/include/llfio/v2.0/tls_socket_handle.hpp
+++ b/include/llfio/v2.0/tls_socket_handle.hpp
@@ -147,7 +147,7 @@ public:
{
deadline nd;
LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
- lasterror = byte_socket_handle::connect(address, nd);
+ lasterror = this->connect(address, nd);
if(lasterror)
{
return lasterror;
diff --git a/test/tests/tls_socket_handle.cpp b/test/tests/tls_socket_handle.cpp
index ee78320c..591525da 100644
--- a/test/tests/tls_socket_handle.cpp
+++ b/test/tests/tls_socket_handle.cpp
@@ -89,7 +89,7 @@ static inline void TestBlockingTLSSocketHandles()
g.unlock();
serversocket->close().value();
llfio::byte buffer[64];
- auto read = s.first->read(0, {{buffer, 64}}).value();
+ auto read = s.first->read({{buffer, 64}}).value();
g.lock();
std::cout << "\nThe inbound server socket negotiated the cipher " << s.first->algorithms_description() << std::endl;
BOOST_REQUIRE(read == 5);
@@ -123,7 +123,7 @@ static inline void TestBlockingTLSSocketHandles()
BOOST_CHECK(writer->is_writable());
std::cout << "\nThe connecting socket negotiated the cipher " << writer->algorithms_description() << std::endl;
g.unlock();
- auto written = writer->write(0, {{(const llfio::byte *) "hello", 5}}).value();
+ auto written = writer->write({{(const llfio::byte *) "hello", 5}}).value();
BOOST_REQUIRE(written == 5);
writer->shutdown_and_close().value();
readerthread.get();
@@ -137,74 +137,144 @@ static inline void TestBlockingTLSSocketHandles()
runtest(tls_socket_source->wrap(&rawserversocket).value(), tls_socket_source->wrap(&rawwriter).value());
}
-#if 0
static inline void TestNonBlockingTLSSocketHandles()
{
namespace llfio = LLFIO_V2_NAMESPACE;
- auto serversocket = llfio::listening_socket_handle::listening_socket(llfio::ip::family::v4, llfio::listening_socket_handle::mode::read,
- llfio::byte_socket_handle::caching::all, llfio::byte_socket_handle::flag::multiplexable)
- .value();
- BOOST_REQUIRE(serversocket.is_valid());
- BOOST_CHECK(serversocket.is_socket());
- BOOST_CHECK(serversocket.is_readable());
- BOOST_CHECK(!serversocket.is_writable());
- serversocket.bind(llfio::ip::address_v4::loopback()).value();
- auto endpoint = serversocket.local_endpoint().value();
- std::cout << "Server socket is listening on " << endpoint << std::endl;
- if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ if(llfio::tls_socket_source_registry::empty())
{
- std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
- << std::endl;
+ std::cout << "\nNOTE: This platform has no TLS socket sources in its registry, skipping this test." << std::endl;
return;
}
+ auto tls_socket_source = llfio::tls_socket_source_registry::default_source().instantiate().value();
+ auto runtest = [](llfio::listening_tls_socket_handle_ptr serversocket, auto &&make_writer)
+ {
+ BOOST_REQUIRE(serversocket->is_valid());
+ BOOST_CHECK(serversocket->is_socket());
+ BOOST_CHECK(serversocket->is_readable());
+ BOOST_CHECK(serversocket->is_writable());
+ // Disable server authentication
+ serversocket->set_authentication_certificates_path({}).value();
+ serversocket->bind(llfio::ip::address_v4::loopback()).value();
+ auto endpoint = serversocket->local_endpoint().value();
+ std::cout << "Server socket is listening on " << endpoint << std::endl;
+ if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ {
+ std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
+ << std::endl;
+ return;
+ }
- std::pair<llfio::byte_socket_handle, llfio::ip::address> reader;
- { // no incoming, so non-blocking read should time out
- auto read = serversocket.read({reader}, std::chrono::milliseconds(0));
- BOOST_REQUIRE(read.has_error());
- BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
- }
- { // no incoming, so blocking read should time out
- auto read = serversocket.read({reader}, std::chrono::seconds(1));
- BOOST_REQUIRE(read.has_error());
- BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
- }
+ std::pair<llfio::tls_socket_handle_ptr, llfio::ip::address> reader;
+ { // no incoming, so non-blocking read should time out
+ auto read = serversocket->read({reader}, std::chrono::milliseconds(0));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ { // no incoming, so blocking read should time out
+ auto read = serversocket->read({reader}, std::chrono::seconds(1));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+
+ // Form the connection.
+ llfio::tls_socket_handle_ptr writer = make_writer();
+ writer->connect(endpoint).value();
+ serversocket->read({reader}, std::chrono::seconds(1)).value();
+ std::cout << "Server socket sees incoming connection from " << reader.second << std::endl;
+
+ llfio::byte buffer[64];
+ { // no data, so non-blocking read should time out
+ auto read = reader.first->read(0, {{buffer, 64}}, std::chrono::milliseconds(0));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ { // no data, so blocking read should time out
+ auto read = reader.first->read(0, {{buffer, 64}}, std::chrono::seconds(1));
+ if(!read.has_error())
+ {
+ std::cout << "Blocking read did not return error, instead returned " << read.value() << std::endl;
+ }
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ auto written = writer->write(0, {{(const llfio::byte *) "hello", 5}}).value();
+ BOOST_REQUIRE(written == 5);
+ // writer.shutdown_and_close().value(); // would block until socket drained by reader
+ // writer.close().value(); // would cause all further reads to fail due to socket broken
+ auto read = reader.first->read(0, {{buffer, 64}}, std::chrono::milliseconds(1));
+ BOOST_REQUIRE(read.value() == 5);
+ BOOST_CHECK(0 == memcmp(buffer, "hello", 5));
+ writer->shutdown_and_close().value(); // must not block nor fail
+ writer->close().value();
+ reader.first->close().value();
+ };
+ std::cout << "\nUnwrapped TLS socket:\n" << std::endl;
+ runtest(tls_socket_source->multiplexable_listening_socket(llfio::ip::family::v4).value(),
+ [&] { return tls_socket_source->multiplexable_connecting_socket(llfio::ip::family::v4).value(); });
- // Form the connection.
- auto writer = llfio::byte_socket_handle::byte_socket(llfio::ip::family::v4, llfio::byte_socket_handle::mode::append,
- llfio::byte_socket_handle::caching::reads, llfio::byte_socket_handle::flag::multiplexable)
- .value();
- writer.connect(endpoint).value();
- serversocket.read({reader}, std::chrono::seconds(1)).value();
- std::cout << "Server socket sees incoming connection from " << reader.second << std::endl;
+ std::cout << "\nWrapped TLS socket:\n" << std::endl;
+ auto rawserversocket = llfio::listening_socket_handle::multiplexable_listening_socket(llfio::ip::family::v4).value();
+ auto rawwriter = llfio::byte_socket_handle::multiplexable_byte_socket(llfio::ip::family::v4).value();
+ runtest(tls_socket_source->wrap(&rawserversocket).value(), [&] { return tls_socket_source->wrap(&rawwriter).value(); });
+}
- llfio::byte buffer[64];
- { // no data, so non-blocking read should time out
- auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::milliseconds(0));
- BOOST_REQUIRE(read.has_error());
- BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+/* This test makes the assumption that the host OS is able to validate github.com's
+TLS certificate.
+*/
+static inline void TestAuthenticatingTLSSocketHandles()
+{
+ static constexpr const char *test_host = "github.com";
+ static constexpr const char *get_request = R"(GET / HTTP/1.0
+Host: github.com
+
+)";
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ if(llfio::tls_socket_source_registry::empty())
+ {
+ std::cout << "\nNOTE: This platform has no TLS socket sources in its registry, skipping this test." << std::endl;
+ return;
+ }
+ auto test_host_ip = llfio::ip::resolve(test_host, "https", llfio::ip::family::any, {}, llfio::ip::resolve_flag::blocking).value()->get().value();
+ std::cout << "The IP address of " << test_host << " is " << test_host_ip.front() << std::endl;
+ auto tls_socket_source = llfio::tls_socket_source_registry::default_source().instantiate().value();
+ auto sock = tls_socket_source->multiplexable_connecting_socket(llfio::ip::family::any).value();
+ {
+ auto r = sock->connect(test_host, 443, std::chrono::seconds(5));
+ if(!r)
+ {
+ if(r.error() == llfio::errc::timed_out || r.error() == llfio::errc::host_unreachable || r.error() == llfio::errc::network_unreachable)
+ {
+ std::cout << "\nNOTE: Failed to connect to " << test_host
+ << " within five seconds, assuming there is no internet connection and skipping this test. Error was: " << r.error().message() << std::endl;
+ return;
+ }
+ r.value();
+ }
}
- { // no data, so blocking read should time out
- auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::seconds(1));
- if(!read.has_error())
+ // Get the front page
+ std::cout << "\nThe socket which connected to " << test_host << " negotiated the cipher " << sock->algorithms_description() << std::endl;
+ auto written = sock->write({{(const llfio::byte *) get_request, strlen(get_request)}}).value();
+ BOOST_REQUIRE(written == strlen(get_request));
+ // Fetch the front page. The connection will close once all data is sent.
+ std::vector<char> buffer(4096);
+ size_t offset = 0;
+ for(size_t readed = 0; (readed = sock->read({{(llfio::byte *) buffer.data() + offset, buffer.size() - offset}}, std::chrono::seconds(3)).value()) > 0;)
+ {
+ offset += readed;
+ if(buffer.size() - offset < 1024)
{
- std::cout << "Blocking read did not return error, instead returned " << read.value() << std::endl;
+ buffer.resize(buffer.size() + 4096);
}
- BOOST_REQUIRE(read.has_error());
- BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
}
- auto written = writer.write(0, {{(const llfio::byte *) "hello", 5}}).value();
- BOOST_REQUIRE(written == 5);
- // writer.shutdown_and_close().value(); // would block until socket drained by reader
- // writer.close().value(); // would cause all further reads to fail due to socket broken
- auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::milliseconds(1));
- BOOST_REQUIRE(read.value() == 5);
- BOOST_CHECK(0 == memcmp(buffer, "hello", 5));
- writer.shutdown_and_close().value(); // must not block nor fail
- writer.close().value();
- reader.first.close().value();
+ buffer.resize(offset);
+ std::cout << "\nRead from " << test_host << " " << offset << " bytes. The first 1024 bytes are:\n\n"
+ << llfio::string_view(buffer.data(), offset).substr(0, 1024) << "\n"
+ << std::endl;
+ // Make sure this doesn't hang because the socket is closed
+ sock->shutdown_and_close().value();
}
+#if 0
#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
static inline void TestMultiplexedTLSSocketHandles()
{
@@ -675,9 +745,12 @@ static inline void TestPollingTLSSocketHandles()
KERNELTEST_TEST_KERNEL(integration, llfio, tls_socket_handle, blocking, "Tests that blocking llfio::tls_byte_socket_handle works as expected",
TestBlockingTLSSocketHandles())
-#if 0
KERNELTEST_TEST_KERNEL(integration, llfio, tls_socket_handle, nonblocking, "Tests that nonblocking llfio::tls_byte_socket_handle works as expected",
TestNonBlockingTLSSocketHandles())
+KERNELTEST_TEST_KERNEL(integration, llfio, tls_socket_handle, authenticating,
+ "Tests that connecting to an authenticating server using llfio::tls_byte_socket_handle works as expected",
+ TestAuthenticatingTLSSocketHandles())
+#if 0
#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
KERNELTEST_TEST_KERNEL(integration, llfio, tls_socket_handle, multiplexed, "Tests that multiplexed llfio::tls_byte_socket_handle works as expected",
TestMultiplexedTLSSocketHandles())