src/corosio/src/tls/detail/engine_types.hpp
94.7% Lines (18/19)
100.0% List of functions (2/2)
85.3% Branches (29/34)
Functions (2)
Function
Calls
Lines
Branches
Blocks
boost::corosio::detail::is_ip_literal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
:82
13x
100.0%
75.0%
100.0%
boost::corosio::detail::map_fill_error(boost::corosio::detail::engine_op, std::error_code, bool)
:117
1677x
93.8%
86.7%
97.7%
| Line | Branch | TLA | Hits | Source Code |
|---|---|---|---|---|
| 1 | // | |||
| 2 | // Copyright (c) 2026 Steve Gerbino | |||
| 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 | #ifndef SRC_TLS_DETAIL_ENGINE_TYPES_HPP | |||
| 11 | #define SRC_TLS_DETAIL_ENGINE_TYPES_HPP | |||
| 12 | ||||
| 13 | #include <boost/corosio/ipv4_address.hpp> | |||
| 14 | #include <boost/corosio/ipv6_address.hpp> | |||
| 15 | ||||
| 16 | #include <boost/capy/cond.hpp> | |||
| 17 | #include <boost/capy/error.hpp> | |||
| 18 | ||||
| 19 | #include <cstddef> | |||
| 20 | #include <string> | |||
| 21 | #include <system_error> | |||
| 22 | ||||
| 23 | namespace boost::corosio { | |||
| 24 | ||||
| 25 | namespace detail { | |||
| 26 | ||||
| 27 | /** What the TLS engine needs from its driver after a `perform` call. | |||
| 28 | ||||
| 29 | The driver owns the transport; the engine only stages bytes. Each | |||
| 30 | verdict tells the driver which transport action unblocks the | |||
| 31 | engine before the operation can make progress. | |||
| 32 | */ | |||
| 33 | enum class engine_want | |||
| 34 | { | |||
| 35 | /// The operation finished; `ec` and `bytes` carry the outcome. | |||
| 36 | done, | |||
| 37 | ||||
| 38 | /// The engine needs more input bytes before it can retry. | |||
| 39 | input, | |||
| 40 | ||||
| 41 | /// Pending output must reach the peer, then retry the operation. | |||
| 42 | output_then_retry, | |||
| 43 | ||||
| 44 | /// Pending output must reach the peer; the operation finished. | |||
| 45 | output_then_done | |||
| 46 | }; | |||
| 47 | ||||
| 48 | /// Outcome of one synchronous engine step. | |||
| 49 | struct engine_result | |||
| 50 | { | |||
| 51 | /// Required driver action. | |||
| 52 | engine_want want; | |||
| 53 | ||||
| 54 | /// Mapped, final error for `done` verdicts; empty otherwise. | |||
| 55 | std::error_code ec; | |||
| 56 | ||||
| 57 | /// Application bytes transferred by this step. | |||
| 58 | std::size_t bytes; | |||
| 59 | }; | |||
| 60 | ||||
| 61 | /// Operation selector for `engine::perform`. | |||
| 62 | enum class engine_op | |||
| 63 | { | |||
| 64 | handshake_client, | |||
| 65 | handshake_server, | |||
| 66 | read, | |||
| 67 | write, | |||
| 68 | shutdown | |||
| 69 | }; | |||
| 70 | ||||
| 71 | /** Check whether a peer name is an IP literal rather than a DNS name. | |||
| 72 | ||||
| 73 | RFC 6066 excludes IP literals from SNI, and a literal must be matched | |||
| 74 | against a certificate's iPAddress entries rather than its DNS names, | |||
| 75 | so both backends branch on this when applying hostname verification. | |||
| 76 | ||||
| 77 | @param s The peer name. | |||
| 78 | ||||
| 79 | @return `true` when `s` parses as an IPv4 or IPv6 address. | |||
| 80 | */ | |||
| 81 | inline bool | |||
| 82 | 13x | is_ip_literal(std::string const& s) noexcept | ||
| 83 | { | |||
| 84 |
2/2✓ Branch 6 → 7 taken 11 times.
✓ Branch 6 → 12 taken 2 times.
|
24x | return !std::get<0>(make_ipv4_address(s)) || | |
| 85 |
1/2✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 11 times.
|
24x | !std::get<0>(make_ipv6_address(s)); | |
| 86 | } | |||
| 87 | ||||
| 88 | /** Map a transport error observed while filling engine input. | |||
| 89 | ||||
| 90 | What a transport failure means depends on which operation was | |||
| 91 | starved of input and on close_notify visibility, but not on the | |||
| 92 | backend; the whole policy lives once here. | |||
| 93 | ||||
| 94 | For `read` / `write`, a transport end-of-stream after the peer's | |||
| 95 | close_notify is a clean `eof`; without one it is a truncation | |||
| 96 | attack per the TLS contract. Other errors pass through unchanged. | |||
| 97 | ||||
| 98 | For `shutdown`, a member of the eof/reset/aborted/broken_pipe | |||
| 99 | family may reflect the peer's close_notify that a concurrent | |||
| 100 | reader already consumed during the shutdown's flush rather than a | |||
| 101 | genuine truncation; the received-shutdown bit distinguishes a | |||
| 102 | completed close (success) from an unannounced one | |||
| 103 | (`stream_truncated`). `canceled` and errors outside that family | |||
| 104 | pass through unchanged so the received-shutdown race window can | |||
| 105 | never absorb an unrelated fault. | |||
| 106 | ||||
| 107 | Handshake operations pass every error through: no close is clean | |||
| 108 | before the session is established. | |||
| 109 | ||||
| 110 | @param op The operation whose input fill failed. | |||
| 111 | @param ec The transport error. | |||
| 112 | @param received_shutdown Whether the peer's close_notify was seen. | |||
| 113 | ||||
| 114 | @return The mapped error. | |||
| 115 | */ | |||
| 116 | inline std::error_code | |||
| 117 | 1677x | map_fill_error( | ||
| 118 | engine_op op, std::error_code ec, bool received_shutdown) noexcept | |||
| 119 | { | |||
| 120 |
2/2✓ Branch 2 → 3 taken 36 times.
✓ Branch 2 → 31 taken 1641 times.
|
1677x | if (op == engine_op::shutdown) | |
| 121 | { | |||
| 122 |
5/6✓ Branch 4 → 5 taken 36 times.
✗ Branch 4 → 8 not taken.
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 9 taken 28 times.
✓ Branch 10 → 11 taken 8 times.
✓ Branch 10 → 12 taken 28 times.
|
36x | if (!ec || ec == capy::cond::canceled) | |
| 123 | 8x | return ec; | ||
| 124 | ||||
| 125 |
2/2✓ Branch 17 → 18 taken 25 times.
✓ Branch 17 → 25 taken 2 times.
|
55x | if (ec != capy::cond::eof && ec != std::errc::connection_reset && | |
| 126 |
4/4✓ Branch 14 → 15 taken 27 times.
✓ Branch 14 → 25 taken 1 time.
✓ Branch 20 → 21 taken 24 times.
✓ Branch 20 → 25 taken 1 time.
|
79x | ec != std::errc::connection_aborted && | |
| 127 |
3/4✓ Branch 23 → 24 taken 24 times.
✗ Branch 23 → 25 not taken.
✓ Branch 26 → 27 taken 24 times.
✓ Branch 26 → 28 taken 4 times.
|
52x | ec != std::errc::broken_pipe) | |
| 128 | 24x | return ec; | ||
| 129 | ||||
| 130 |
1/2✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 4 times.
|
4x | if (received_shutdown) | |
| 131 | ✗ | return {}; | ||
| 132 | ||||
| 133 | // A peer that closed without a proper close_notify is a | |||
| 134 | // truncated stream. | |||
| 135 | 4x | return make_error_code(capy::error::stream_truncated); | ||
| 136 | } | |||
| 137 | ||||
| 138 |
5/6✓ Branch 31 → 32 taken 1557 times.
✓ Branch 31 → 33 taken 84 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 37 taken 1557 times.
✓ Branch 35 → 36 taken 5 times.
✓ Branch 35 → 37 taken 79 times.
|
1725x | if ((op == engine_op::read || op == engine_op::write) && | |
| 139 |
2/2✓ Branch 38 → 39 taken 5 times.
✓ Branch 38 → 44 taken 1636 times.
|
1725x | ec == capy::cond::eof) | |
| 140 |
2/2✓ Branch 39 → 40 taken 1 time.
✓ Branch 39 → 41 taken 4 times.
|
5x | return make_error_code( | |
| 141 | received_shutdown ? capy::error::eof | |||
| 142 | 5x | : capy::error::stream_truncated); | ||
| 143 | ||||
| 144 | 1636x | return ec; | ||
| 145 | } | |||
| 146 | ||||
| 147 | } // namespace detail | |||
| 148 | ||||
| 149 | } // namespace boost::corosio | |||
| 150 | ||||
| 151 | #endif | |||
| 152 |