src/corosio/src/tls/detail/engine_types.hpp
94.7% Lines (18/0/19)
100.0% List of functions (2/0/2)
Functions (2)
| Line | 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 | 24x | return !std::get<0>(make_ipv4_address(s)) || | |
| 85 | 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 | 1352x | map_fill_error( | |
| 118 | engine_op op, std::error_code ec, bool received_shutdown) noexcept | ||
| 119 | { | ||
| 120 | 1352x | if (op == engine_op::shutdown) | |
| 121 | { | ||
| 122 | 30x | if (!ec || ec == capy::cond::canceled) | |
| 123 | 8x | return ec; | |
| 124 | |||
| 125 | 42x | if (ec != capy::cond::eof && ec != std::errc::connection_reset && | |
| 126 | 60x | ec != std::errc::connection_aborted && | |
| 127 | 40x | ec != std::errc::broken_pipe) | |
| 128 | 18x | return ec; | |
| 129 | |||
| 130 | 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 | 1364x | if ((op == engine_op::read || op == engine_op::write) && | |
| 139 | 1364x | ec == capy::cond::eof) | |
| 140 | 5x | return make_error_code( | |
| 141 | received_shutdown ? capy::error::eof | ||
| 142 | 5x | : capy::error::stream_truncated); | |
| 143 | |||
| 144 | 1317x | return ec; | |
| 145 | } | ||
| 146 | |||
| 147 | } // namespace detail | ||
| 148 | |||
| 149 | } // namespace boost::corosio | ||
| 150 | |||
| 151 | #endif | ||
| 152 |