src/openssl/src/openssl_stream.cpp

77.3% Lines (163/211) 89.7% List of functions (26/29)
openssl_stream.cpp
f(x) Functions (29)
Function Calls Lines Blocks
boost::corosio::(anonymous namespace)::tls_method_compat() :65 2114x 100.0% 100.0% boost::corosio::(anonymous namespace)::apply_hostname_verification(ssl_st*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) :75 2139x 100.0% 100.0% boost::corosio::(anonymous namespace)::normalize_openssl_shutdown_read_error(std::error_code) :91 8x 77.8% 62.0% boost::corosio::detail::password_callback(char*, int, int, void*) :116 0 0.0% 0.0% boost::corosio::detail::sni_callback(ssl_st*, int*, void*) :138 2x 90.0% 89.0% boost::corosio::detail::openssl_native_context::openssl_native_context(boost::corosio::detail::tls_context_data const&) :163 2114x 93.4% 91.0% boost::corosio::detail::openssl_native_context::~openssl_native_context() :295 4228x 100.0% 100.0% boost::corosio::detail::get_openssl_context(boost::corosio::detail::tls_context_data const&) :303 2119x 100.0% 100.0% boost::corosio::detail::get_openssl_context(boost::corosio::detail::tls_context_data const&)::{lambda()#1}::operator()() const :306 2114x 100.0% 71.0% boost::corosio::openssl_stream::impl::impl(boost::capy::any_stream&, boost::corosio::tls_context) :325 2119x 100.0% 64.0% boost::corosio::openssl_stream::impl::~impl() :331 2119x 100.0% 100.0% boost::corosio::openssl_stream::impl::reset() :339 20x 77.8% 77.0% boost::corosio::openssl_stream::impl::flush_output() :359 78073x 100.0% 44.0% boost::corosio::openssl_stream::impl::read_input() :390 4151x 100.0% 44.0% boost::corosio::openssl_stream::impl::do_read_some(boost::capy::buffer_array<16ul, false>) :411 73030x 100.0% 42.0% boost::corosio::openssl_stream::impl::do_write_some(boost::capy::buffer_array<16ul, true>) :497 73160x 100.0% 42.0% boost::corosio::openssl_stream::impl::do_handshake(int) :558 1444x 100.0% 44.0% boost::corosio::openssl_stream::impl::do_shutdown() :611 36x 100.0% 44.0% boost::corosio::openssl_stream::impl::init_ssl() :679 2119x 50.0% 60.0% boost::corosio::openssl_stream::make_impl(boost::capy::any_stream&, boost::corosio::tls_context const&) :717 2119x 71.4% 52.0% boost::corosio::openssl_stream::~openssl_stream() :731 2119x 100.0% 100.0% boost::corosio::openssl_stream::openssl_stream(boost::corosio::openssl_stream&&) :736 0 0.0% 0.0% boost::corosio::openssl_stream::operator=(boost::corosio::openssl_stream&&) :744 0 0.0% 0.0% boost::corosio::openssl_stream::do_read_some(boost::capy::buffer_array<16ul, false>) :757 73030x 100.0% 42.0% boost::corosio::openssl_stream::do_write_some(boost::capy::buffer_array<16ul, true>) :764 73160x 100.0% 42.0% boost::corosio::openssl_stream::handshake(boost::corosio::tls_stream::handshake_type) :771 1444x 100.0% 44.0% boost::corosio::openssl_stream::shutdown() :777 36x 100.0% 44.0% boost::corosio::openssl_stream::reset() :783 16x 100.0% 100.0% boost::corosio::openssl_stream::name() const :789 1x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #include <boost/corosio/openssl_stream.hpp>
11 #include <boost/corosio/detail/config.hpp>
12 #include <boost/capy/buffers/buffer_array.hpp>
13 #include <boost/capy/ex/async_mutex.hpp>
14 #include <boost/capy/error.hpp>
15 #include <boost/capy/write.hpp>
16
17 // Internal context implementation
18 #include "src/tls/detail/context_impl.hpp"
19
20 #include <openssl/ssl.h>
21 #include <openssl/err.h>
22 #include <openssl/bio.h>
23 #include <openssl/x509.h>
24
25 #include <algorithm>
26 #include <array>
27 #include <cstring>
28 #include <vector>
29
30 /*
31 openssl_stream Architecture
32 ===========================
33
34 TLS layer wrapping an underlying stream (via any_stream). Supports one
35 concurrent read_some and one concurrent write_some (like Asio's ssl::stream).
36
37 Data Flow (using BIO pairs)
38 ---------------------------
39 App -> SSL_write -> int_bio_ -> BIO_read(ext_bio_) -> out_buf_ -> s_.write_some -> Network
40 App <- SSL_read <- int_bio_ <- BIO_write(ext_bio_) <- in_buf_ <- s_.read_some <- Network
41
42 WANT_READ / WANT_WRITE Pattern
43 ------------------------------
44 OpenSSL's SSL_read/SSL_write return SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
45 when they need I/O. Our coroutine handles this by:
46
47 1. Call SSL_read or SSL_write
48 2. Check for pending output in ext_bio_ via BIO_ctrl_pending
49 3. If output pending: write to network via s_.write_some
50 4. If SSL_ERROR_WANT_READ: read from network into ext_bio_ via s_.read_some + BIO_write
51 5. Loop back to step 1
52
53 Renegotiation causes cross-direction I/O: SSL_read may need to write
54 handshake data, SSL_write may need to read. Each operation handles
55 whatever I/O direction OpenSSL requests.
56 */
57
58 namespace boost::corosio {
59
60 namespace {
61
62 constexpr std::size_t default_buffer_size = 16384;
63
64 inline SSL_METHOD const*
65 2114x tls_method_compat() noexcept
66 {
67 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
68 2114x return TLS_method();
69 #else
70 return SSLv23_method();
71 #endif
72 }
73
74 inline void
75 2139x apply_hostname_verification(SSL* ssl, std::string const& hostname)
76 {
77 2139x if (hostname.empty())
78 2135x return;
79
80 4x SSL_set_tlsext_host_name(ssl, hostname.c_str());
81
82 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
83 4x SSL_set1_host(ssl, hostname.c_str());
84 #else
85 if (auto* param = SSL_get0_param(ssl))
86 X509_VERIFY_PARAM_set1_host(param, hostname.c_str(), 0);
87 #endif
88 }
89
90 inline std::error_code
91 8x normalize_openssl_shutdown_read_error(std::error_code ec) noexcept
92 {
93 8x if (!ec)
94 return ec;
95
96 10x if (ec == make_error_code(capy::error::eof) ||
97 4x ec == make_error_code(capy::error::canceled) ||
98 2x ec == std::errc::connection_reset ||
99 10x ec == std::errc::connection_aborted || ec == std::errc::broken_pipe)
100 8x return make_error_code(capy::error::stream_truncated);
101
102 return ec;
103 }
104
105 } // namespace
106
107 //
108 // Native context caching
109 //
110
111 namespace detail {
112
113 static int sni_ctx_data_index = -1;
114
115 static int
116 password_callback(char* buf, int size, int rwflag, void* userdata)
117 {
118 auto* cd = static_cast<tls_context_data const*>(userdata);
119 if (!cd || !cd->password_callback)
120 return 0;
121
122 tls_password_purpose purpose = (rwflag == 0)
123 ? tls_password_purpose::for_reading
124 : tls_password_purpose::for_writing;
125
126 std::string password =
127 cd->password_callback(static_cast<std::size_t>(size), purpose);
128
129 int len = static_cast<int>(password.size());
130 if (len > size)
131 len = size;
132
133 std::memcpy(buf, password.data(), static_cast<std::size_t>(len));
134 return len;
135 }
136
137 static int
138 2x sni_callback(SSL* ssl, int* /* alert */, void* /* arg */)
139 {
140 2x char const* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
141 2x if (!servername)
142 return SSL_TLSEXT_ERR_NOACK;
143
144 2x SSL_CTX* ctx = SSL_get_SSL_CTX(ssl);
145 auto* cd = static_cast<tls_context_data const*>(
146 2x SSL_CTX_get_ex_data(ctx, sni_ctx_data_index));
147
148 2x if (cd && cd->servername_callback)
149 {
150 2x if (!cd->servername_callback(servername))
151 1x return SSL_TLSEXT_ERR_ALERT_FATAL;
152 }
153
154 1x return SSL_TLSEXT_ERR_OK;
155 }
156
157 class openssl_native_context : public native_context_base
158 {
159 public:
160 SSL_CTX* ctx_;
161 tls_context_data const* cd_;
162
163 2114x explicit openssl_native_context(tls_context_data const& cd)
164 4228x : ctx_(nullptr)
165 2114x , cd_(&cd)
166 {
167 2114x ctx_ = SSL_CTX_new(tls_method_compat());
168 2114x if (!ctx_)
169 return;
170
171 2114x if (sni_ctx_data_index < 0)
172 2x sni_ctx_data_index =
173 2x SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
174
175 2114x SSL_CTX_set_ex_data(
176 ctx_, sni_ctx_data_index, const_cast<tls_context_data*>(&cd));
177
178 2114x if (cd.servername_callback)
179 2x SSL_CTX_set_tlsext_servername_callback(ctx_, sni_callback);
180
181 2114x SSL_CTX_set_mode(ctx_, SSL_MODE_ENABLE_PARTIAL_WRITE);
182 2114x SSL_CTX_set_mode(ctx_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
183 #if defined(SSL_MODE_RELEASE_BUFFERS)
184 2114x SSL_CTX_set_mode(ctx_, SSL_MODE_RELEASE_BUFFERS);
185 #endif
186
187 2114x int verify_mode_flag = SSL_VERIFY_NONE;
188 2114x if (cd.verification_mode == tls_verify_mode::peer)
189 1053x verify_mode_flag = SSL_VERIFY_PEER;
190 1061x else if (cd.verification_mode == tls_verify_mode::require_peer)
191 3x verify_mode_flag =
192 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
193 2114x SSL_CTX_set_verify(ctx_, verify_mode_flag, nullptr);
194
195 2114x if (!cd.entity_certificate.empty())
196 {
197 1058x BIO* bio = BIO_new_mem_buf(
198 1058x cd.entity_certificate.data(),
199 1058x static_cast<int>(cd.entity_certificate.size()));
200 1058x if (bio)
201 {
202 1058x X509* cert = nullptr;
203 1058x if (cd.entity_cert_format == tls_file_format::pem)
204 1058x cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
205 else
206 cert = d2i_X509_bio(bio, nullptr);
207 1058x if (cert)
208 {
209 1058x SSL_CTX_use_certificate(ctx_, cert);
210 1058x X509_free(cert);
211 }
212 1058x BIO_free(bio);
213 }
214 }
215
216 2114x if (!cd.certificate_chain.empty())
217 {
218 1x BIO* bio = BIO_new_mem_buf(
219 1x cd.certificate_chain.data(),
220 1x static_cast<int>(cd.certificate_chain.size()));
221 1x if (bio)
222 {
223 X509* entity =
224 1x PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
225 1x if (entity)
226 {
227 1x SSL_CTX_use_certificate(ctx_, entity);
228 1x X509_free(entity);
229 }
230
231 X509* cert;
232 2x while ((cert = PEM_read_bio_X509(
233 2x bio, nullptr, nullptr, nullptr)) != nullptr)
234 {
235 1x SSL_CTX_add_extra_chain_cert(ctx_, cert);
236 }
237 1x ERR_clear_error();
238 1x BIO_free(bio);
239 }
240 }
241
242 2114x if (!cd.private_key.empty())
243 {
244 1059x BIO* bio = BIO_new_mem_buf(
245 1059x cd.private_key.data(), static_cast<int>(cd.private_key.size()));
246 1059x if (bio)
247 {
248 1059x EVP_PKEY* pkey = nullptr;
249 1059x if (cd.private_key_format == tls_file_format::pem)
250 {
251 1059x if (cd.password_callback)
252 pkey = PEM_read_bio_PrivateKey(
253 bio, nullptr, password_callback,
254 const_cast<tls_context_data*>(&cd));
255 else
256 1059x pkey = PEM_read_bio_PrivateKey(
257 bio, nullptr, nullptr, nullptr);
258 }
259 else
260 pkey = d2i_PrivateKey_bio(bio, nullptr);
261 1059x if (pkey)
262 {
263 1059x SSL_CTX_use_PrivateKey(ctx_, pkey);
264 1059x EVP_PKEY_free(pkey);
265 }
266 1059x BIO_free(bio);
267 }
268 }
269
270 2114x X509_STORE* store = SSL_CTX_get_cert_store(ctx_);
271 3181x for (auto const& ca : cd.ca_certificates)
272 {
273 1067x BIO* bio = BIO_new_mem_buf(ca.data(), static_cast<int>(ca.size()));
274 1067x if (bio)
275 {
276 1067x X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
277 1067x if (cert)
278 {
279 1066x X509_STORE_add_cert(store, cert);
280 1066x X509_free(cert);
281 }
282 1067x BIO_free(bio);
283 }
284 }
285
286 2114x SSL_CTX_set_verify_depth(ctx_, cd.verify_depth);
287
288 2114x if (!cd.ciphersuites.empty())
289 {
290 3x SSL_CTX_set_security_level(ctx_, 0);
291 3x SSL_CTX_set_cipher_list(ctx_, cd.ciphersuites.c_str());
292 }
293 }
294
295 4228x ~openssl_native_context() override
296 2114x {
297 2114x if (ctx_)
298 2114x SSL_CTX_free(ctx_);
299 4228x }
300 };
301
302 inline SSL_CTX*
303 2119x get_openssl_context(tls_context_data const& cd)
304 {
305 static char key;
306 4233x auto* p = cd.find(&key, [&] { return new openssl_native_context(cd); });
307 2119x return static_cast<openssl_native_context*>(p)->ctx_;
308 }
309
310 } // namespace detail
311
312 struct openssl_stream::impl
313 {
314 capy::any_stream& s_;
315 tls_context ctx_;
316 SSL* ssl_ = nullptr;
317 BIO* ext_bio_ = nullptr;
318 bool used_ = false;
319
320 std::vector<char> in_buf_;
321 std::vector<char> out_buf_;
322
323 capy::async_mutex io_cm_;
324
325 2119x impl(capy::any_stream& s, tls_context ctx) : s_(s), ctx_(std::move(ctx))
326 {
327 2119x in_buf_.resize(default_buffer_size);
328 2119x out_buf_.resize(default_buffer_size);
329 2119x }
330
331 2119x ~impl()
332 {
333 2119x if (ext_bio_)
334 2119x BIO_free(ext_bio_);
335 2119x if (ssl_)
336 2119x SSL_free(ssl_);
337 2119x }
338
339 20x void reset()
340 {
341 20x if (!ssl_)
342 return;
343
344 // Preserves SSL* and BIO pair, releases session state
345 20x SSL_clear(ssl_);
346
347 // Drain stale data from the external BIO
348 char drain[1024];
349 20x while (BIO_ctrl_pending(ext_bio_) > 0)
350 BIO_read(ext_bio_, drain, sizeof(drain));
351
352 // SSL_clear clears per-session settings; reapply hostname
353 20x auto& cd = detail::get_tls_context_data(ctx_);
354 20x apply_hostname_verification(ssl_, cd.hostname);
355
356 20x used_ = false;
357 }
358
359 78073x capy::task<std::error_code> flush_output()
360 {
361 while (BIO_ctrl_pending(ext_bio_) > 0)
362 {
363 std::size_t got = 0;
364 while (BIO_ctrl_pending(ext_bio_) > 0 && got < out_buf_.size())
365 {
366 int put = static_cast<int>(BIO_ctrl_pending(ext_bio_));
367 put = (std::min)(put, static_cast<int>(out_buf_.size() - got));
368 int r = BIO_read(ext_bio_, out_buf_.data() + got, put);
369 if (r <= 0)
370 break;
371 got += static_cast<std::size_t>(r);
372 }
373 if (got == 0)
374 break;
375
376 {
377 auto [lec] = co_await io_cm_.lock();
378 if (lec)
379 co_return lec;
380 capy::async_mutex::lock_guard io_guard(&io_cm_);
381 auto [ec, n] = co_await capy::write(
382 s_, capy::const_buffer(out_buf_.data(), got));
383 if (ec)
384 co_return ec;
385 }
386 }
387 co_return std::error_code{};
388 156146x }
389
390 4151x capy::task<std::error_code> read_input()
391 {
392 auto [lec] = co_await io_cm_.lock();
393 if (lec)
394 co_return lec;
395 capy::async_mutex::lock_guard io_guard(&io_cm_);
396 auto [ec, n] = co_await s_.read_some(
397 capy::mutable_buffer(in_buf_.data(), in_buf_.size()));
398 if (ec)
399 co_return ec;
400
401 int got = BIO_write(ext_bio_, in_buf_.data(), static_cast<int>(n));
402 if (got < static_cast<int>(n))
403 {
404 co_return make_error_code(std::errc::no_buffer_space);
405 }
406
407 co_return std::error_code{};
408 8302x }
409
410 capy::io_task<std::size_t>
411 73030x do_read_some(capy::mutable_buffer_array<capy::detail::max_iovec_> buffers)
412 {
413 std::error_code ec;
414 std::size_t total_read = 0;
415
416 for (auto& buf : buffers)
417 {
418 char* dest = static_cast<char*>(buf.data());
419 int remaining = static_cast<int>(buf.size());
420
421 while (remaining > 0)
422 {
423 ERR_clear_error();
424 int ret = SSL_read(ssl_, dest, remaining);
425
426 if (ret > 0)
427 {
428 dest += ret;
429 remaining -= ret;
430 total_read += static_cast<std::size_t>(ret);
431
432 if (total_read > 0)
433 co_return {std::error_code{}, total_read};
434 }
435 else
436 {
437 int err = SSL_get_error(ssl_, ret);
438
439 if (err == SSL_ERROR_WANT_WRITE)
440 {
441 ec = co_await flush_output();
442 if (ec)
443 co_return {ec, total_read};
444 }
445 else if (err == SSL_ERROR_WANT_READ)
446 {
447 ec = co_await flush_output();
448 if (ec)
449 co_return {ec, total_read};
450
451 ec = co_await read_input();
452 if (ec)
453 {
454 if (ec == make_error_code(capy::error::eof))
455 {
456 if (SSL_get_shutdown(ssl_) &
457 SSL_RECEIVED_SHUTDOWN)
458 ec = make_error_code(capy::error::eof);
459 else
460 ec = make_error_code(
461 capy::error::stream_truncated);
462 }
463 co_return {ec, total_read};
464 }
465 }
466 else if (err == SSL_ERROR_ZERO_RETURN)
467 {
468 co_return {
469 make_error_code(capy::error::eof), total_read};
470 }
471 else if (err == SSL_ERROR_SYSCALL)
472 {
473 unsigned long ssl_err = ERR_get_error();
474 if (ssl_err == 0)
475 ec = make_error_code(capy::error::stream_truncated);
476 else
477 ec = std::error_code(
478 static_cast<int>(ssl_err),
479 std::system_category());
480 co_return {ec, total_read};
481 }
482 else
483 {
484 unsigned long ssl_err = ERR_get_error();
485 ec = std::error_code(
486 static_cast<int>(ssl_err), std::system_category());
487 co_return {ec, total_read};
488 }
489 }
490 }
491 }
492
493 co_return {std::error_code{}, total_read};
494 146060x }
495
496 capy::io_task<std::size_t>
497 73160x do_write_some(capy::const_buffer_array<capy::detail::max_iovec_> buffers)
498 {
499 std::error_code ec;
500 std::size_t total_written = 0;
501
502 for (auto const& buf : buffers)
503 {
504 char const* src = static_cast<char const*>(buf.data());
505 int remaining = static_cast<int>(buf.size());
506
507 while (remaining > 0)
508 {
509 ERR_clear_error();
510 int ret = SSL_write(ssl_, src, remaining);
511
512 if (ret > 0)
513 {
514 src += ret;
515 remaining -= ret;
516 total_written += static_cast<std::size_t>(ret);
517
518 if (total_written > 0)
519 {
520 ec = co_await flush_output();
521 co_return {ec, total_written};
522 }
523 }
524 else
525 {
526 int err = SSL_get_error(ssl_, ret);
527
528 if (err == SSL_ERROR_WANT_WRITE)
529 {
530 ec = co_await flush_output();
531 if (ec)
532 co_return {ec, total_written};
533 }
534 else if (err == SSL_ERROR_WANT_READ)
535 {
536 ec = co_await flush_output();
537 if (ec)
538 co_return {ec, total_written};
539
540 ec = co_await read_input();
541 if (ec)
542 co_return {ec, total_written};
543 }
544 else
545 {
546 unsigned long ssl_err = ERR_get_error();
547 ec = std::error_code(
548 static_cast<int>(ssl_err), std::system_category());
549 co_return {ec, total_written};
550 }
551 }
552 }
553 }
554
555 co_return {std::error_code{}, total_written};
556 146320x }
557
558 1444x capy::io_task<> do_handshake(int type)
559 {
560 if (used_)
561 reset();
562
563 std::error_code ec;
564
565 while (true)
566 {
567 ERR_clear_error();
568 int ret;
569 if (type == openssl_stream::client)
570 ret = SSL_connect(ssl_);
571 else
572 ret = SSL_accept(ssl_);
573
574 if (ret == 1)
575 {
576 used_ = true;
577 ec = co_await flush_output();
578 co_return {ec};
579 }
580 else
581 {
582 int err = SSL_get_error(ssl_, ret);
583
584 if (err == SSL_ERROR_WANT_WRITE)
585 {
586 ec = co_await flush_output();
587 if (ec)
588 co_return {ec};
589 }
590 else if (err == SSL_ERROR_WANT_READ)
591 {
592 ec = co_await flush_output();
593 if (ec)
594 co_return {ec};
595
596 ec = co_await read_input();
597 if (ec)
598 co_return {ec};
599 }
600 else
601 {
602 unsigned long ssl_err = ERR_get_error();
603 ec = std::error_code(
604 static_cast<int>(ssl_err), std::system_category());
605 co_return {ec};
606 }
607 }
608 }
609 2888x }
610
611 36x capy::io_task<> do_shutdown()
612 {
613 std::error_code ec;
614
615 while (true)
616 {
617 ERR_clear_error();
618 int ret = SSL_shutdown(ssl_);
619
620 if (ret == 1)
621 {
622 ec = co_await flush_output();
623 co_return {ec};
624 }
625 else if (ret == 0)
626 {
627 ec = co_await flush_output();
628 if (ec)
629 co_return {ec};
630
631 ec = co_await read_input();
632 if (ec)
633 {
634 ec = normalize_openssl_shutdown_read_error(ec);
635 co_return {ec};
636 }
637 }
638 else
639 {
640 int err = SSL_get_error(ssl_, ret);
641
642 if (err == SSL_ERROR_WANT_WRITE)
643 {
644 ec = co_await flush_output();
645 if (ec)
646 co_return {ec};
647 }
648 else if (err == SSL_ERROR_WANT_READ)
649 {
650 ec = co_await flush_output();
651 if (ec)
652 co_return {ec};
653
654 ec = co_await read_input();
655 if (ec)
656 {
657 ec = normalize_openssl_shutdown_read_error(ec);
658 co_return {ec};
659 }
660 }
661 else
662 {
663 unsigned long ssl_err = ERR_get_error();
664 if (ssl_err == 0 && err == SSL_ERROR_SYSCALL)
665 {
666 ec = {};
667 }
668 else
669 {
670 ec = std::error_code(
671 static_cast<int>(ssl_err), std::system_category());
672 }
673 co_return {ec};
674 }
675 }
676 }
677 72x }
678
679 2119x std::error_code init_ssl()
680 {
681 2119x auto& cd = detail::get_tls_context_data(ctx_);
682 2119x SSL_CTX* native_ctx = detail::get_openssl_context(cd);
683 2119x if (!native_ctx)
684 {
685 unsigned long err = ERR_get_error();
686 return std::error_code(
687 static_cast<int>(err), std::system_category());
688 }
689
690 2119x ssl_ = SSL_new(native_ctx);
691 2119x if (!ssl_)
692 {
693 unsigned long err = ERR_get_error();
694 return std::error_code(
695 static_cast<int>(err), std::system_category());
696 }
697
698 2119x BIO* int_bio = nullptr;
699 2119x if (!BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0))
700 {
701 unsigned long err = ERR_get_error();
702 SSL_free(ssl_);
703 ssl_ = nullptr;
704 return std::error_code(
705 static_cast<int>(err), std::system_category());
706 }
707
708 2119x SSL_set_bio(ssl_, int_bio, int_bio);
709
710 2119x apply_hostname_verification(ssl_, cd.hostname);
711
712 2119x return {};
713 }
714 };
715
716 openssl_stream::impl*
717 2119x openssl_stream::make_impl(capy::any_stream& stream, tls_context const& ctx)
718 {
719 2119x auto* p = new impl(stream, ctx);
720
721 2119x auto ec = p->init_ssl();
722 2119x if (ec)
723 {
724 delete p;
725 return nullptr;
726 }
727
728 2119x return p;
729 }
730
731 2119x openssl_stream::~openssl_stream()
732 {
733 2119x delete impl_;
734 2119x }
735
736 openssl_stream::openssl_stream(openssl_stream&& other) noexcept
737 : stream_(std::move(other.stream_))
738 , impl_(other.impl_)
739 {
740 other.impl_ = nullptr;
741 }
742
743 openssl_stream&
744 openssl_stream::operator=(openssl_stream&& other) noexcept
745 {
746 if (this != &other)
747 {
748 delete impl_;
749 stream_ = std::move(other.stream_);
750 impl_ = other.impl_;
751 other.impl_ = nullptr;
752 }
753 return *this;
754 }
755
756 capy::io_task<std::size_t>
757 73030x openssl_stream::do_read_some(
758 capy::mutable_buffer_array<capy::detail::max_iovec_> buffers)
759 {
760 co_return co_await impl_->do_read_some(buffers);
761 146060x }
762
763 capy::io_task<std::size_t>
764 73160x openssl_stream::do_write_some(
765 capy::const_buffer_array<capy::detail::max_iovec_> buffers)
766 {
767 co_return co_await impl_->do_write_some(buffers);
768 146320x }
769
770 capy::io_task<>
771 1444x openssl_stream::handshake(handshake_type type)
772 {
773 co_return co_await impl_->do_handshake(type);
774 2888x }
775
776 capy::io_task<>
777 36x openssl_stream::shutdown()
778 {
779 co_return co_await impl_->do_shutdown();
780 72x }
781
782 void
783 16x openssl_stream::reset()
784 {
785 16x impl_->reset();
786 16x }
787
788 std::string_view
789 1x openssl_stream::name() const noexcept
790 {
791 1x return "openssl";
792 }
793
794 } // namespace boost::corosio
795