src/openssl/src/openssl_stream.cpp

84.0% Lines (535/637) 100.0% List of functions (41/41) 47.6% Branches (622/1306)
openssl_stream.cpp
f(x) Functions (41)
Function Calls Lines Branches Blocks
boost::corosio::(anonymous namespace)::tls_method_compat() :68 1890x 100.0% 50.0% 66.0% boost::corosio::(anonymous namespace)::apply_hostname_verification(ssl_st*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) :78 1916x 100.0% 100.0% 100.0% boost::corosio::(anonymous namespace)::openssl_proto_version(boost::corosio::tls_version) :95 3780x 100.0% 100.0% boost::corosio::(anonymous namespace)::build_alpn_wire(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>> const&) :104 4x 88.9% 60.0% 71.0% boost::corosio::(anonymous namespace)::normalize_openssl_shutdown_read_error(std::__1::error_code) :118 11x 87.5% 80.0% 90.0% boost::corosio::(anonymous namespace)::openssl_category_impl::~openssl_category_impl() :133 2x 100.0% 100.0% boost::corosio::(anonymous namespace)::openssl_category_impl::name() const :136 1x 100.0% 100.0% boost::corosio::(anonymous namespace)::openssl_category_impl::message(int) const :142 2x 100.0% 100.0% boost::corosio::(anonymous namespace)::make_openssl_error(unsigned long) :155 18x 66.7% 33.3% 57.0% boost::corosio::openssl_category() :166 21x 100.0% 75.0% 100.0% boost::corosio::detail::password_callback(char*, int, int, void*) :181 1x 83.3% 50.0% 71.0% boost::corosio::detail::verify_callback_trampoline(int, x509_store_ctx_st*) :208 10x 83.9% 50.0% 70.0% boost::corosio::detail::alpn_select_cb(ssl_st*, unsigned char const**, unsigned char*, unsigned char const*, unsigned int, void*) :267 2x 88.2% 71.4% 88.0% boost::corosio::detail::sni_callback(ssl_st*, int*, void*) :301 3x 100.0% 75.0% 100.0% boost::corosio::detail::openssl_native_context::openssl_native_context(boost::corosio::detail::tls_context_data const&) :342 3780x 92.7% 62.2% 100.0% boost::corosio::detail::openssl_native_context::~openssl_native_context() :628 5670x 100.0% 50.0% 100.0% boost::corosio::detail::get_openssl_native_context(boost::corosio::detail::tls_context_data const&) :636 3250x 100.0% 100.0% boost::corosio::detail::get_openssl_native_context(boost::corosio::detail::tls_context_data const&)::'lambda'()::operator()() const :639 1890x 100.0% 50.0% 50.0% boost::corosio::detail::get_openssl_context(boost::corosio::detail::tls_context_data const&) :644 1896x 100.0% 100.0% boost::corosio::openssl_stream::impl::impl(boost::capy::any_stream&, boost::corosio::tls_context) :667 3792x 100.0% 50.0% 100.0% boost::corosio::openssl_stream::impl::~impl() :673 3792x 100.0% 50.0% 100.0% boost::corosio::openssl_stream::impl::reset() :681 20x 81.8% 50.0% 71.0% boost::corosio::openssl_stream::impl::capture_alpn() :703 752x 100.0% 75.0% 100.0% boost::corosio::openssl_stream::impl::flush_output() :712 163429x 88.5% 50.0% 53.0% boost::corosio::openssl_stream::impl::read_input() :743 21800x 86.7% 44.6% 48.0% boost::corosio::openssl_stream::impl::do_read_some(boost::capy::detail::buffer_array<16ul, false>) :764 140853x 65.5% 37.3% 44.0% boost::corosio::openssl_stream::impl::do_write_some(boost::capy::detail::buffer_array<16ul, true>) :847 140849x 48.8% 19.7% 26.0% boost::corosio::openssl_stream::impl::do_handshake(int) :907 1354x 87.5% 49.3% 53.0% boost::corosio::openssl_stream::impl::do_shutdown() :985 39x 69.8% 44.6% 47.0% boost::corosio::openssl_stream::impl::init_ssl() :1052 1896x 60.0% 50.0% 62.0% boost::corosio::openssl_stream::make_impl(boost::capy::any_stream&, boost::corosio::tls_context const&) :1087 1896x 75.0% 25.0% 36.0% boost::corosio::openssl_stream::~openssl_stream() :1101 3796x 100.0% 100.0% 100.0% boost::corosio::openssl_stream::openssl_stream(boost::corosio::openssl_stream&&) :1106 4x 100.0% 50.0% 100.0% boost::corosio::openssl_stream::operator=(boost::corosio::openssl_stream&&) :1116 1x 100.0% 50.0% 100.0% boost::corosio::openssl_stream::do_read_some(boost::capy::detail::buffer_array<16ul, false>) :1131 140853x 100.0% 50.0% 48.0% boost::corosio::openssl_stream::do_write_some(boost::capy::detail::buffer_array<16ul, true>) :1138 140849x 100.0% 50.0% 48.0% boost::corosio::openssl_stream::handshake(boost::corosio::tls_stream::handshake_type) :1145 1354x 100.0% 50.0% 48.0% boost::corosio::openssl_stream::shutdown() :1151 39x 100.0% 50.0% 48.0% boost::corosio::openssl_stream::reset() :1157 16x 100.0% 100.0% boost::corosio::openssl_stream::name() const :1163 1x 100.0% 50.0% 66.0% boost::corosio::openssl_stream::alpn_protocol() const :1169 3x 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
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 #include <boost/corosio/openssl_stream.hpp>
12 #include <boost/corosio/detail/config.hpp>
13 #include <boost/capy/detail/buffer_array.hpp>
14 #include <boost/capy/ex/async_mutex.hpp>
15 #include <boost/capy/cond.hpp>
16 #include <boost/capy/error.hpp>
17 #include <boost/capy/write.hpp>
18
19 // Internal context implementation
20 #include "src/tls/detail/context_impl.hpp"
21
22 #include <openssl/ssl.h>
23 #include <openssl/err.h>
24 #include <openssl/bio.h>
25 #include <openssl/x509.h>
26 #include <openssl/pkcs12.h>
27
28 #include <algorithm>
29 #include <array>
30 #include <cstring>
31 #include <vector>
32
33 /*
34 openssl_stream Architecture
35 ===========================
36
37 TLS layer wrapping an underlying stream (via any_stream). Supports one
38 concurrent read_some and one concurrent write_some (like Asio's ssl::stream).
39
40 Data Flow (using BIO pairs)
41 ---------------------------
42 App -> SSL_write -> int_bio_ -> BIO_read(ext_bio_) -> out_buf_ -> s_->write_some -> Network
43 App <- SSL_read <- int_bio_ <- BIO_write(ext_bio_) <- in_buf_ <- s_->read_some <- Network
44
45 WANT_READ / WANT_WRITE Pattern
46 ------------------------------
47 OpenSSL's SSL_read/SSL_write return SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
48 when they need I/O. Our coroutine handles this by:
49
50 1. Call SSL_read or SSL_write
51 2. Check for pending output in ext_bio_ via BIO_ctrl_pending
52 3. If output pending: write to network via s_->write_some
53 4. If SSL_ERROR_WANT_READ: read from network into ext_bio_ via s_->read_some + BIO_write
54 5. Loop back to step 1
55
56 Renegotiation causes cross-direction I/O: SSL_read may need to write
57 handshake data, SSL_write may need to read. Each operation handles
58 whatever I/O direction OpenSSL requests.
59 */
60
61 namespace boost::corosio {
62
63 namespace {
64
65 constexpr std::size_t default_buffer_size = 16384;
66
67 inline SSL_METHOD const*
68 1890x tls_method_compat() noexcept
69 {
70 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
71
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x return TLS_method();
72 #else
73 return SSLv23_method();
74 #endif
75 }
76
77 inline void
78 1916x apply_hostname_verification(SSL* ssl, std::string const& hostname)
79 {
80
2/2
✓ Branch 0 taken 1912 times.
✓ Branch 1 taken 4 times.
1916x if (hostname.empty())
81 1912x return;
82
83 4x SSL_set_tlsext_host_name(ssl, hostname.c_str());
84
85 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
86 4x SSL_set1_host(ssl, hostname.c_str());
87 #else
88 if (auto* param = SSL_get0_param(ssl))
89 X509_VERIFY_PARAM_set1_host(param, hostname.c_str(), 0);
90 #endif
91 1916x }
92
93 // Map a portable protocol version to the OpenSSL version constant.
94 inline int
95 3780x openssl_proto_version(tls_version v) noexcept
96 {
97 3780x return v == tls_version::tls_1_3 ? TLS1_3_VERSION : TLS1_2_VERSION;
98 }
99
100 // Encode a protocol list into ALPN wire format: each entry is a
101 // one-byte length followed by that many bytes. Entries longer than 255
102 // bytes are skipped (invalid per RFC 7301).
103 inline std::string
104 4x build_alpn_wire(std::vector<std::string> const& protocols)
105 {
106 4x std::string wire;
107
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10x for (auto const& p : protocols)
108 {
109
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6x if (p.empty() || p.size() > 255)
110 continue;
111
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6x wire.push_back(static_cast<char>(p.size()));
112
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6x wire.append(p);
113 }
114 4x return wire;
115 4x }
116
117 inline std::error_code
118 11x normalize_openssl_shutdown_read_error(std::error_code ec) noexcept
119 {
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11x if (!ec)
121 return ec;
122
123 // A peer that closed without a proper close_notify is a truncated stream.
124 // Cancellation is deliberately excluded: a stopped shutdown must surface as
125 // canceled, not stream_truncated.
126
6/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 2 times.
13x if (ec == capy::cond::eof || ec == std::errc::connection_reset ||
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x ec == std::errc::connection_aborted || ec == std::errc::broken_pipe)
128 9x return make_error_code(capy::error::stream_truncated);
129
130 2x return ec;
131 11x }
132
133 class openssl_category_impl final : public std::error_category
134 {
135 char const*
136 1x name() const noexcept override
137 {
138 1x return "corosio.openssl";
139 }
140
141 std::string
142 2x message(int value) const override
143 {
144 char buf[256];
145 2x ::ERR_error_string_n(
146 2x static_cast<unsigned long>(value), buf, sizeof(buf));
147 2x return buf;
148 }
149 };
150
151 // Convert a packed OpenSSL error (from ERR_get_error) into an error_code.
152 // Codes from the ERR_LIB_SYS library carry a genuine errno reason and are
153 // reported with the system category; everything else uses openssl_category.
154 inline std::error_code
155 18x make_openssl_error(unsigned long err) noexcept
156 {
157
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18x if (ERR_GET_LIB(err) == ERR_LIB_SYS)
158 return std::error_code(
159 static_cast<int>(ERR_GET_REASON(err)), std::system_category());
160 18x return std::error_code(static_cast<int>(err), openssl_category());
161 18x }
162
163 } // namespace
164
165 std::error_category const&
166 21x openssl_category() noexcept
167 {
168
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
21x static openssl_category_impl instance;
169 21x return instance;
170 }
171
172 //
173 // Native context caching
174 //
175
176 namespace detail {
177
178 static int sni_ctx_data_index = -1;
179
180 static int
181 1x password_callback(char* buf, int size, int rwflag, void* userdata)
182 {
183 1x auto* cd = static_cast<tls_context_data const*>(userdata);
184
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1x if (!cd || !cd->password_callback)
185 return 0;
186
187 1x tls_password_purpose purpose = (rwflag == 0)
188 ? tls_password_purpose::for_reading
189 : tls_password_purpose::for_writing;
190
191 std::string password =
192 1x cd->password_callback(static_cast<std::size_t>(size), purpose);
193
194 1x int len = static_cast<int>(password.size());
195
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x if (len > size)
196 len = size;
197
198 1x std::memcpy(buf, password.data(), static_cast<std::size_t>(len));
199 1x return len;
200 1x }
201
202 // Trampoline installed via SSL_CTX_set_verify. Recovers the portable
203 // context data from the SSL_CTX ex_data (populated for every context)
204 // and applies, in order: the revocation policy's soft-fail downgrade,
205 // then the user's verify callback. Installed whenever a verify callback
206 // or a non-disabled revocation policy is configured.
207 static int
208 10x verify_callback_trampoline(int preverified, X509_STORE_CTX* store_ctx)
209 {
210 10x SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(
211 10x store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
212
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x if (!ssl)
213 return preverified;
214
215 10x auto* cd = static_cast<tls_context_data const*>(
216 10x SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), sni_ctx_data_index));
217
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x if (!cd)
218 return preverified;
219
220 10x bool ok = preverified != 0;
221
222 // Soft-fail revocation: accept certificates whose revocation status
223 // could not be determined (missing/expired CRL), but never downgrade
224 // an actual revocation. hard_fail leaves every CRL error fatal.
225
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
10x if (!ok && cd->revocation == tls_revocation_policy::soft_fail)
226 {
227 1x int const err = X509_STORE_CTX_get_error(store_ctx);
228
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1x if (err == X509_V_ERR_UNABLE_TO_GET_CRL ||
229 err == X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER ||
230 err == X509_V_ERR_CRL_HAS_EXPIRED ||
231 err == X509_V_ERR_CRL_NOT_YET_VALID)
232 1x ok = true;
233 1x }
234
235
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10x if (cd->verify_callback)
236 {
237 // Expose the current certificate's DER so the callback can inspect
238 // it portably. i2d_X509 allocates; free it after the callback.
239 6x X509* cert = X509_STORE_CTX_get_current_cert(store_ctx);
240 6x unsigned char* der = nullptr;
241
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6x int der_len = cert ? i2d_X509(cert, &der) : 0;
242
243 6x verify_context vc(
244 6x store_ctx, der,
245
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6x der_len > 0 ? static_cast<std::size_t>(der_len) : 0);
246 6x ok = cd->verify_callback(ok, vc);
247
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6x if (der)
249 6x OPENSSL_free(der);
250 6x }
251
252 10x return ok ? 1 : 0;
253 10x }
254
255 // Server-side ALPN selection. Chooses the server's most-preferred
256 // protocol that the client also offered. On no overlap it sends a fatal
257 // no_application_protocol alert (RFC 7301 §3.2).
258 //
259 // `arg` points at the native context's build-time snapshot of the server
260 // preference list (a std::vector<std::string>), so client offer and server
261 // selection are both taken from the same immutable snapshot.
262 //
263 // The selected protocol pointer must stay valid until the callback runs
264 // again, so we point *out into the client list `in` (OpenSSL keeps it
265 // valid for the connection) rather than into a local buffer.
266 static int
267 2x alpn_select_cb(
268 SSL* /* ssl */, unsigned char const** out, unsigned char* outlen,
269 unsigned char const* in, unsigned int inlen, void* arg)
270 {
271 2x auto const* prefs = static_cast<std::vector<std::string> const*>(arg);
272
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2x if (!prefs || prefs->empty())
273 return SSL_TLSEXT_ERR_NOACK; // nothing configured (defensive)
274
275 // Server preference order wins: for each server protocol, look for a
276 // matching entry in the client's offered list.
277
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3x for (auto const& pref : *prefs)
278 {
279
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3x for (unsigned int i = 0; i + 1 <= inlen;)
280 {
281 2x unsigned int len = in[i];
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x if (i + 1 + len > inlen)
283 break; // malformed
284
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
2x if (len == pref.size() &&
285 1x std::memcmp(in + i + 1, pref.data(), len) == 0)
286 {
287 1x *out = in + i + 1;
288 1x *outlen = static_cast<unsigned char>(len);
289 1x return SSL_TLSEXT_ERR_OK;
290 }
291 1x i += 1 + len;
292 }
293 }
294
295 // The server supports ALPN but shares no protocol with the client.
296 // RFC 7301 §3.2: fail the handshake with a fatal alert.
297 1x return SSL_TLSEXT_ERR_ALERT_FATAL;
298 2x }
299
300 static int
301 3x sni_callback(SSL* ssl, int* /* alert */, void* /* arg */)
302 {
303 3x char const* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
304
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3x if (!servername)
305 1x return SSL_TLSEXT_ERR_NOACK;
306
307 2x SSL_CTX* ctx = SSL_get_SSL_CTX(ssl);
308 2x auto* cd = static_cast<tls_context_data const*>(
309 2x SSL_CTX_get_ex_data(ctx, sni_ctx_data_index));
310
311
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2x if (cd && cd->servername_callback)
312 {
313
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2x if (!cd->servername_callback(servername))
314 1x return SSL_TLSEXT_ERR_ALERT_FATAL;
315 1x }
316
317 1x return SSL_TLSEXT_ERR_OK;
318 3x }
319
320 class openssl_native_context : public native_context_base
321 {
322 public:
323 SSL_CTX* ctx_;
324 tls_context_data const* cd_;
325 // Set when a requested configuration could not be applied: an inverted
326 // protocol window (min > max), a cipher list / suite the library
327 // rejected, a protocol-version bound that would not set, or a CRL that
328 // parsed as neither PEM nor DER. Silently proceeding would negotiate an
329 // unexpected version, ignore the requested ciphers, or weaken revocation
330 // (fail-open under soft_fail), so do_handshake refuses the handshake.
331 1890x bool setup_failed_ = false;
332 // ALPN offer in wire format (length-prefixed), encoded once from the
333 // immutable protocol list. The client sets it per-SSL each handshake;
334 // caching it here avoids re-encoding and re-allocating per connection.
335 std::string alpn_wire_;
336 // Server preference snapshot, captured at build time so the select
337 // callback matches against the same immutable list the client offers
338 // from (see alpn_select_cb). Its address is handed to OpenSSL as the
339 // callback arg, so it must outlive the SSL_CTX (it does — same object).
340 std::vector<std::string> alpn_snapshot_;
341
342 5670x explicit openssl_native_context(tls_context_data const& cd)
343 1890x : ctx_(nullptr)
344 1890x , cd_(&cd)
345 3780x {
346
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x ctx_ = SSL_CTX_new(tls_method_compat());
347
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x if (!ctx_)
348 return;
349
350
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1886 times.
1890x if (sni_ctx_data_index < 0)
351 4x sni_ctx_data_index =
352
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
353
354
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_ex_data(
355 1890x ctx_, sni_ctx_data_index, const_cast<tls_context_data*>(&cd));
356
357
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1887 times.
1890x if (cd.servername_callback)
358
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x SSL_CTX_set_tlsext_servername_callback(ctx_, sni_callback);
359
360 // ALPN server-side selection. The callback only fires when this
361 // context is used as a server; the client offer (encoded once here)
362 // is set per-SSL from alpn_wire_. Snapshot the preference list so the
363 // callback and the client offer share one immutable source.
364
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1886 times.
1890x if (!cd.alpn_protocols.empty())
365 {
366
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x alpn_snapshot_ = cd.alpn_protocols;
367
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x SSL_CTX_set_alpn_select_cb(ctx_, alpn_select_cb, &alpn_snapshot_);
368
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x alpn_wire_ = build_alpn_wire(cd.alpn_protocols);
369 4x }
370
371
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_mode(ctx_, SSL_MODE_ENABLE_PARTIAL_WRITE);
372
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_mode(ctx_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
373 #if defined(SSL_MODE_RELEASE_BUFFERS)
374
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_mode(ctx_, SSL_MODE_RELEASE_BUFFERS);
375 #endif
376
377 // Enforce the configured protocol version window (role-agnostic).
378 // An inverted window (min > max) admits no protocol; fail closed
379 // rather than silently negotiate an unexpected version.
380
2/2
✓ Branch 0 taken 1889 times.
✓ Branch 1 taken 1 time.
1890x if (cd.min_version > cd.max_version)
381 1x setup_failed_ = true;
382
2/4
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1890 times.
✗ Branch 3 not taken.
1890x if (!SSL_CTX_set_min_proto_version(
383 ctx_, openssl_proto_version(cd.min_version)))
384 setup_failed_ = true;
385
2/4
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1890 times.
✗ Branch 3 not taken.
1890x if (!SSL_CTX_set_max_proto_version(
386 ctx_, openssl_proto_version(cd.max_version)))
387 setup_failed_ = true;
388
389 1890x int verify_mode_flag = SSL_VERIFY_NONE;
390
2/2
✓ Branch 0 taken 942 times.
✓ Branch 1 taken 948 times.
1890x if (cd.verification_mode == tls_verify_mode::peer)
391 942x verify_mode_flag = SSL_VERIFY_PEER;
392
2/2
✓ Branch 0 taken 945 times.
✓ Branch 1 taken 3 times.
948x else if (cd.verification_mode == tls_verify_mode::require_peer)
393 3x verify_mode_flag =
394 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
395 // The trampoline runs the revocation soft-fail downgrade and the
396 // user callback, so install it if either is configured.
397 1890x bool const need_trampoline =
398
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1885 times.
1890x cd.verify_callback ||
399 1885x cd.revocation != tls_revocation_policy::disabled;
400
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_verify(
401 1890x ctx_, verify_mode_flag,
402 1890x need_trampoline ? &verify_callback_trampoline : nullptr);
403
404 // PKCS#12 bundle: decode cert + key + chain directly into the
405 // context. This is an alternative credential source; the PEM/DER
406 // fields below are only consulted when no bundle is supplied.
407
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1885 times.
1890x if (!cd.pkcs12_data.empty())
408 {
409 // A bundle that fails to decode or parse (wrong passphrase,
410 // malformed) must not leave the context silently credential-less:
411 // a client using PKCS#12 for mTLS would then fail open against a
412 // verify_mode::peer server. Fail closed like every other setup
413 // error.
414
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x BIO* bio = BIO_new_mem_buf(
415 5x cd.pkcs12_data.data(),
416 5x static_cast<int>(cd.pkcs12_data.size()));
417
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x if (!bio)
418 setup_failed_ = true;
419 else
420 {
421
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x PKCS12* p12 = d2i_PKCS12_bio(bio, nullptr);
422
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x if (!p12)
423 setup_failed_ = true;
424 else
425 {
426 5x EVP_PKEY* pkey = nullptr;
427 5x X509* cert = nullptr;
428 5x STACK_OF(X509)* chain = nullptr;
429
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
5x if (PKCS12_parse(
430 5x p12, cd.pkcs12_password.c_str(), &pkey, &cert,
431 &chain))
432 {
433
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x if (cert)
434
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x SSL_CTX_use_certificate(ctx_, cert);
435
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x if (pkey)
436
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x SSL_CTX_use_PrivateKey(ctx_, pkey);
437
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3x if (chain)
438
4/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
2x for (int i = 0; i < sk_X509_num(chain); ++i)
439 {
440 // add_extra_chain_cert takes ownership of the
441 // dup only on success; free it (and fail
442 // closed) otherwise so a partial chain isn't
443 // sent silently.
444
3/6
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
1x X509* dup = X509_dup(sk_X509_value(chain, i));
445
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2x if (!dup ||
446
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x !SSL_CTX_add_extra_chain_cert(ctx_, dup))
447 {
448 X509_free(dup);
449 setup_failed_ = true;
450 }
451 2x }
452 3x }
453 else
454 2x setup_failed_ = true;
455
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x EVP_PKEY_free(pkey);
456
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x X509_free(cert);
457
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5x if (chain)
458
3/6
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
1x sk_X509_pop_free(chain, X509_free);
459
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x PKCS12_free(p12);
460 }
461
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x ERR_clear_error();
462
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x BIO_free(bio);
463 }
464 5x }
465
466
4/4
✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 941 times.
✓ Branch 3 taken 944 times.
1890x if (cd.pkcs12_data.empty() && !cd.entity_certificate.empty())
467 {
468
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x BIO* bio = BIO_new_mem_buf(
469 941x cd.entity_certificate.data(),
470 941x static_cast<int>(cd.entity_certificate.size()));
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 941 times.
941x if (bio)
472 {
473 941x X509* cert = nullptr;
474
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x if (cd.entity_cert_format == tls_file_format::pem)
475
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
476 else
477 cert = d2i_X509_bio(bio, nullptr);
478
2/2
✓ Branch 0 taken 940 times.
✓ Branch 1 taken 1 time.
941x if (cert)
479 {
480
1/2
✓ Branch 0 taken 940 times.
✗ Branch 1 not taken.
940x SSL_CTX_use_certificate(ctx_, cert);
481
1/2
✓ Branch 0 taken 940 times.
✗ Branch 1 not taken.
940x X509_free(cert);
482 940x }
483
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x BIO_free(bio);
484 941x }
485 941x }
486
487
4/4
✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1884 times.
1890x if (cd.pkcs12_data.empty() && !cd.certificate_chain.empty())
488 {
489
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x BIO* bio = BIO_new_mem_buf(
490 1x cd.certificate_chain.data(),
491 1x static_cast<int>(cd.certificate_chain.size()));
492
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x if (bio)
493 {
494 1x X509* entity =
495
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
496
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x if (entity)
497 {
498
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x SSL_CTX_use_certificate(ctx_, entity);
499
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x X509_free(entity);
500 1x }
501
502 X509* cert;
503
5/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
2x while ((cert = PEM_read_bio_X509(
504 2x bio, nullptr, nullptr, nullptr)) != nullptr)
505 {
506
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x SSL_CTX_add_extra_chain_cert(ctx_, cert);
507 }
508
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x ERR_clear_error();
509
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x BIO_free(bio);
510 1x }
511 1x }
512
513
4/4
✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 942 times.
✓ Branch 3 taken 943 times.
1890x if (cd.pkcs12_data.empty() && !cd.private_key.empty())
514 {
515
1/2
✓ Branch 0 taken 942 times.
✗ Branch 1 not taken.
942x BIO* bio = BIO_new_mem_buf(
516 942x cd.private_key.data(), static_cast<int>(cd.private_key.size()));
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 942 times.
942x if (bio)
518 {
519 942x EVP_PKEY* pkey = nullptr;
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 942 times.
942x if (cd.private_key_format == tls_file_format::pem)
521 {
522
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 941 times.
942x if (cd.password_callback)
523
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x pkey = PEM_read_bio_PrivateKey(
524 1x bio, nullptr, password_callback,
525 1x const_cast<tls_context_data*>(&cd));
526 else
527
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x pkey = PEM_read_bio_PrivateKey(
528 941x bio, nullptr, nullptr, nullptr);
529 942x }
530 else
531 pkey = d2i_PrivateKey_bio(bio, nullptr);
532
2/2
✓ Branch 0 taken 941 times.
✓ Branch 1 taken 1 time.
942x if (pkey)
533 {
534
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x SSL_CTX_use_PrivateKey(ctx_, pkey);
535
1/2
✓ Branch 0 taken 941 times.
✗ Branch 1 not taken.
941x EVP_PKEY_free(pkey);
536 941x }
537
1/2
✓ Branch 0 taken 942 times.
✗ Branch 1 not taken.
942x BIO_free(bio);
538 942x }
539 942x }
540
541
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x X509_STORE* store = SSL_CTX_get_cert_store(ctx_);
542
2/2
✓ Branch 0 taken 954 times.
✓ Branch 1 taken 1890 times.
2844x for (auto const& ca : cd.ca_certificates)
543 {
544
1/2
✓ Branch 0 taken 954 times.
✗ Branch 1 not taken.
954x BIO* bio = BIO_new_mem_buf(ca.data(), static_cast<int>(ca.size()));
545
1/2
✓ Branch 0 taken 954 times.
✗ Branch 1 not taken.
954x if (bio)
546 {
547
1/2
✓ Branch 0 taken 954 times.
✗ Branch 1 not taken.
954x X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
548
2/2
✓ Branch 0 taken 951 times.
✓ Branch 1 taken 3 times.
954x if (cert)
549 {
550
1/2
✓ Branch 0 taken 951 times.
✗ Branch 1 not taken.
951x X509_STORE_add_cert(store, cert);
551
1/2
✓ Branch 0 taken 951 times.
✗ Branch 1 not taken.
951x X509_free(cert);
552 951x }
553
1/2
✓ Branch 0 taken 954 times.
✗ Branch 1 not taken.
954x BIO_free(bio);
554 954x }
555 }
556
557 // Trust anchors from the system store and explicit directories.
558 // Failures leave the affected source unloaded rather than aborting
559 // context creation; the error queue is cleared so it does not leak
560 // into a later handshake.
561
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1889 times.
1890x if (cd.use_default_verify_paths)
562
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x SSL_CTX_set_default_verify_paths(ctx_);
563
2/2
✓ Branch 0 taken 1890 times.
✓ Branch 1 taken 1 time.
1891x for (auto const& path : cd.verify_paths)
564
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x SSL_CTX_load_verify_locations(ctx_, nullptr, path.c_str());
565
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x ERR_clear_error();
566
567 // Certificate revocation via CRLs. Load any supplied CRLs and, when
568 // a revocation policy is active, enable leaf CRL checking. soft_fail
569 // vs hard_fail is applied in the verify trampoline. CRL_CHECK (leaf
570 // only) is used so a missing CRL for a trusted root is not itself an
571 // error.
572
2/2
✓ Branch 0 taken 1887 times.
✓ Branch 1 taken 3 times.
1890x if (cd.revocation != tls_revocation_policy::disabled)
573 {
574
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5x for (auto const& crl_data : cd.crls)
575 {
576
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x BIO* bio = BIO_new_mem_buf(
577 2x crl_data.data(), static_cast<int>(crl_data.size()));
578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x if (!bio)
579 {
580 setup_failed_ = true;
581 continue;
582 }
583 // Accept PEM or DER (the documented contract). Try PEM first,
584 // then rewind and try DER.
585 2x X509_CRL* crl =
586
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x PEM_read_bio_X509_CRL(bio, nullptr, nullptr, nullptr);
587
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2x if (!crl)
588 {
589
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x BIO_reset(bio);
590
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x crl = d2i_X509_CRL_bio(bio, nullptr);
591 1x }
592
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2x if (crl)
593 {
594
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x X509_STORE_add_crl(store, crl);
595
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x X509_CRL_free(crl);
596 1x }
597 else
598 {
599 // A supplied CRL that parses as neither PEM nor DER must
600 // not be silently dropped; record it so the handshake
601 // fails closed rather than weakening revocation.
602 1x setup_failed_ = true;
603 }
604
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x BIO_free(bio);
605 }
606
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);
607
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x ERR_clear_error();
608 3x }
609
610
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_set_verify_depth(ctx_, cd.verify_depth);
611
612 // Cipher configuration. TLS 1.2-and-below use the cipher list;
613 // TLS 1.3 uses the separate ciphersuites API. The security level
614 // is deliberately left at the library default: a weak cipher
615 // string should fail loudly rather than be silently permitted via
616 // a forced @SECLEVEL=0. Callers that genuinely need a lower level
617 // can express it in the cipher string (e.g. "...:@SECLEVEL=0").
618 // A cipher string the library rejects must not silently fall back to
619 // the default suites; fail closed instead.
620
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1886 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
1894x if (!cd.ciphersuites.empty() &&
621
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x !SSL_CTX_set_cipher_list(ctx_, cd.ciphersuites.c_str()))
622 setup_failed_ = true;
623
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1885 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4 times.
1895x if (!cd.ciphersuites_tls13.empty() &&
624
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5x !SSL_CTX_set_ciphersuites(ctx_, cd.ciphersuites_tls13.c_str()))
625 1x setup_failed_ = true;
626 3780x }
627
628 5670x ~openssl_native_context() override
629 5670x {
630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1890 times.
1890x if (ctx_)
631
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
1890x SSL_CTX_free(ctx_);
632 5670x }
633 };
634
635 inline openssl_native_context*
636 3250x get_openssl_native_context(tls_context_data const& cd)
637 {
638 static char key;
639
1/2
✓ Branch 0 taken 1890 times.
✗ Branch 1 not taken.
5140x auto* p = cd.find(&key, [&] { return new openssl_native_context(cd); });
640 3250x return static_cast<openssl_native_context*>(p);
641 }
642
643 SSL_CTX*
644 1896x get_openssl_context(tls_context_data const& cd)
645 {
646 1896x return get_openssl_native_context(cd)->ctx_;
647 }
648
649 } // namespace detail
650
651 struct openssl_stream::impl
652 {
653 capy::any_stream* s_;
654 tls_context ctx_;
655 1896x SSL* ssl_ = nullptr;
656 1896x BIO* ext_bio_ = nullptr;
657 1896x bool used_ = false;
658
659 // ALPN protocol negotiated during the handshake (empty if none).
660 std::string alpn_selected_;
661
662 std::vector<char> in_buf_;
663 std::vector<char> out_buf_;
664
665 capy::async_mutex io_cm_;
666
667 5688x impl(capy::any_stream& s, tls_context ctx) : s_(&s), ctx_(std::move(ctx))
668 1896x {
669
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x in_buf_.resize(default_buffer_size);
670
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x out_buf_.resize(default_buffer_size);
671 3792x }
672
673 3792x ~impl()
674 1896x {
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1896 times.
1896x if (ext_bio_)
676
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x BIO_free(ext_bio_);
677
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x if (ssl_)
678
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x SSL_free(ssl_);
679 3792x }
680
681 20x void reset()
682 {
683
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20x if (!ssl_)
684 return;
685
686 // Preserves SSL* and BIO pair, releases session state
687 20x SSL_clear(ssl_);
688
689 // Drain stale data from the external BIO
690 char drain[1024];
691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20x while (BIO_ctrl_pending(ext_bio_) > 0)
692 BIO_read(ext_bio_, drain, sizeof(drain));
693
694 // SSL_clear clears per-session settings; reapply hostname
695 20x auto& cd = detail::get_tls_context_data(ctx_);
696 20x apply_hostname_verification(ssl_, cd.hostname);
697
698 20x alpn_selected_.clear();
699 20x used_ = false;
700 20x }
701
702 // Record the ALPN protocol selected during the handshake, if any.
703 752x void capture_alpn()
704 {
705 752x unsigned char const* data = nullptr;
706 752x unsigned int len = 0;
707 752x SSL_get0_alpn_selected(ssl_, &data, &len);
708
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 750 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
752x if (data && len)
709 2x alpn_selected_.assign(reinterpret_cast<char const*>(data), len);
710 752x }
711
712
10/24
✓ Branch 0 taken 163429 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 163429 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 163429 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 163429 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 163429 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 163429 times.
✓ Branch 15 taken 163429 times.
✓ Branch 16 taken 163429 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 285968 times.
✓ Branch 19 taken 163429 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
1389081x capy::task<std::error_code> flush_output()
713 163429x {
714
3/4
✓ Branch 0 taken 306412 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 163428 times.
✓ Branch 3 taken 142984 times.
306412x while (BIO_ctrl_pending(ext_bio_) > 0)
715 {
716 142984x std::size_t got = 0;
717
5/6
✓ Branch 0 taken 285968 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142978 times.
✓ Branch 3 taken 142990 times.
✓ Branch 4 taken 142984 times.
✓ Branch 5 taken 142984 times.
285968x while (BIO_ctrl_pending(ext_bio_) > 0 && got < out_buf_.size())
718 {
719
1/2
✓ Branch 0 taken 142984 times.
✗ Branch 1 not taken.
142984x int put = static_cast<int>(BIO_ctrl_pending(ext_bio_));
720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 142984 times.
142984x put = (std::min)(put, static_cast<int>(out_buf_.size() - got));
721
1/2
✓ Branch 0 taken 142984 times.
✗ Branch 1 not taken.
142984x int r = BIO_read(ext_bio_, out_buf_.data() + got, put);
722
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 142984 times.
142984x if (r <= 0)
723 break;
724 142984x got += static_cast<std::size_t>(r);
725 142984x }
726
1/2
✓ Branch 0 taken 142984 times.
✗ Branch 1 not taken.
142984x if (got == 0)
727 break;
728
729 {
730
5/12
✓ Branch 0 taken 142984 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 142984 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 142984 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 285968 times.
✓ Branch 11 taken 428952 times.
142984x auto [lec] = co_await io_cm_.lock();
731
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 428952 times.
428952x if (lec)
732 co_return lec;
733 428952x capy::async_mutex::lock_guard io_guard(&io_cm_);
734
10/18
✗ Branch 0 not taken.
✓ Branch 1 taken 428952 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 428952 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 428952 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 428952 times.
✓ Branch 8 taken 142984 times.
✓ Branch 9 taken 285968 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 142984 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 142984 times.
✓ Branch 15 taken 142984 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 142984 times.
1429840x auto [ec, n] = co_await capy::write(
735 857904x *s_, capy::const_buffer(out_buf_.data(), got));
736
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 142983 times.
142984x if (ec)
737
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2x co_return ec;
738 428952x }
739 428952x }
740
1/2
✓ Branch 0 taken 163428 times.
✗ Branch 1 not taken.
163428x co_return std::error_code{};
741 285968x }
742
743
10/24
✓ Branch 0 taken 21800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21800 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21800 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 21800 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 21800 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 21800 times.
✓ Branch 15 taken 21800 times.
✓ Branch 16 taken 21800 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 43600 times.
✓ Branch 19 taken 21800 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
196200x capy::task<std::error_code> read_input()
744 21800x {
745
4/10
✓ Branch 0 taken 21800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21800 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 43600 times.
✓ Branch 9 taken 65400 times.
21800x auto [lec] = co_await io_cm_.lock();
746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65400 times.
65400x if (lec)
747 co_return lec;
748 65400x capy::async_mutex::lock_guard io_guard(&io_cm_);
749
9/16
✓ Branch 0 taken 65400 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65400 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 65400 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 21800 times.
✓ Branch 7 taken 43600 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 21800 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 21800 times.
✓ Branch 13 taken 21800 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 21800 times.
152600x auto [ec, n] = co_await s_->read_some(
750 65400x capy::mutable_buffer(in_buf_.data(), in_buf_.size()));
751
2/2
✓ Branch 0 taken 596 times.
✓ Branch 1 taken 21204 times.
21800x if (ec)
752
2/4
✓ Branch 0 taken 596 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 596 times.
✗ Branch 3 not taken.
1192x co_return ec;
753
754
2/4
✓ Branch 0 taken 21204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21204 times.
✗ Branch 3 not taken.
42408x int got = BIO_write(ext_bio_, in_buf_.data(), static_cast<int>(n));
755
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 21204 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21204 times.
42408x if (got < static_cast<int>(n))
756 {
757 co_return make_error_code(std::errc::no_buffer_space);
758 }
759
760
1/2
✓ Branch 0 taken 21204 times.
✗ Branch 1 not taken.
21204x co_return std::error_code{};
761 109000x }
762
763 capy::io_task<std::size_t>
764
9/24
✓ Branch 0 taken 140853 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140853 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140853 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 140853 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140853 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 140853 times.
✓ Branch 15 taken 140853 times.
✓ Branch 16 taken 140853 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 140853 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
704265x do_read_some(capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers)
765 140853x {
766 140853x std::error_code ec;
767 140853x std::size_t total_read = 0;
768
769
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 140853 times.
✓ Branch 2 taken 140853 times.
✗ Branch 3 not taken.
281706x for (auto& buf : buffers)
770 {
771 140853x char* dest = static_cast<char*>(buf.data());
772 140853x int remaining = static_cast<int>(buf.size());
773
774
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 200895 times.
200895x while (remaining > 0)
775 {
776
1/2
✓ Branch 0 taken 200895 times.
✗ Branch 1 not taken.
200895x ERR_clear_error();
777
1/2
✓ Branch 0 taken 200895 times.
✗ Branch 1 not taken.
200895x int ret = SSL_read(ssl_, dest, remaining);
778
779
2/2
✓ Branch 0 taken 140823 times.
✓ Branch 1 taken 60072 times.
200895x if (ret > 0)
780 {
781 140823x dest += ret;
782 140823x remaining -= ret;
783 140823x total_read += static_cast<std::size_t>(ret);
784
785
1/2
✓ Branch 0 taken 140823 times.
✗ Branch 1 not taken.
140823x if (total_read > 0)
786
2/4
✓ Branch 0 taken 140823 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140823 times.
✗ Branch 3 not taken.
281676x co_return {std::error_code{}, total_read};
787 }
788 else
789 {
790
1/2
✓ Branch 0 taken 60072 times.
✗ Branch 1 not taken.
60072x int err = SSL_get_error(ssl_, ret);
791
792
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60072 times.
60072x if (err == SSL_ERROR_WANT_WRITE)
793 {
794 ec = co_await flush_output();
795 if (ec)
796 co_return {ec, total_read};
797 }
798
2/2
✓ Branch 0 taken 60048 times.
✓ Branch 1 taken 24 times.
60072x else if (err == SSL_ERROR_WANT_READ)
799 {
800
9/14
✓ Branch 0 taken 60048 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60048 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20016 times.
✓ Branch 5 taken 40032 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 20016 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 20016 times.
✓ Branch 11 taken 20016 times.
✓ Branch 12 taken 40032 times.
✓ Branch 13 taken 60048 times.
80064x ec = co_await flush_output();
801
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60048 times.
60048x if (ec)
802 co_return {ec, total_read};
803
804
8/14
✓ Branch 0 taken 60048 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60048 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20016 times.
✓ Branch 5 taken 40032 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 20016 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 20016 times.
✓ Branch 11 taken 20016 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 20016 times.
80064x ec = co_await read_input();
805
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 20010 times.
20016x if (ec)
806 {
807
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6x if (ec == capy::cond::eof)
808 {
809
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1x if (SSL_get_shutdown(ssl_) &
810 SSL_RECEIVED_SHUTDOWN)
811 ec = make_error_code(capy::error::eof);
812 else
813 1x ec = make_error_code(
814 capy::error::stream_truncated);
815 1x }
816
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6x co_return {ec, total_read};
817 }
818 20010x }
819
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24x else if (err == SSL_ERROR_ZERO_RETURN)
820 {
821
3/6
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48x co_return {
822 24x make_error_code(capy::error::eof), total_read};
823 }
824 else if (err == SSL_ERROR_SYSCALL)
825 {
826 unsigned long ssl_err = ERR_get_error();
827 if (ssl_err == 0)
828 ec = make_error_code(capy::error::stream_truncated);
829 else
830 ec = make_openssl_error(ssl_err);
831 co_return {ec, total_read};
832 }
833 else
834 {
835 unsigned long ssl_err = ERR_get_error();
836 ec = make_openssl_error(ssl_err);
837 co_return {ec, total_read};
838 }
839 60072x }
840 200895x }
841 140853x }
842
843 co_return {std::error_code{}, total_read};
844 220917x }
845
846 capy::io_task<std::size_t>
847
9/24
✓ Branch 0 taken 140849 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140849 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140849 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 140849 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140849 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 140849 times.
✓ Branch 15 taken 140849 times.
✓ Branch 16 taken 140849 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 140849 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
704245x do_write_some(capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers)
848 140849x {
849 140849x std::error_code ec;
850 140849x std::size_t total_written = 0;
851
852
2/4
✓ Branch 0 taken 140849 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140849 times.
✗ Branch 3 not taken.
281698x for (auto const& buf : buffers)
853 {
854 140849x char const* src = static_cast<char const*>(buf.data());
855 140849x int remaining = static_cast<int>(buf.size());
856
857
1/2
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
422547x while (remaining > 0)
858 {
859
1/2
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
422547x ERR_clear_error();
860
1/2
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
422547x int ret = SSL_write(ssl_, src, remaining);
861
862
1/2
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
422547x if (ret > 0)
863 {
864 422547x src += ret;
865 422547x remaining -= ret;
866 422547x total_written += static_cast<std::size_t>(ret);
867
868
1/2
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
422547x if (total_written > 0)
869 {
870
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 422547 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 422547 times.
✓ Branch 4 taken 140849 times.
✓ Branch 5 taken 281698 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 140849 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140849 times.
✓ Branch 11 taken 140849 times.
✓ Branch 12 taken 140849 times.
✗ Branch 13 not taken.
704245x ec = co_await flush_output();
871
2/4
✓ Branch 0 taken 140849 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140849 times.
✗ Branch 3 not taken.
140849x co_return {ec, total_written};
872 }
873 }
874 else
875 {
876 int err = SSL_get_error(ssl_, ret);
877
878 if (err == SSL_ERROR_WANT_WRITE)
879 {
880 ec = co_await flush_output();
881 if (ec)
882 co_return {ec, total_written};
883 }
884 else if (err == SSL_ERROR_WANT_READ)
885 {
886 ec = co_await flush_output();
887 if (ec)
888 co_return {ec, total_written};
889
890 ec = co_await read_input();
891 if (ec)
892 co_return {ec, total_written};
893 }
894 else
895 {
896 unsigned long ssl_err = ERR_get_error();
897 ec = make_openssl_error(ssl_err);
898 co_return {ec, total_written};
899 }
900 }
901 140849x }
902 140849x }
903
904 co_return {std::error_code{}, total_written};
905 422547x }
906
907
11/24
✓ Branch 0 taken 1354 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1354 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1354 times.
✓ Branch 6 taken 1504 times.
✓ Branch 7 taken 2858 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1354 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 1354 times.
✓ Branch 15 taken 1354 times.
✓ Branch 16 taken 1354 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 1504 times.
✓ Branch 19 taken 1354 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
8274x capy::io_task<> do_handshake(int type)
908 1354x {
909 // A requested configuration could not be applied when the native
910 // context was built (inverted protocol window, rejected cipher/
911 // version, or an unparseable CRL). Refuse the handshake rather than
912 // proceed with weakened or unexpected settings.
913
2/4
✓ Branch 0 taken 2858 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2858 times.
✗ Branch 3 not taken.
5716x auto* nc = detail::get_openssl_native_context(
914 2858x detail::get_tls_context_data(ctx_));
915
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2853 times.
2858x if (nc->setup_failed_)
916
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
1359x co_return std::make_error_code(std::errc::invalid_argument);
917
918
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2849 times.
2853x if (used_)
919
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4x reset();
920
921 2853x std::error_code ec;
922
923 // Client offers its ALPN protocol list; the server selects via the
924 // context callback. Role is only known here, so set the pre-encoded
925 // wire offer per-SSL.
926
4/4
✓ Branch 0 taken 952 times.
✓ Branch 1 taken 1901 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 950 times.
2853x if (type == openssl_stream::client && !nc->alpn_wire_.empty())
927 {
928 // SSL_set_alpn_protos uses the inverted convention: 0 = success.
929 // A non-zero return (allocation failure) means the offer was not
930 // installed; fail closed rather than negotiate nothing silently.
931
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2x if (SSL_set_alpn_protos(
932 2x ssl_,
933 reinterpret_cast<unsigned char const*>(
934 2x nc->alpn_wire_.data()),
935 2x static_cast<unsigned int>(nc->alpn_wire_.size())) != 0)
936 co_return std::make_error_code(std::errc::invalid_argument);
937 2x }
938
939 7485x while (true)
940 {
941
1/2
✓ Branch 0 taken 7485 times.
✗ Branch 1 not taken.
7485x ERR_clear_error();
942 7485x int ret;
943
2/2
✓ Branch 0 taken 1340 times.
✓ Branch 1 taken 6145 times.
7485x if (type == openssl_stream::client)
944
1/2
✓ Branch 0 taken 1340 times.
✗ Branch 1 not taken.
1340x ret = SSL_connect(ssl_);
945 else
946
2/2
✓ Branch 0 taken 4641 times.
✓ Branch 1 taken 1504 times.
6145x ret = SSL_accept(ssl_);
947
948
2/2
✓ Branch 0 taken 752 times.
✓ Branch 1 taken 5229 times.
5981x if (ret == 1)
949 {
950 752x used_ = true;
951
2/2
✓ Branch 0 taken 2256 times.
✓ Branch 1 taken 1504 times.
752x capture_alpn();
952
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 2256 times.
✓ Branch 2 taken 2256 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 752 times.
✓ Branch 5 taken 1504 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 752 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 752 times.
✓ Branch 11 taken 752 times.
✓ Branch 12 taken 752 times.
✗ Branch 13 not taken.
3008x ec = co_await flush_output();
953
2/4
✓ Branch 0 taken 752 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 752 times.
✗ Branch 3 not taken.
752x co_return {ec};
954 }
955 else
956 {
957
1/2
✓ Branch 0 taken 5229 times.
✗ Branch 1 not taken.
5229x int err = SSL_get_error(ssl_, ret);
958
959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5229 times.
5229x if (err == SSL_ERROR_WANT_WRITE)
960 {
961 ec = co_await flush_output();
962 if (ec)
963 co_return {ec};
964 }
965
2/2
✓ Branch 0 taken 5211 times.
✓ Branch 1 taken 18 times.
5229x else if (err == SSL_ERROR_WANT_READ)
966 {
967
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 5211 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5211 times.
✓ Branch 4 taken 1737 times.
✓ Branch 5 taken 3474 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1737 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1737 times.
✓ Branch 11 taken 1737 times.
✓ Branch 12 taken 3474 times.
✓ Branch 13 taken 5211 times.
6948x ec = co_await flush_output();
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5211 times.
5211x if (ec)
969 co_return {ec};
970
971
8/14
✓ Branch 0 taken 5211 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5211 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1737 times.
✓ Branch 5 taken 3474 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1737 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1737 times.
✓ Branch 11 taken 1737 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1737 times.
6948x ec = co_await read_input();
972
2/2
✓ Branch 0 taken 579 times.
✓ Branch 1 taken 1158 times.
1737x if (ec)
973
2/4
✓ Branch 0 taken 579 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 579 times.
✗ Branch 3 not taken.
579x co_return {ec};
974 1158x }
975 else
976 {
977
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18x unsigned long ssl_err = ERR_get_error();
978 18x ec = make_openssl_error(ssl_err);
979
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18x co_return {ec};
980 18x }
981 5229x }
982 5981x }
983 12814x }
984
985
11/24
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 56 times.
✓ Branch 7 taken 95 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 39 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 39 times.
✓ Branch 15 taken 39 times.
✓ Branch 16 taken 39 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 39 times.
✓ Branch 19 taken 56 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
251x capy::io_task<> do_shutdown()
986 39x {
987 95x std::error_code ec;
988
989 225x while (true)
990 {
991
1/2
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
225x ERR_clear_error();
992
1/2
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
225x int ret = SSL_shutdown(ssl_);
993
994
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 141 times.
225x if (ret == 1)
995 {
996
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 28 times.
✓ Branch 5 taken 56 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 28 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 28 times.
✓ Branch 11 taken 28 times.
✓ Branch 12 taken 28 times.
✗ Branch 13 not taken.
112x ec = co_await flush_output();
997
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28x co_return {ec};
998 }
999
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 75 times.
141x else if (ret == 0)
1000 {
1001
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
✓ Branch 4 taken 25 times.
✓ Branch 5 taken 50 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 25 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 25 times.
✓ Branch 11 taken 25 times.
✓ Branch 12 taken 50 times.
✓ Branch 13 taken 75 times.
100x ec = co_await flush_output();
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75x if (ec)
1003 co_return {ec};
1004
1005
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
✓ Branch 4 taken 25 times.
✓ Branch 5 taken 50 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 25 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 25 times.
✓ Branch 11 taken 25 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 25 times.
100x ec = co_await read_input();
1006
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25x if (ec)
1007 {
1008 3x ec = normalize_openssl_shutdown_read_error(ec);
1009
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3x co_return {ec};
1010 }
1011 22x }
1012 else
1013 {
1014
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66x int err = SSL_get_error(ssl_, ret);
1015
1016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66x if (err == SSL_ERROR_WANT_WRITE)
1017 {
1018 ec = co_await flush_output();
1019 if (ec)
1020 co_return {ec};
1021 }
1022
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66x else if (err == SSL_ERROR_WANT_READ)
1023 {
1024
9/14
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 22 times.
✓ Branch 11 taken 22 times.
✓ Branch 12 taken 44 times.
✓ Branch 13 taken 66 times.
88x ec = co_await flush_output();
1025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66x if (ec)
1026 co_return {ec};
1027
1028
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 22 times.
✓ Branch 11 taken 22 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 22 times.
88x ec = co_await read_input();
1029
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 14 times.
22x if (ec)
1030 {
1031 8x ec = normalize_openssl_shutdown_read_error(ec);
1032
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8x co_return {ec};
1033 }
1034 14x }
1035 else
1036 {
1037 unsigned long ssl_err = ERR_get_error();
1038 if (ssl_err == 0 && err == SSL_ERROR_SYSCALL)
1039 {
1040 ec = {};
1041 }
1042 else
1043 {
1044 ec = make_openssl_error(ssl_err);
1045 }
1046 co_return {ec};
1047 }
1048 66x }
1049 169x }
1050 283x }
1051
1052 1896x std::error_code init_ssl()
1053 {
1054 1896x auto& cd = detail::get_tls_context_data(ctx_);
1055 1896x SSL_CTX* native_ctx = detail::get_openssl_context(cd);
1056
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x if (!native_ctx)
1057 {
1058 unsigned long err = ERR_get_error();
1059 return make_openssl_error(err);
1060 }
1061
1062 1896x ssl_ = SSL_new(native_ctx);
1063
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x if (!ssl_)
1064 {
1065 unsigned long err = ERR_get_error();
1066 return make_openssl_error(err);
1067 }
1068
1069 1896x BIO* int_bio = nullptr;
1070
1/2
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
1896x if (!BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0))
1071 {
1072 unsigned long err = ERR_get_error();
1073 SSL_free(ssl_);
1074 ssl_ = nullptr;
1075 return make_openssl_error(err);
1076 }
1077
1078 1896x SSL_set_bio(ssl_, int_bio, int_bio);
1079
1080 1896x apply_hostname_verification(ssl_, cd.hostname);
1081
1082 1896x return {};
1083 1896x }
1084 };
1085
1086 openssl_stream::impl*
1087 1896x openssl_stream::make_impl(capy::any_stream& stream, tls_context const& ctx)
1088 {
1089
1/4
✓ Branch 0 taken 1896 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1896x auto* p = new impl(stream, ctx);
1090
1091 1896x auto ec = p->init_ssl();
1092
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1896 times.
1896x if (ec)
1093 {
1094 delete p;
1095 return nullptr;
1096 }
1097
1098 1896x return p;
1099 1896x }
1100
1101 3796x openssl_stream::~openssl_stream()
1102 1898x {
1103
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1895 times.
1898x delete impl_;
1104 3796x }
1105
1106 4x openssl_stream::openssl_stream(openssl_stream&& other) noexcept
1107 2x : stream_(std::move(other.stream_))
1108 2x , impl_(other.impl_)
1109 4x {
1110 2x other.impl_ = nullptr;
1111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x if (impl_)
1112 2x impl_->s_ = &stream_;
1113 4x }
1114
1115 openssl_stream&
1116 1x openssl_stream::operator=(openssl_stream&& other) noexcept
1117 {
1118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1x if (this != &other)
1119 {
1120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1x delete impl_;
1121 1x stream_ = std::move(other.stream_);
1122 1x impl_ = other.impl_;
1123 1x other.impl_ = nullptr;
1124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1x if (impl_)
1125 1x impl_->s_ = &stream_;
1126 1x }
1127 1x return *this;
1128 }
1129
1130 capy::io_task<std::size_t>
1131
11/24
✓ Branch 0 taken 140853 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140853 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140853 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 281706 times.
✓ Branch 7 taken 422559 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140853 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 140853 times.
✓ Branch 15 taken 140853 times.
✓ Branch 16 taken 140853 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 281706 times.
✓ Branch 19 taken 140853 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
985971x openssl_stream::do_read_some(
1132 capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers)
1133 140853x {
1134
9/16
✓ Branch 0 taken 422559 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 422559 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140853 times.
✓ Branch 5 taken 281706 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 140853 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140853 times.
✓ Branch 11 taken 140853 times.
✓ Branch 12 taken 140853 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 140853 times.
704265x co_return co_await impl_->do_read_some(buffers);
1135 281706x }
1136
1137 capy::io_task<std::size_t>
1138
11/24
✓ Branch 0 taken 140849 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 140849 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140849 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 281698 times.
✓ Branch 7 taken 422547 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140849 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 140849 times.
✓ Branch 15 taken 140849 times.
✓ Branch 16 taken 140849 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 281698 times.
✓ Branch 19 taken 140849 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
985943x openssl_stream::do_write_some(
1139 capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers)
1140 140849x {
1141
9/16
✓ Branch 0 taken 422547 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 422547 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140849 times.
✓ Branch 5 taken 281698 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 140849 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 140849 times.
✓ Branch 11 taken 140849 times.
✓ Branch 12 taken 140849 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 140849 times.
704245x co_return co_await impl_->do_write_some(buffers);
1142 281698x }
1143
1144 capy::io_task<>
1145
11/24
✓ Branch 0 taken 1354 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1354 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1354 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2708 times.
✓ Branch 7 taken 4062 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1354 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 1354 times.
✓ Branch 15 taken 1354 times.
✓ Branch 16 taken 1354 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2708 times.
✓ Branch 19 taken 1354 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
9478x openssl_stream::handshake(handshake_type type)
1146 1354x {
1147
9/16
✓ Branch 0 taken 4062 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4062 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1354 times.
✓ Branch 5 taken 2708 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1354 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1354 times.
✓ Branch 11 taken 1354 times.
✓ Branch 12 taken 1354 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1354 times.
6770x co_return co_await impl_->do_handshake(type);
1148 2708x }
1149
1150 capy::io_task<>
1151
11/24
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 78 times.
✓ Branch 7 taken 117 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 39 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 39 times.
✓ Branch 15 taken 39 times.
✓ Branch 16 taken 39 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 78 times.
✓ Branch 19 taken 39 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
273x openssl_stream::shutdown()
1152 39x {
1153
9/16
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✓ Branch 5 taken 78 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 39 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 39 times.
✓ Branch 11 taken 39 times.
✓ Branch 12 taken 39 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 39 times.
156x co_return co_await impl_->do_shutdown();
1154 78x }
1155
1156 void
1157 16x openssl_stream::reset()
1158 {
1159 16x impl_->reset();
1160 16x }
1161
1162 std::string_view
1163 1x openssl_stream::name() const noexcept
1164 {
1165
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x return "openssl";
1166 }
1167
1168 std::string_view
1169 3x openssl_stream::alpn_protocol() const noexcept
1170 {
1171 3x return impl_->alpn_selected_;
1172 }
1173
1174 } // namespace boost::corosio
1175