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