diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2022-05-11 23:34:02 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2022-05-11 23:34:02 +0300 |
commit | 55696ec43579b34e22e5ed9a2680cbb8c72442d3 (patch) | |
tree | bf0d2fba31a9033fae0be5310d151ee9dcd0b169 | |
parent | d06bf6d6c2e1a23fb9eee3cb0c1056abc52de471 (diff) |
Fix warnings pre-C++20 recently introduced and bring TLS sockets reference implementation closer to the WG21 proposal paper.
-rw-r--r-- | example/.clang-format | 57 | ||||
-rw-r--r-- | example/use_cases.cpp | 195 | ||||
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/tls_socket_sources/openssl.ipp | 14 | ||||
-rw-r--r-- | include/llfio/v2.0/tls_socket_handle.hpp | 10 |
5 files changed, 275 insertions, 7 deletions
diff --git a/example/.clang-format b/example/.clang-format new file mode 100644 index 00000000..2ae6fd03 --- /dev/null +++ b/example/.clang-format @@ -0,0 +1,57 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: true +BinPackParameters: true +ColumnLimit: 78 +CommentPragmas: '^!<' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ContinuationIndentWidth: 0 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IndentCaseLabels: false +IndentFunctionDeclarationAfterType: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +MaxEmptyLinesToKeep: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +Standard: Cpp11 +SpaceAfterCStyleCast: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: Never +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +TabWidth: 8 +UseTab: Never +... + diff --git a/example/use_cases.cpp b/example/use_cases.cpp index 334e1cc6..0269c68b 100644 --- a/example/use_cases.cpp +++ b/example/use_cases.cpp @@ -1,5 +1,5 @@ /* Examples of LLFIO use -(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (2 commits) +(C) 2018 - 2022 Niall Douglas <http://www.nedproductions.biz/> (2 commits) File Created: Aug 2018 @@ -307,6 +307,199 @@ void sparse_array() } #endif +void tls_socket_server() +{ + //! [tls_socket_server] + namespace llfio = LLFIO_V2_NAMESPACE; + std::string host; // set to one of my network cards + + // Get me a FIPS 140 compliant source of TLS sockets which uses kernel + // sockets, this call takes bits set and bits masked, so the implementation + // features bitfield is masked for the FIPS 140 bit, and only if that is set + // is the TLS socket source considered. + // + // The need for a kernel sockets based TLS implementation is so poll() will + // work, as it requires kernel sockets compatible input. + static constexpr auto required_features = + llfio::tls_socket_source_implementation_features::FIPS_140_2 + | llfio::tls_socket_source_implementation_features::kernel_sockets; + llfio::tls_socket_source_ptr tls_socket_source = + llfio::tls_socket_source_registry::default_source(required_features, + required_features).instantiate().value(); + + // Create a listening TLS socket on any IP family. + llfio::listening_tls_socket_handle_ptr serversocket = tls_socket_source + ->multiplexable_listening_socket(llfio::ip::family::any).value(); + + // The default is to NOT authenticate the TLS certificates of those connecting + // in with the system's TLS certificate store. If you wanted something + // different, you'd set that now using: + // serversocket->set_authentication_certificates_path() + + // Bind the listening TLS socket to port 8989 on the NIC described by host + // ip::address incidentally is inspired by ASIO's class, it is very + // similar. I only made it more constexpr. + serversocket->bind(llfio::ip::make_address(host+":8989").value()).value(); + + // poll() works on three spans. We deliberately have separate poll_what + // storage so we can avoid having to reset topoll's contents per poll() + std::vector<llfio::pollable_handle *> pollable_handles; + std::vector<llfio::poll_what> topoll, whatchanged; + + // We will want to know about new inbound connections to our listening + // socket + pollable_handles.push_back(serversocket.get()); + topoll.push_back(llfio::poll_what::is_readable); + whatchanged.push_back(llfio::poll_what::none); + + // Connected socket state + struct connected_socket { + // The connected socket + llfio::tls_socket_handle_ptr socket; + + // The endpoint from which it connected + llfio::ip::address endpoint; + + // The size of registered buffer actually allocated (we request system + // page size, the DMA controller may return that or more) + size_t rbuf_storage_length{llfio::utils::page_size()}; + size_t wbuf_storage_length{llfio::utils::page_size()}; + + // These are shared ptrs to memory potentially registered with the NIC's + // DMA controller and therefore i/o using them would be true whole system + // zero copy + llfio::tls_socket_handle::registered_buffer_type rbuf_storage, wbuf_storage; + + // These spans of byte and const byte index into buffer storage and get + // updated by each i/o operation to describe what was done + llfio::tls_socket_handle::buffer_type rbuf; + llfio::tls_socket_handle::const_buffer_type wbuf; + + explicit connected_socket(std::pair<llfio::tls_socket_handle_ptr, llfio::ip::address> s) + : socket(std::move(s.first)) + , endpoint(std::move(s.second)) + , rbuf_storage(socket->allocate_registered_buffer(rbuf_storage_length).value()) + , wbuf_storage(socket->allocate_registered_buffer(wbuf_storage_length).value()) + , rbuf(*rbuf_storage) // fill this buffer + , wbuf(wbuf_storage->data(), 0) // nothing to write + {} + }; + std::vector<connected_socket> connected_sockets; + + // Begin the processing loop + for(;;) { + // We need to clear whatchanged before use, as it accumulates bits set. + std::fill(whatchanged.begin(), whatchanged.end(), llfio::poll_what::none); + + // As with all LLFIO calls, you can set a deadline here so this call + // times out. The default as usual is wait until infinity. + size_t handles_changed + = llfio::poll(whatchanged, {pollable_handles}, topoll).value(); + + // Loop the polled handles looking for events changed. + for(size_t handleidx = 0; + handles_changed > 0 && handleidx < pollable_handles.size(); + handleidx++) { + if(whatchanged[handleidx] == llfio::poll_what::none) { + continue; + } + if(0 == handleidx) { + // This is the listening socket, and there is a new connection to be + // read, as listening socket defines its read operation to be + // connected sockets and the endpoint they connected from. + std::pair<llfio::tls_socket_handle_ptr, llfio::ip::address> s; + serversocket->read({s}).value(); + connected_sockets.emplace_back(std::move(s)); + + // Watch this connected socket going forth for data to read or failure + pollable_handles.push_back(connected_sockets.back().socket.get()); + topoll.push_back(llfio::poll_what::is_readable|llfio::poll_what::is_errored); + whatchanged.push_back(llfio::poll_what::none); + handles_changed--; + continue; + } + // There has been an event on this socket + auto &sock = connected_sockets[handleidx - 1]; + handles_changed--; + if(whatchanged[handleidx] & llfio::poll_what::is_errored) { + // Force it closed and ignore it from polling going forth + sock.socket->close().value(); + sock.rbuf_storage.reset(); + sock.wbuf_storage.reset(); + pollable_handles[handleidx] = nullptr; + } + if(whatchanged[handleidx] & llfio::poll_what::is_readable) { + // Set up the buffer to fill. Note the scatter buffer list + // based API. + sock.rbuf = *sock.rbuf_storage; + sock.socket->read({{&sock.rbuf, 1}}).value(); + // sock.rbuf has its size adjusted to bytes read + } + if(whatchanged[handleidx] & llfio::poll_what::is_writable) { + // If this was set in topoll, it means write buffers + // were full at some point, and we are now being told + // there is space to write some more + if(!sock.wbuf.empty()) { + // Take a copy of the buffer to write, as it will be + // modified in place with what was written + auto b(sock.wbuf); + sock.socket->write({{&b, 1}}).value(); + // Adjust the buffer to yet to write + sock.wbuf = {sock.wbuf.data() + b.size(), sock.wbuf.size() - b.size() }; + } + if(sock.wbuf.empty()) { + // Nothing more to write, so no longer poll for this + topoll[handleidx]&=~llfio::poll_what::is_writable; + } + } + + // Process buffers read and written for this socket ... + } + } + //! [tls_socket_server] +} + +void wrap_tls_socket() +{ + //! [wrap_tls_socket] + namespace llfio = LLFIO_V2_NAMESPACE; + + // I want a TLS socket source which supports wrapping externally + // supplied byte_socket_handle instances + static constexpr auto required_features = + llfio::tls_socket_source_implementation_features::supports_wrap; + llfio::tls_socket_source_ptr tls_socket_source = + llfio::tls_socket_source_registry::default_source(required_features, + required_features).instantiate().value(); + + // Create a raw kernel socket. This could also be your custom + // subclass of byte_socket_handle or listening_byte_socket_handle. + // + // byte_socket_handle and listening_byte_socket_handle default + // to your OS kernel's BSD socket implementation, but their + // implementation is completely customisable. In fact, tls_socket_ptr + // points at an unknown (i.e. ABI erased) byte_socket_handle implementation. + llfio::byte_socket_handle rawsocket + = llfio::byte_socket_handle::byte_socket(llfio::ip::family::any).value(); + + llfio::listening_byte_socket_handle rawlsocket + = llfio::listening_byte_socket_handle::listening_byte_socket(llfio::ip::family::any).value(); + + // Attempt to wrap the raw socket with TLS. Note the "attempt" part, + // just because a TLS implementation supports wrapping doesn't mean + // that it will wrap this specific raw socket, it may error out. + // + // Note also that no ownership of the raw socket is taken - you must + // NOT move it in memory until the TLS wrapped socket is done with it. + llfio::tls_socket_handle_ptr securesocket + = tls_socket_source->wrap(&rawsocket).value(); + + llfio::listening_tls_socket_handle_ptr serversocket + = tls_socket_source->wrap(&rawlsocket).value(); + + //! [wrap_tls_socket] +} + int main() { return 0; diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 1e6b67a4..7eb59194 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 ceeaddba4c571291d42690a54424e8bf8cf4d5dd -#define LLFIO_PREVIOUS_COMMIT_DATE "2022-04-16 23:05:51 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE ceeaddba +#define LLFIO_PREVIOUS_COMMIT_REF d06bf6d6c2e1a23fb9eee3cb0c1056abc52de471 +#define LLFIO_PREVIOUS_COMMIT_DATE "2022-05-06 10:05:54 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE d06bf6d6 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 09c830c7..03963aa8 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 @@ -1275,7 +1275,12 @@ public: int _bread(BIO *bio, char *buffer, size_t bytes, size_t *read) { LLFIO_LOG_FUNCTION_CALL(this); - auto ret = [=, this]() mutable + auto ret = [= +#if __cplusplus >= 202000L || _HAS_CXX20 + , + this +#endif + ]() mutable { assert(_lock_holder.owns_lock()); *read = 0; @@ -1374,7 +1379,12 @@ public: int _bwrite(BIO *bio, const char *buffer, size_t bytes, size_t *written) { LLFIO_LOG_FUNCTION_CALL(this); - auto ret = [=, this]() mutable + auto ret = [= +#if __cplusplus >= 202000L || _HAS_CXX20 + , + this +#endif + ]() mutable { assert(_lock_holder.owns_lock()); *written = 0; diff --git a/include/llfio/v2.0/tls_socket_handle.hpp b/include/llfio/v2.0/tls_socket_handle.hpp index 6cefca74..08333af0 100644 --- a/include/llfio/v2.0/tls_socket_handle.hpp +++ b/include/llfio/v2.0/tls_socket_handle.hpp @@ -309,6 +309,8 @@ system_implementation = (1U << 1U), //!< This socket source is the "system" rat io_multiplexer = (1U << 2U), //!< This socket source provides an i/o multiplexer supports_wrap = (1U << 3U), //!< This socket source may be able to wrap third party plain sockets +FIPS_140_2 = (1U << 16U), //!< This socket source provides FIPS_140_2 compliant algorithms + all = 0xffffffff //!< All bits set } // QUICKCPPLIB_BITFIELD_END(tls_socket_source_implementation_features) // @@ -533,6 +535,10 @@ if(string_view(buffer, b.size()) != "World") { // rather than hard close sock->shutdown_and_close(std::chrono::seconds(3)).value(); ``` + +Fuller fat example: + + \snippet use_cases.cpp tls_socket_server */ class LLFIO_DECL tls_socket_source_registry { @@ -552,16 +558,18 @@ public: //! Convenience overload retrieving TLS socket sources, preferring system over third party implementations. static tls_socket_source_implementation_information default_source(tls_socket_source_implementation_features set = tls_socket_source_implementation_features::none, - tls_socket_source_implementation_features mask = tls_socket_source_implementation_features::system_implementation) noexcept + tls_socket_source_implementation_features mask = tls_socket_source_implementation_features::none) noexcept { tls_socket_source_implementation_information ret{"no implementation available"}; set |= tls_socket_source_implementation_features::system_implementation; + mask |= tls_socket_source_implementation_features::system_implementation; auto filled = sources({&ret, 1}, set, mask); if(!filled.empty()) { return ret; } set &= ~tls_socket_source_implementation_features::system_implementation; + mask &= ~tls_socket_source_implementation_features::system_implementation; filled = sources({&ret, 1}, set, mask); if(!filled.empty()) { |