include/boost/corosio/tls_stream.hpp

0.0% Lines (0/4) 0.0% List of functions (0/2)
tls_stream.hpp
f(x) Functions (2)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_TLS_STREAM_HPP
12 #define BOOST_COROSIO_TLS_STREAM_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/capy/buffers.hpp>
16 #include <boost/capy/detail/buffer_array.hpp>
17 #include <boost/capy/io/any_stream.hpp>
18 #include <boost/capy/io_task.hpp>
19
20 #include <cstddef>
21 #include <string_view>
22
23 namespace boost::corosio {
24
25 /** TLS handshake role.
26
27 Specifies whether to perform the TLS handshake as a client or server.
28
29 @see tls_stream::handshake
30 */
31 enum class tls_role
32 {
33 /// Perform handshake as the connecting client.
34 client,
35
36 /// Perform handshake as the accepting server.
37 server
38 };
39
40 /** Abstract base class for TLS streams.
41
42 This class provides a runtime-polymorphic interface for TLS
43 implementations. Derived classes (openssl_stream, wolfssl_stream)
44 implement the virtual functions to provide backend-specific
45 TLS functionality.
46
47 Unlike @ref io_stream which represents OS-level I/O completed
48 by the kernel, TLS streams are coroutine-based: their operations
49 are implemented as coroutines that orchestrate sub-operations
50 on the underlying stream.
51
52 The non-virtual template wrappers (`read_some`, `write_some`)
53 satisfy the `capy::Stream` concept, enabling TLS streams to
54 be used anywhere a Stream is expected.
55
56 @par Thread Safety
57 Distinct objects: Safe.@n
58 Shared objects: Unsafe.
59
60 @see openssl_stream, wolfssl_stream
61 */
62 class BOOST_COROSIO_DECL tls_stream
63 {
64 public:
65 /// Destroy the TLS stream.
66 virtual ~tls_stream() = default;
67
68 tls_stream(tls_stream const&) = delete;
69 tls_stream& operator=(tls_stream const&) = delete;
70
71 /** Initiate an asynchronous read operation.
72
73 Reads decrypted data into the provided buffer sequence. The
74 operation completes when at least one byte has been read,
75 or an error occurs.
76
77 This non-virtual template wrapper satisfies the `capy::Stream`
78 concept by delegating to the virtual `do_read_some`.
79
80 @param buffers The buffer sequence to read data into.
81
82 @return An awaitable yielding `(error_code,std::size_t)`.
83 */
84 template<capy::MutableBufferSequence Buffers>
85 auto read_some(Buffers const& buffers)
86 {
87 return do_read_some(buffers);
88 }
89
90 /** Initiate an asynchronous write operation.
91
92 Encrypts and writes data from the provided buffer sequence.
93 The operation completes when at least one byte has been
94 written, or an error occurs.
95
96 This non-virtual template wrapper satisfies the `capy::Stream`
97 concept by delegating to the virtual `do_write_some`.
98
99 @param buffers The buffer sequence containing data to write.
100
101 @return An awaitable yielding `(error_code,std::size_t)`.
102 */
103 template<capy::ConstBufferSequence Buffers>
104 auto write_some(Buffers const& buffers)
105 {
106 return do_write_some(buffers);
107 }
108
109 /** Perform the TLS handshake asynchronously.
110
111 Initiates the TLS handshake process. For client connections,
112 this sends the ClientHello and processes the server's response.
113 For server connections, this waits for the ClientHello and
114 sends the server's response.
115
116 A handshake attempt, successful or not, consumes the stream
117 state: a subsequent call behaves as if `reset()` had been
118 called first and performs a fresh handshake using the
119 current configuration.
120
121 @param role The handshake role, client or server.
122
123 @return An awaitable yielding `(error_code)`.
124 */
125 virtual capy::io_task<> handshake(tls_role role) = 0;
126
127 /** Perform a graceful TLS shutdown asynchronously.
128
129 Initiates the TLS shutdown sequence by sending a close_notify
130 alert and waiting for the peer's close_notify response.
131
132 @return An awaitable yielding `(error_code)`.
133 */
134 virtual capy::io_task<> shutdown() = 0;
135
136 /** Reset TLS session state for reuse.
137
138 Releases TLS session state including session keys and peer
139 certificates, returning the stream to a state where
140 `handshake()` can be called again. Internal memory
141 allocations (I/O buffers) are preserved.
142
143 Calling `handshake()` on a previously-used stream
144 implicitly performs a reset first, so explicit calls
145 are only needed to eagerly release session state.
146
147 @par Preconditions
148 No TLS operation (handshake, read, write, shutdown) is
149 in progress.
150
151 @par Thread Safety
152 Not thread safe. The caller must ensure no concurrent
153 operations are in progress on this stream.
154
155 @note If called mid-session before `shutdown()`, pending
156 TLS data is discarded and the peer will observe a
157 truncated stream.
158 */
159 virtual void reset() = 0;
160
161 /** Set the peer hostname for SNI and certificate verification.
162
163 Configures the hostname sent in the TLS Server Name
164 Indication extension and matched against the peer
165 certificate during verification. The value takes effect
166 at the next `handshake()`; an established session is not
167 affected. It persists across `reset()`, so a stream reused
168 to reach a different host must set the new name before
169 handshaking again.
170
171 An empty hostname (the default) disables SNI and hostname
172 verification.
173
174 If `hostname` is an IP literal (IPv4 or IPv6), it is matched
175 against the certificate's iPAddress entries instead of its
176 DNS names, and no SNI is sent (RFC 6066 excludes literals).
177 A backend build that cannot match iPAddress entries fails the
178 handshake with `std::errc::function_not_supported` rather
179 than skip verification.
180
181 @par Postconditions
182 The next `handshake()` uses `hostname` for SNI and
183 certificate verification, or neither if it is empty.
184
185 @note The hostname is used for client handshakes only;
186 it is ignored when handshaking as a server.
187
188 @param hostname The peer hostname, or empty to disable.
189 */
190 virtual void set_hostname(std::string_view hostname) = 0;
191
192 /** Returns a reference to the underlying stream.
193
194 Provides access to the type-erased underlying stream for
195 operations like cancellation or accessing native handles.
196
197 @warning Do not reseat (assign to) the returned reference.
198 The TLS implementation holds internal state bound to
199 the original stream. Replacing it causes undefined
200 behavior.
201
202 @return Reference to the wrapped stream.
203 */
204 virtual capy::any_stream& next_layer() noexcept = 0;
205
206 /** Returns a const reference to the underlying stream.
207
208 @return Const reference to the wrapped stream.
209 */
210 virtual capy::any_stream const& next_layer() const noexcept = 0;
211
212 /** Returns the name of the TLS backend.
213
214 @return A string identifying the TLS implementation,
215 such as "openssl" or "wolfssl".
216 */
217 virtual std::string_view name() const noexcept = 0;
218
219 /** Returns the ALPN protocol negotiated during the handshake.
220
221 Application-Layer Protocol Negotiation selects a single
222 application protocol (for example `"h2"` or `"http/1.1"`)
223 during the TLS handshake, from the list supplied via
224 @ref tls_context::set_alpn.
225
226 @return The negotiated protocol, or an empty view if no
227 protocol was negotiated, ALPN was not offered, the
228 handshake has not completed, or the backend/build does
229 not support ALPN.
230
231 @par Thread Safety
232 Safe to call after the handshake completes; not safe to call
233 concurrently with a handshake or reset.
234 */
235 virtual std::string_view alpn_protocol() const noexcept { return {}; }
236
237 protected:
238 tls_stream() = default;
239
240 /** Virtual read implementation.
241
242 Derived classes override this to perform TLS decryption
243 and read operations.
244
245 @param buffers Buffer sequence to read into.
246
247 @return An awaitable yielding `(error_code,std::size_t)`.
248 */
249 virtual capy::io_task<std::size_t> do_read_some(
250 capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers) = 0;
251
252 /** Virtual write implementation.
253
254 Derived classes override this to perform TLS encryption
255 and write operations.
256
257 @param buffers Buffer sequence to write from.
258
259 @return An awaitable yielding `(error_code,std::size_t)`.
260 */
261 virtual capy::io_task<std::size_t> do_write_some(
262 capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0;
263 };
264
265 } // namespace boost::corosio
266
267 #endif
268