LCOV - code coverage report
Current view: top level - corosio - tls_stream.hpp (source / functions) Coverage Total Missed
Test: coverage_remapped.info Lines: 0.0 % 4 4
Test Date: 2026-10-17 04:01:32 Functions: 0.0 % 4 4

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : // Copyright (c) 2026 Steve Gerbino
       5                 : //
       6                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       7                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       8                 : //
       9                 : // Official repository: https://github.com/cppalliance/corosio
      10                 : //
      11                 : 
      12                 : #ifndef BOOST_COROSIO_TLS_STREAM_HPP
      13                 : #define BOOST_COROSIO_TLS_STREAM_HPP
      14                 : 
      15                 : #include <boost/corosio/detail/config.hpp>
      16                 : #include <boost/capy/buffers.hpp>
      17                 : #include <boost/capy/detail/buffer_array.hpp>
      18                 : #include <boost/capy/io/any_stream.hpp>
      19                 : #include <boost/capy/results.hpp>
      20                 : #include <boost/capy/task.hpp>
      21                 : 
      22                 : #include <cstddef>
      23                 : #include <string_view>
      24                 : 
      25                 : namespace boost::corosio {
      26                 : 
      27                 : namespace detail {
      28                 : 
      29                 : /** A transport a TLS stream owns, held apart from its `any_stream`.
      30                 : 
      31                 :     Destroying the stream cancels the transport's pending operations
      32                 :     while the TLS state they complete into stays alive; the transport
      33                 :     itself outlives every operation that could be parked on it.
      34                 : */
      35                 : struct tls_owned_transport
      36                 : {
      37                 :     /// The owned transport, or null in reference mode.
      38                 :     void* p = nullptr;
      39                 : 
      40                 :     /// Destroy the transport `p` points to.
      41                 :     void (*destroy)(void*) noexcept = nullptr;
      42                 : 
      43                 :     /// Cancel the transport's pending operations, or null if it can't.
      44                 :     void (*cancel)(void*) noexcept = nullptr;
      45                 : };
      46                 : 
      47                 : /// Destroy an owned transport of type `S`.
      48                 : template<class S>
      49                 : void
      50                 : destroy_owned_transport(void* p) noexcept
      51                 : {
      52                 :     delete static_cast<S*>(p);
      53                 : }
      54                 : 
      55                 : /// Cancel an owned transport of type `S`'s pending operations.
      56                 : template<class S>
      57                 : void
      58                 : cancel_owned_transport(void* p) noexcept
      59                 : {
      60                 :     static_cast<S*>(p)->cancel();
      61                 : }
      62                 : 
      63                 : /// Make the owned-transport record for a transport of type `S`.
      64                 : template<class S>
      65                 : tls_owned_transport
      66                 : make_owned_transport(S* p) noexcept
      67                 : {
      68                 :     tls_owned_transport t{p, &destroy_owned_transport<S>, nullptr};
      69                 :     if constexpr (requires(S& s) {
      70                 :                       { s.cancel() } noexcept;
      71                 :                   })
      72                 :         t.cancel = &cancel_owned_transport<S>;
      73                 :     return t;
      74                 : }
      75                 : 
      76                 : } // namespace detail
      77                 : 
      78                 : /** TLS handshake role.
      79                 : 
      80                 :     Specifies whether to perform the TLS handshake as a client or server.
      81                 : 
      82                 :     @see tls_stream::handshake
      83                 : */
      84                 : enum class tls_role
      85                 : {
      86                 :     /// Perform handshake as the connecting client.
      87                 :     client,
      88                 : 
      89                 :     /// Perform handshake as the accepting server.
      90                 :     server
      91                 : };
      92                 : 
      93                 : /** Reads, writes, and manages the handshake lifecycle of a TLS
      94                 :     session over an underlying stream.
      95                 : 
      96                 :     This class provides a runtime-polymorphic interface for TLS
      97                 :     implementations. Derived classes (`openssl_stream`, `wolfssl_stream`)
      98                 :     implement the virtual functions to provide backend-specific
      99                 :     TLS functionality.
     100                 : 
     101                 :     An @ref io_stream represents OS-level I/O completed by the kernel.
     102                 :     TLS streams are coroutine-based instead: their operations are
     103                 :     coroutines that orchestrate sub-operations on the underlying stream.
     104                 : 
     105                 :     The non-virtual template wrappers (`read_some`, `write_some`)
     106                 :     satisfy the `capy::Stream` concept, enabling TLS streams to
     107                 :     be used anywhere a Stream is expected.
     108                 : 
     109                 :     @par Thread Safety
     110                 :     Distinct objects: Safe.@n
     111                 :     Shared objects: Unsafe, with one exception: one read operation and
     112                 :     one write operation may be in flight simultaneously. `shutdown()`
     113                 :     may overlap a pending read. On a multi-threaded execution context,
     114                 :     all operations on one stream must run within the same
     115                 :     `capy::strand`, or must otherwise never run concurrently. A
     116                 :     single-threaded context needs no strand.
     117                 : 
     118                 :     @see openssl_stream, wolfssl_stream
     119                 : */
     120                 : class BOOST_COROSIO_DECL tls_stream
     121                 : {
     122                 : public:
     123                 :     /// Destroy the TLS stream.
     124                 :     virtual ~tls_stream() = default;
     125                 : 
     126                 :     /// Copy construction is disabled; copying a stream would slice the derived session.
     127                 :     tls_stream(tls_stream const&) = delete;
     128                 :     /// Copy assignment is disabled; copying a stream would slice the derived session.
     129                 :     tls_stream& operator=(tls_stream const&) = delete;
     130                 : 
     131                 :     /** Initiate an asynchronous read operation.
     132                 : 
     133                 :         Reads decrypted data into the provided buffer sequence. The
     134                 :         operation completes when it reads at least one byte,
     135                 :         or an error occurs.
     136                 : 
     137                 :         This non-virtual template wrapper satisfies the `capy::Stream`
     138                 :         concept by delegating to the virtual `do_read_some`.
     139                 : 
     140                 :         @par Thread Safety
     141                 :         May run concurrently with one operation in the other
     142                 :         direction, subject to the class-level threading contract.
     143                 :         Two concurrent operations in the same direction are
     144                 :         undefined.
     145                 : 
     146                 :         @param buffers The buffer sequence to read data into.
     147                 : 
     148                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     149                 :     */
     150                 :     template<capy::MutableBufferSequence Buffers>
     151 MIS           0 :     [[nodiscard]] auto read_some(Buffers const& buffers)
     152                 :     {
     153               0 :         return do_read_some(buffers);
     154                 :     }
     155                 : 
     156                 :     /** Initiate an asynchronous write operation.
     157                 : 
     158                 :         Encrypts and writes data from the provided buffer sequence.
     159                 :         The operation completes when it writes at least one byte,
     160                 :         or an error occurs.
     161                 : 
     162                 :         This non-virtual template wrapper satisfies the `capy::Stream`
     163                 :         concept by delegating to the virtual `do_write_some`.
     164                 : 
     165                 :         @par Thread Safety
     166                 :         May run concurrently with one operation in the other
     167                 :         direction, subject to the class-level threading contract.
     168                 :         Two concurrent operations in the same direction are
     169                 :         undefined.
     170                 : 
     171                 :         @param buffers The buffer sequence containing data to write.
     172                 : 
     173                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     174                 :     */
     175                 :     template<capy::ConstBufferSequence Buffers>
     176               0 :     [[nodiscard]] auto write_some(Buffers const& buffers)
     177                 :     {
     178               0 :         return do_write_some(buffers);
     179                 :     }
     180                 : 
     181                 :     /** Asynchronously perform the TLS handshake.
     182                 : 
     183                 :         Initiates the TLS handshake process. For client connections,
     184                 :         this sends the ClientHello and processes the server's response.
     185                 :         For server connections, this waits for the ClientHello and
     186                 :         sends the server's response.
     187                 : 
     188                 :         A handshake attempt consumes the stream state, whether it
     189                 :         succeeds or not. A subsequent call behaves as if `reset()` ran
     190                 :         first, and performs a fresh handshake using the current
     191                 :         configuration.
     192                 : 
     193                 :         @pre The underlying stream must be connected. No other TLS
     194                 :             operation may be in progress on this stream.
     195                 : 
     196                 :         @param role The handshake role, client or server.
     197                 : 
     198                 :         @return An awaitable yielding `(error_code)`.
     199                 :     */
     200                 :     [[nodiscard]] virtual capy::task<capy::results<>> handshake(tls_role role) = 0;
     201                 : 
     202                 :     /** Asynchronously perform a graceful TLS shutdown.
     203                 : 
     204                 :         Initiates the TLS shutdown sequence by sending a close_notify
     205                 :         alert and waiting for the peer's close_notify response.
     206                 : 
     207                 :         @pre A handshake must have completed successfully. May overlap
     208                 :             a pending read. No concurrent write may be in progress.
     209                 : 
     210                 :         @par Postconditions
     211                 :         If the transport ends before the peer's close_notify arrives,
     212                 :         the result is `capy::error::stream_truncated`, not success. An
     213                 :         unannounced close is indistinguishable from a truncation attack,
     214                 :         so it must not be reported as a clean shutdown. A shutdown
     215                 :         stopped mid-flight reports canceled. Any other transport
     216                 :         error propagates unchanged.
     217                 : 
     218                 :         @return An awaitable yielding `(error_code)`.
     219                 :     */
     220                 :     [[nodiscard]] virtual capy::task<capy::results<>> shutdown() = 0;
     221                 : 
     222                 :     /** Cancel operations pending on the owned underlying stream.
     223                 : 
     224                 :         Operations parked on the underlying stream complete with
     225                 :         `capy::error::canceled`. A stream that owns another TLS stream
     226                 :         reaches the innermost transport through this, so destroying
     227                 :         the outer stream cancels a read parked through both.
     228                 : 
     229                 :         In reference mode, and when the owned stream has no `cancel()`
     230                 :         member, this does nothing; cancel the underlying stream
     231                 :         directly instead.
     232                 : 
     233                 :         @par Thread Safety
     234                 :         Not thread safe with respect to destroying or moving this
     235                 :         stream.
     236                 :     */
     237                 :     virtual void cancel() noexcept = 0;
     238                 : 
     239                 :     /** Reset TLS session state for reuse.
     240                 : 
     241                 :         Releases TLS session state including session keys and peer
     242                 :         certificates, returning the stream to a state where
     243                 :         `handshake()` can be called again. Internal memory
     244                 :         allocations (I/O buffers) are preserved.
     245                 : 
     246                 :         Calling `handshake()` on a previously-used stream
     247                 :         implicitly performs a reset first, so explicit calls
     248                 :         are only needed to eagerly release session state.
     249                 : 
     250                 :         @pre No TLS operation (handshake, read, write, shutdown) is
     251                 :             in progress.
     252                 : 
     253                 :         @par Thread Safety
     254                 :         Not thread safe. The caller must ensure no concurrent
     255                 :         operations are in progress on this stream.
     256                 : 
     257                 :         @note If called mid-session before `shutdown()`, pending
     258                 :             TLS data is discarded and the peer observes a
     259                 :             truncated stream.
     260                 :     */
     261                 :     virtual void reset() = 0;
     262                 : 
     263                 :     /** Set the peer hostname for SNI and certificate verification.
     264                 : 
     265                 :         Configures the hostname sent in the TLS Server Name
     266                 :         Indication extension and matched against the peer
     267                 :         certificate during verification. The value takes effect
     268                 :         at the next `handshake()`; an established session is not
     269                 :         affected. It persists across `reset()`, so a stream reused
     270                 :         to reach a different host must set the new name before
     271                 :         handshaking again.
     272                 : 
     273                 :         An empty hostname (the default) disables SNI and hostname
     274                 :         verification.
     275                 : 
     276                 :         If `hostname` is an IP literal (IPv4 or IPv6), it is matched
     277                 :         against the certificate's iPAddress entries instead of its DNS
     278                 :         names. No SNI is sent, because RFC 6066 excludes literals.
     279                 :         A backend build that cannot match iPAddress entries fails the
     280                 :         handshake with `std::errc::function_not_supported` rather
     281                 :         than skip verification.
     282                 : 
     283                 :         @par Postconditions
     284                 :         The next `handshake()` uses `hostname` for SNI and
     285                 :         certificate verification, or neither if it is empty.
     286                 : 
     287                 :         @note The hostname is used for client handshakes only;
     288                 :         it is ignored when handshaking as a server.
     289                 : 
     290                 :         @param hostname The peer hostname, or empty to disable.
     291                 :     */
     292                 :     virtual void set_hostname(std::string_view hostname) = 0;
     293                 : 
     294                 :     /** Return a reference to the underlying stream.
     295                 : 
     296                 :         Provides access to the type-erased underlying stream for
     297                 :         operations like cancellation or accessing native handles.
     298                 : 
     299                 :         @warning Do not reseat (assign to) the returned reference.
     300                 :             The TLS implementation holds internal state bound to
     301                 :             the original stream. Replacing it causes undefined
     302                 :             behavior.
     303                 : 
     304                 :         @return Reference to the wrapped stream.
     305                 :     */
     306                 :     virtual capy::any_stream& next_layer() noexcept = 0;
     307                 : 
     308                 :     /** Return a const reference to the underlying stream.
     309                 : 
     310                 :         @return Const reference to the wrapped stream.
     311                 :     */
     312                 :     virtual capy::any_stream const& next_layer() const noexcept = 0;
     313                 : 
     314                 :     /** Return the name of the TLS backend.
     315                 : 
     316                 :         @return A string identifying the TLS implementation,
     317                 :             such as "openssl" or "wolfssl".
     318                 :     */
     319                 :     virtual std::string_view name() const noexcept = 0;
     320                 : 
     321                 :     /** Return the ALPN protocol negotiated during the handshake.
     322                 : 
     323                 :         Application-Layer Protocol Negotiation selects a single
     324                 :         application protocol (for example `"h2"` or `"http/1.1"`)
     325                 :         during the TLS handshake, from the list supplied via
     326                 :         @ref tls_context::set_alpn.
     327                 : 
     328                 :         @return The negotiated protocol, or an empty view. It is empty
     329                 :         if no protocol was negotiated or ALPN was not offered. It is
     330                 :         also empty if the handshake has not completed, or if the build
     331                 :         lacks ALPN support.
     332                 : 
     333                 :         @par Thread Safety
     334                 :         Safe to call after the handshake completes; not safe to call
     335                 :         concurrently with a handshake or reset.
     336                 :     */
     337                 :     virtual std::string_view alpn_protocol() const noexcept
     338                 :     {
     339                 :         return {};
     340                 :     } // LCOV_EXCL_LINE every concrete stream overrides this; the base default is never called
     341                 : 
     342                 : protected:
     343                 :     /// Default construct; a derived class supplies the session.
     344                 :     tls_stream() = default;
     345                 : 
     346                 :     /** Perform the backend-specific decrypted read.
     347                 : 
     348                 :         Derived classes override this to perform TLS decryption
     349                 :         and read operations.
     350                 : 
     351                 :         @param buffers Buffer sequence to read into.
     352                 : 
     353                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     354                 :     */
     355                 :     virtual capy::task<capy::results<std::size_t>> do_read_some(
     356                 :         capy::detail::mutable_buffer_array<capy::detail::max_iovec_>
     357                 :             buffers) = 0;
     358                 : 
     359                 :     /** Perform the backend-specific encrypted write.
     360                 : 
     361                 :         Derived classes override this to perform TLS encryption
     362                 :         and write operations.
     363                 : 
     364                 :         @param buffers Buffer sequence to write from.
     365                 : 
     366                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     367                 :     */
     368                 :     virtual capy::task<capy::results<std::size_t>> do_write_some(
     369                 :         capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0;
     370                 : };
     371                 : 
     372                 : } // namespace boost::corosio
     373                 : 
     374                 : #endif
        

Generated by: LCOV version 2.3