/home/runner/work/coquic/coquic/src/quic/core.cpp
Line | Count | Source |
1 | | #include "src/quic/core.h" |
2 | | |
3 | | #include <algorithm> |
4 | | #include <array> |
5 | | #include <cassert> |
6 | | #include <cstdio> |
7 | | #include <cstdlib> |
8 | | #include <cstring> |
9 | | #if !defined(COQUIC_WASM_NO_FILESYSTEM) |
10 | | #include <fstream> |
11 | | #endif |
12 | | #include <iomanip> |
13 | | #include <iostream> |
14 | | #include <limits> |
15 | | #include <optional> |
16 | | #include <random> |
17 | | #include <ranges> |
18 | | #include <sstream> |
19 | | #include <utility> |
20 | | |
21 | | #include <openssl/crypto.h> |
22 | | #include <openssl/evp.h> |
23 | | #include <openssl/hmac.h> |
24 | | #include <openssl/rand.h> |
25 | | |
26 | | #include "src/quic/codec/buffer.h" |
27 | | #include "src/quic/codec/protected_codec.h" |
28 | | #include "src/quic/connection/connection.h" |
29 | | #include "src/quic/crypto/packet_crypto.h" |
30 | | #include "src/quic/object_cache.h" |
31 | | #include "src/quic/transport/streams.h" |
32 | | |
33 | | #if defined(__clang__) |
34 | | #define COQUIC_NO_PROFILE __attribute__((no_profile_instrument_function)) |
35 | | #else |
36 | | #define COQUIC_NO_PROFILE |
37 | | #endif |
38 | | |
39 | | namespace coquic::quic { |
40 | | |
41 | | namespace { |
42 | | |
43 | | constexpr std::size_t kPmtudIPv6EthernetUdpPayloadSize = 1452; |
44 | | constexpr std::size_t kPmtudIPv4EthernetUdpPayloadSize = 1472; |
45 | | |
46 | | template <typename... Ts> struct overloaded : Ts... { |
47 | | using Ts::operator()...; |
48 | | }; |
49 | | |
50 | | template <typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; |
51 | | |
52 | | constexpr auto kStreamStateErrorMap = std::to_array<QuicCoreLocalErrorCode>({ |
53 | | QuicCoreLocalErrorCode::invalid_stream_id, |
54 | | QuicCoreLocalErrorCode::invalid_stream_direction, |
55 | | QuicCoreLocalErrorCode::send_side_closed, |
56 | | QuicCoreLocalErrorCode::receive_side_closed, |
57 | | QuicCoreLocalErrorCode::flow_control_violation, |
58 | | QuicCoreLocalErrorCode::final_size_conflict, |
59 | | }); |
60 | | |
61 | | struct CoreTestFaultState { |
62 | | bool force_address_validation_token_tag_failure = false; |
63 | | bool force_stateless_reset_token_derivation_failure = false; |
64 | | bool force_endpoint_connection_id_rand_failure = false; |
65 | | bool force_fill_random_bytes_rand_failure = false; |
66 | | }; |
67 | | |
68 | 1.00k | CoreTestFaultState &core_test_fault_state() { |
69 | 1.00k | static auto state = CoreTestFaultState{}; |
70 | 1.00k | return state; |
71 | 1.00k | } |
72 | | |
73 | | class ScopedCoreTestFault { |
74 | | public: |
75 | 0 | explicit ScopedCoreTestFault(bool &target) : target_(target), previous_(target) { |
76 | 0 | target_ = true; |
77 | 0 | } |
78 | | |
79 | 0 | ~ScopedCoreTestFault() { |
80 | 0 | target_ = previous_; |
81 | 0 | } |
82 | | |
83 | | ScopedCoreTestFault(const ScopedCoreTestFault &) = delete; |
84 | | ScopedCoreTestFault &operator=(const ScopedCoreTestFault &) = delete; |
85 | | |
86 | | private: |
87 | | bool &target_; |
88 | | bool previous_ = false; |
89 | | }; |
90 | | constexpr std::size_t kMinimumClientInitialDatagramBytes = 1200; |
91 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.2 |
92 | | // # An endpoint that uses this design MUST either use the same connection ID |
93 | | // # length for all connections or encode the length of the connection ID such |
94 | | // # that it can be recovered without state. |
95 | | constexpr std::size_t kEndpointConnectionIdLength = 8; |
96 | | constexpr std::size_t kMaxDatagramsPerDrain = 256; |
97 | | constexpr QuicPathId kDefaultPathId = 0; |
98 | | constexpr QuicCoreDuration kRetryTokenLifetime{10000000}; |
99 | | constexpr QuicCoreDuration kNewTokenLifetime{86400000000}; |
100 | | constexpr std::size_t kStatelessResetTokenLength = 16; |
101 | | constexpr std::size_t kMinimumStatelessResetDatagramSize = 21; |
102 | | constexpr std::size_t kAddressValidationTokenTagLength = 16; |
103 | | constexpr std::byte kAddressValidationRetryTokenType{0x52}; |
104 | | constexpr std::byte kAddressValidationNewTokenType{0x4e}; |
105 | | constexpr std::uint32_t kGreasedReservedVersion = 0x0a0a0a0a; |
106 | | constexpr std::byte kServerConnectionIdPrefix{0x53}; |
107 | | constexpr std::size_t kCoreEffectStorageCacheMaxBytes = std::size_t{64} * 1024; |
108 | | constexpr std::size_t kCoreEffectStorageCacheBucketBytes = std::size_t{4} * 1024; |
109 | | constexpr std::size_t kCoreEffectStorageCacheSlots = 128; |
110 | | |
111 | | enum class AddressValidationTokenKind : std::uint8_t { |
112 | | unknown, |
113 | | retry, |
114 | | new_token, |
115 | | }; |
116 | | |
117 | | static_assert(kStreamStateErrorMap.size() == |
118 | | static_cast<std::size_t>(StreamStateErrorCode::final_size_conflict) + 1); |
119 | | |
120 | | struct CoreProfileCounters { |
121 | | std::uint64_t drain_calls = 0; |
122 | | std::uint64_t drain_datagrams = 0; |
123 | | std::uint64_t drain_send_effects = 0; |
124 | | std::uint64_t drain_ns = 0; |
125 | | std::uint64_t drain_emplace_send_ns = 0; |
126 | | std::uint64_t append_result_calls = 0; |
127 | | std::uint64_t append_result_effects = 0; |
128 | | std::uint64_t append_result_ns = 0; |
129 | | }; |
130 | | |
131 | | constexpr bool kCoquicProfileHooksEnabled = COQUIC_PROFILE_HOOKS != 0; |
132 | | |
133 | | COQUIC_NO_PROFILE bool core_profile_enabled() { |
134 | | if constexpr (!kCoquicProfileHooksEnabled) { |
135 | | return false; |
136 | | } |
137 | | |
138 | 8 | static const bool enabled = [] { |
139 | 8 | const char *value = std::getenv("COQUIC_SEND_PROFILE"); |
140 | 8 | return value != nullptr && value[0] != '\0' && std::string_view(value) != "0"; |
141 | 8 | }(); |
142 | | return enabled; |
143 | | } |
144 | | |
145 | | COQUIC_NO_PROFILE CoreProfileCounters &core_profile_counters() { |
146 | | static CoreProfileCounters counters; |
147 | | return counters; |
148 | | } |
149 | | |
150 | | COQUIC_NO_PROFILE void print_core_profile() { |
151 | | if (!core_profile_enabled()) { |
152 | | return; |
153 | | } |
154 | | |
155 | | const auto &c = core_profile_counters(); |
156 | | std::cerr << "coquic-core-profile" << " drain_calls=" << c.drain_calls |
157 | | << " drain_datagrams=" << c.drain_datagrams |
158 | | << " drain_send_effects=" << c.drain_send_effects << " drain_ns=" << c.drain_ns |
159 | | << " drain_emplace_send_ns=" << c.drain_emplace_send_ns |
160 | | << " append_result_calls=" << c.append_result_calls |
161 | | << " append_result_effects=" << c.append_result_effects |
162 | | << " append_result_ns=" << c.append_result_ns << '\n'; |
163 | | } |
164 | | |
165 | | COQUIC_NO_PROFILE void register_core_profile_printer_once() { |
166 | | if constexpr (!kCoquicProfileHooksEnabled) { |
167 | | return; |
168 | | } |
169 | | |
170 | 8 | static const bool registered = [] { |
171 | 8 | std::atexit(print_core_profile); |
172 | 8 | return true; |
173 | 8 | }(); |
174 | | static_cast<void>(registered); |
175 | | } |
176 | | |
177 | | struct CoreProfileTimer { |
178 | | std::uint64_t *target = nullptr; |
179 | | QuicCoreTimePoint start{}; |
180 | | |
181 | | COQUIC_NO_PROFILE explicit CoreProfileTimer(std::uint64_t &counter) |
182 | | : target(kCoquicProfileHooksEnabled && core_profile_enabled() ? &counter : nullptr) { |
183 | | if (target != nullptr) { |
184 | | start = QuicCoreClock::now(); |
185 | | } |
186 | | } |
187 | | |
188 | | COQUIC_NO_PROFILE ~CoreProfileTimer() { |
189 | | if (target == nullptr) { |
190 | | return; |
191 | | } |
192 | | *target += static_cast<std::uint64_t>( |
193 | | std::chrono::duration_cast<std::chrono::nanoseconds>(QuicCoreClock::now() - start) |
194 | | .count()); |
195 | | } |
196 | | }; |
197 | | |
198 | | #if COQUIC_PROFILE_HOOKS |
199 | | #define COQUIC_CORE_PROFILE_TIMER(name, counter) \ |
200 | 73.4k | CoreProfileTimer name(core_profile_counters().counter) |
201 | | #else |
202 | | #define COQUIC_CORE_PROFILE_TIMER(name, counter) static_cast<void>(0) |
203 | | #endif |
204 | | |
205 | | #if !defined(COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE) |
206 | | #if defined(__wasm__) |
207 | | #define COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE 1 |
208 | | #else |
209 | | #define COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE 0 |
210 | | #endif |
211 | | #endif |
212 | | |
213 | | COQUIC_NO_PROFILE std::size_t core_effect_storage_allocation_bytes(std::size_t byte_count) { |
214 | | #if COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE != 0 |
215 | | return byte_count; |
216 | | #else |
217 | | if (byte_count == 0 || byte_count > kCoreEffectStorageCacheMaxBytes) { |
218 | | return byte_count; |
219 | | } |
220 | | |
221 | | return ((byte_count + kCoreEffectStorageCacheBucketBytes - 1) / |
222 | | kCoreEffectStorageCacheBucketBytes) * |
223 | | kCoreEffectStorageCacheBucketBytes; |
224 | | #endif |
225 | | } |
226 | | |
227 | | #if COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE == 0 |
228 | | using CoreEffectStorageCache = detail::FixedAlignedBlockCache<kCoreEffectStorageCacheSlots>; |
229 | | |
230 | | COQUIC_NO_PROFILE CoreEffectStorageCache &core_effect_storage_cache() { |
231 | | thread_local CoreEffectStorageCache storage_cache; |
232 | | return storage_cache; |
233 | | } |
234 | | #endif |
235 | | |
236 | | COQUIC_NO_PROFILE bool has_send_continuation(std::size_t emitted, |
237 | | bool last_drained_allows_send_continuation, |
238 | | const QuicConnection &quic_connection, |
239 | | QuicCoreTimePoint now) { |
240 | | static_cast<void>(quic_connection); |
241 | | static_cast<void>(now); |
242 | | return emitted == kMaxDatagramsPerDrain && last_drained_allows_send_continuation; |
243 | | } |
244 | | |
245 | | COQUIC_NO_PROFILE bool wakeup_not_due(const std::optional<QuicCoreTimePoint> &wakeup, |
246 | 0 | QuicCoreTimePoint now) { |
247 | 0 | return !wakeup.has_value() || *wakeup > now; |
248 | 0 | } |
249 | | |
250 | | COQUIC_NO_PROFILE void merge_send_continuation_pending(QuicCoreResult &target, |
251 | | const QuicCoreResult &source) { |
252 | | target.send_continuation_pending = |
253 | | target.send_continuation_pending || source.send_continuation_pending; |
254 | | } |
255 | | |
256 | | COQUIC_NO_PROFILE bool has_route_handle(const std::optional<QuicRouteHandle> &route_handle) { |
257 | | return route_handle.has_value(); |
258 | | } |
259 | | |
260 | | template <typename T> |
261 | | COQUIC_NO_PROFILE const T &optional_ref_or_abort(const std::optional<T> &value) { |
262 | | if (!value.has_value()) { |
263 | | std::abort(); |
264 | | } |
265 | | return value.value(); |
266 | | } |
267 | | |
268 | | template <typename Entry> COQUIC_NO_PROFILE bool has_legacy_entry(Entry *entry) { |
269 | | return entry != nullptr; |
270 | | } |
271 | | |
272 | | template <typename Entry> |
273 | | COQUIC_NO_PROFILE void |
274 | | maybe_note_legacy_send_continuation(Entry *entry, const QuicCoreResult &source_result, |
275 | | QuicCoreTimePoint now, const auto ¬e) { |
276 | | if (has_legacy_entry(entry)) { |
277 | | note(*entry, source_result, now); |
278 | | } |
279 | | } |
280 | | |
281 | | COQUIC_NO_PROFILE void |
282 | | clamp_result_wakeup_to_now_if_continuation_pending(QuicCoreResult &core_result, |
283 | | QuicCoreTimePoint now) { |
284 | | if (core_result.send_continuation_pending) { |
285 | | core_result.next_wakeup = std::min(core_result.next_wakeup.value_or(now), now); |
286 | | } |
287 | | } |
288 | | |
289 | 172 | QuicCoreLocalError stream_state_error_to_local_error(const StreamStateError &error) { |
290 | 172 | return QuicCoreLocalError{ |
291 | 172 | .connection = std::nullopt, |
292 | 172 | .code = kStreamStateErrorMap[static_cast<std::size_t>(error.code)], |
293 | 172 | .stream_id = error.stream_id, |
294 | 172 | }; |
295 | 172 | } |
296 | | |
297 | 56 | QuicCoreLocalError datagram_send_error_to_local_error(const CodecError &error) { |
298 | 56 | switch (error.code) { |
299 | 40 | case CodecErrorCode::invalid_packet_protection_state: |
300 | 40 | return QuicCoreLocalError{ |
301 | 40 | .connection = std::nullopt, |
302 | 40 | .code = QuicCoreLocalErrorCode::datagram_not_supported, |
303 | 40 | .stream_id = std::nullopt, |
304 | 40 | }; |
305 | 16 | case CodecErrorCode::packet_length_mismatch: |
306 | 16 | return QuicCoreLocalError{ |
307 | 16 | .connection = std::nullopt, |
308 | 16 | .code = QuicCoreLocalErrorCode::datagram_too_large, |
309 | 16 | .stream_id = std::nullopt, |
310 | 16 | }; |
311 | 0 | default: |
312 | 0 | return QuicCoreLocalError{ |
313 | 0 | .connection = std::nullopt, |
314 | 0 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
315 | 0 | .stream_id = std::nullopt, |
316 | 0 | }; |
317 | 56 | } |
318 | 56 | } |
319 | | |
320 | | COQUIC_NO_PROFILE void emit_send_datagram(QuicCoreResult &result, QuicCoreSendDatagram datagram, |
321 | | QuicCoreSendDatagramSink *send_sink) { |
322 | | if (send_sink == nullptr) { |
323 | | result.effects.emplace_back(std::move(datagram)); |
324 | | return; |
325 | | } |
326 | | if (!send_sink->on_send_datagram(std::move(datagram))) { |
327 | | result.send_sink_failed = true; |
328 | | } |
329 | | } |
330 | | |
331 | | COQUIC_NO_PROFILE QuicCoreResult take_connection_non_send_effects( |
332 | | QuicConnectionHandle connection_handle, QuicConnection &quic_connection) { |
333 | | QuicCoreResult result; |
334 | | while (auto received = quic_connection.take_received_stream_data()) { |
335 | | received->connection = connection_handle; |
336 | | result.effects.emplace_back(std::move(*received)); |
337 | | } |
338 | | while (auto received = quic_connection.take_received_datagram_data()) { |
339 | | received->connection = connection_handle; |
340 | | result.effects.emplace_back(std::move(*received)); |
341 | | } |
342 | | while (const auto reset = quic_connection.take_peer_reset_stream()) { |
343 | | result.effects.emplace_back(QuicCorePeerResetStream{ |
344 | | .connection = connection_handle, |
345 | | .stream_id = reset->stream_id, |
346 | | .application_error_code = reset->application_error_code, |
347 | | .final_size = reset->final_size, |
348 | | }); |
349 | | } |
350 | | while (const auto stop = quic_connection.take_peer_stop_sending()) { |
351 | | result.effects.emplace_back(QuicCorePeerStopSending{ |
352 | | .connection = connection_handle, |
353 | | .stream_id = stop->stream_id, |
354 | | .application_error_code = stop->application_error_code, |
355 | | }); |
356 | | } |
357 | | while (const auto event = quic_connection.take_state_change()) { |
358 | | result.effects.emplace_back(QuicCoreStateEvent{ |
359 | | .connection = connection_handle, |
360 | | .change = *event, |
361 | | }); |
362 | | } |
363 | | while (const auto preferred = quic_connection.take_peer_preferred_address_available()) { |
364 | | result.effects.emplace_back(QuicCorePeerPreferredAddressAvailable{ |
365 | | .connection = connection_handle, |
366 | | .preferred_address = preferred->preferred_address, |
367 | | }); |
368 | | } |
369 | | while (const auto state = quic_connection.take_resumption_state_available()) { |
370 | | result.effects.emplace_back(QuicCoreResumptionStateAvailable{ |
371 | | .connection = connection_handle, |
372 | | .state = state->state, |
373 | | }); |
374 | | } |
375 | | while (const auto status = quic_connection.take_zero_rtt_status_event()) { |
376 | | result.effects.emplace_back(QuicCoreZeroRttStatusEvent{ |
377 | | .connection = connection_handle, |
378 | | .status = status->status, |
379 | | }); |
380 | | } |
381 | | while (auto new_token = quic_connection.take_new_token()) { |
382 | | result.effects.emplace_back(QuicCoreNewTokenAvailable{ |
383 | | .connection = connection_handle, |
384 | | .token = std::move(*new_token), |
385 | | }); |
386 | | } |
387 | | while (auto inspection = quic_connection.take_packet_inspection()) { |
388 | | inspection->connection = connection_handle; |
389 | | result.effects.emplace_back(std::move(*inspection)); |
390 | | } |
391 | | if (const auto terminal = quic_connection.take_terminal_state()) { |
392 | | if (*terminal == QuicConnectionTerminalState::closed) { |
393 | | result.effects.emplace_back(QuicCoreConnectionLifecycleEvent{ |
394 | | .connection = connection_handle, |
395 | | .event = QuicCoreConnectionLifecycle::closed, |
396 | | }); |
397 | | } |
398 | | } |
399 | | return result; |
400 | | } |
401 | | |
402 | | COQUIC_NO_PROFILE QuicCoreResult drain_connection_effects( |
403 | | QuicConnectionHandle connection_handle, |
404 | | const std::optional<QuicRouteHandle> &default_route_handle, |
405 | | const std::unordered_map<QuicPathId, QuicRouteHandle> &route_handle_by_path_id, |
406 | | QuicConnection &quic_connection, QuicCoreTimePoint now, bool continue_paced_burst = false, |
407 | | QuicCoreSendDatagramSink *send_sink = nullptr) { |
408 | | register_core_profile_printer_once(); |
409 | | if (core_profile_enabled()) { |
410 | | ++core_profile_counters().drain_calls; |
411 | | } |
412 | | COQUIC_CORE_PROFILE_TIMER(core_drain_timer, drain_ns); |
413 | | QuicCoreResult drain_result; |
414 | | |
415 | | // Drain send-side datagrams first, because each datagram can change pacing state and route |
416 | | // metadata. After that, collect every queued application/control effect so callers can make one |
417 | | // routing/removal decision from a complete connection snapshot. |
418 | | std::size_t emitted = 0; |
419 | | bool last_drained_allows_send_continuation = false; |
420 | | if (send_sink != nullptr) { |
421 | | class FastBulkCoreSink final : public QuicConnectionDrainedDatagramSink { |
422 | | public: |
423 | | FastBulkCoreSink(QuicCoreResult &result, QuicCoreSendDatagramSink &sink, |
424 | | QuicConnectionHandle connection, |
425 | | const std::optional<QuicRouteHandle> &default_route, |
426 | | const std::unordered_map<QuicPathId, QuicRouteHandle> &routes) |
427 | 8 | : result_(result), sink_(sink), connection_(connection), |
428 | 8 | default_route_(default_route), routes_(routes) { |
429 | 8 | } |
430 | | |
431 | 0 | bool on_connection_datagram(QuicConnectionDrainedDatagram datagram) override { |
432 | 0 | const auto route_it = |
433 | 0 | datagram.path_id.has_value() ? routes_.find(*datagram.path_id) : routes_.end(); |
434 | 0 | if (route_it != routes_.end()) { |
435 | 0 | if (!sink_.on_send_datagram_payload( |
436 | 0 | connection_, route_it->second, std::move(datagram.bytes), datagram.ecn, |
437 | 0 | datagram.is_pmtu_probe, datagram.packet_inspection_datagram_id)) { |
438 | 0 | result_.send_sink_failed = true; |
439 | 0 | return false; |
440 | 0 | } |
441 | 0 | return true; |
442 | 0 | } |
443 | 0 | if (default_route_.has_value()) { |
444 | 0 | if (!sink_.on_send_datagram_payload( |
445 | 0 | connection_, *default_route_, std::move(datagram.bytes), datagram.ecn, |
446 | 0 | datagram.is_pmtu_probe, datagram.packet_inspection_datagram_id)) { |
447 | 0 | result_.send_sink_failed = true; |
448 | 0 | return false; |
449 | 0 | } |
450 | 0 | return true; |
451 | 0 | } |
452 | 0 | auto send_datagram = QuicCoreSendDatagram{ |
453 | 0 | .connection = connection_, |
454 | 0 | .route_handle = std::nullopt, |
455 | 0 | .bytes = std::move(datagram.bytes), |
456 | 0 | .ecn = datagram.ecn, |
457 | 0 | .is_pmtu_probe = datagram.is_pmtu_probe, |
458 | 0 | .packet_inspection_datagram_id = datagram.packet_inspection_datagram_id, |
459 | 0 | }; |
460 | 0 | if (!sink_.on_send_datagram(std::move(send_datagram))) { |
461 | 0 | result_.send_sink_failed = true; |
462 | 0 | return false; |
463 | 0 | } |
464 | 0 | return true; |
465 | 0 | } |
466 | | |
467 | | private: |
468 | | QuicCoreResult &result_; |
469 | | QuicCoreSendDatagramSink &sink_; |
470 | | QuicConnectionHandle connection_ = 0; |
471 | | const std::optional<QuicRouteHandle> &default_route_; |
472 | | const std::unordered_map<QuicPathId, QuicRouteHandle> &routes_; |
473 | | }; |
474 | | |
475 | | FastBulkCoreSink fast_sink{drain_result, *send_sink, connection_handle, |
476 | | default_route_handle, route_handle_by_path_id}; |
477 | | emitted = quic_connection.drain_fast_bulk_stream_datagrams( |
478 | | now, continue_paced_burst, kMaxDatagramsPerDrain, fast_sink); |
479 | | if (emitted != 0) { |
480 | | last_drained_allows_send_continuation = |
481 | | quic_connection.last_drained_allows_send_continuation(); |
482 | | continue_paced_burst = last_drained_allows_send_continuation; |
483 | | if (core_profile_enabled()) { |
484 | | core_profile_counters().drain_send_effects += emitted; |
485 | | } |
486 | | } |
487 | | } |
488 | | for (; emitted < kMaxDatagramsPerDrain; ++emitted) { |
489 | | if (drain_result.send_sink_failed) { |
490 | | break; |
491 | | } |
492 | | if (!continue_paced_burst && |
493 | | !quic_connection.has_sendable_datagram(now, continue_paced_burst)) { |
494 | | break; |
495 | | } |
496 | | auto datagram = quic_connection.drain_outbound_datagram(now, continue_paced_burst); |
497 | | if (datagram.empty()) { |
498 | | break; |
499 | | } |
500 | | last_drained_allows_send_continuation = |
501 | | quic_connection.last_drained_allows_send_continuation(); |
502 | | continue_paced_burst = last_drained_allows_send_continuation; |
503 | | if (emitted == 0 && last_drained_allows_send_continuation && |
504 | | quic_connection.has_sendable_datagram(now, /*continue_paced_burst=*/true)) { |
505 | | drain_result.effects.reserve(kMaxDatagramsPerDrain); |
506 | | } |
507 | | |
508 | | const auto drained_path_id = quic_connection.last_drained_path_id(); |
509 | | const auto route_it = drained_path_id.has_value() |
510 | | ? route_handle_by_path_id.find(*drained_path_id) |
511 | | : route_handle_by_path_id.end(); |
512 | | auto send_datagram = QuicCoreSendDatagram{ |
513 | | .connection = connection_handle, |
514 | | .route_handle = route_it != route_handle_by_path_id.end() |
515 | | ? std::optional<QuicRouteHandle>(route_it->second) |
516 | | : default_route_handle, |
517 | | .bytes = std::move(datagram), |
518 | | .ecn = quic_connection.last_drained_ecn_codepoint(), |
519 | | .is_pmtu_probe = quic_connection.last_drained_is_pmtu_probe(), |
520 | | .packet_inspection_datagram_id = |
521 | | quic_connection.last_drained_packet_inspection_datagram_id(), |
522 | | }; |
523 | | { |
524 | | COQUIC_CORE_PROFILE_TIMER(core_emplace_timer, drain_emplace_send_ns); |
525 | | emit_send_datagram(drain_result, std::move(send_datagram), send_sink); |
526 | | if (drain_result.send_sink_failed) { |
527 | | ++emitted; |
528 | | break; |
529 | | } |
530 | | } |
531 | | if (core_profile_enabled()) { |
532 | | ++core_profile_counters().drain_send_effects; |
533 | | } |
534 | | } |
535 | | if (core_profile_enabled()) { |
536 | | core_profile_counters().drain_datagrams += emitted; |
537 | | } |
538 | | if (has_send_continuation(emitted, last_drained_allows_send_continuation, quic_connection, |
539 | | now)) { |
540 | | drain_result.next_wakeup = now; |
541 | | drain_result.send_continuation_pending = true; |
542 | | } |
543 | | |
544 | | auto non_send_effects = take_connection_non_send_effects(connection_handle, quic_connection); |
545 | | if (drain_result.effects.empty()) { |
546 | | drain_result.effects = std::move(non_send_effects.effects); |
547 | | } else if (!non_send_effects.effects.empty()) { |
548 | | drain_result.effects.reserve(drain_result.effects.size() + non_send_effects.effects.size()); |
549 | | drain_result.effects.insert(drain_result.effects.end(), |
550 | | std::make_move_iterator(non_send_effects.effects.begin()), |
551 | | std::make_move_iterator(non_send_effects.effects.end())); |
552 | | } |
553 | | |
554 | | if (!drain_result.send_continuation_pending) { |
555 | | drain_result.next_wakeup = quic_connection.next_wakeup(); |
556 | | } |
557 | | return drain_result; |
558 | | } |
559 | | |
560 | | COQUIC_NO_PROFILE StreamStateResult<bool> |
561 | | queue_legacy_local_command(QuicConnection &quic_connection, const QuicCoreSendStreamData &input) { |
562 | | return quic_connection.queue_stream_send(input.stream_id, input.bytes, input.fin, |
563 | | input.priority); |
564 | | } |
565 | | |
566 | | COQUIC_NO_PROFILE StreamStateResult<bool> |
567 | | queue_legacy_local_command(QuicConnection &quic_connection, |
568 | | const QuicCoreSendSharedStreamData &input) { |
569 | | return quic_connection.queue_stream_send_shared(input.stream_id, input.bytes, input.fin, |
570 | | input.priority); |
571 | | } |
572 | | |
573 | | COQUIC_NO_PROFILE CodecResult<bool> |
574 | | queue_legacy_local_command(QuicConnection &quic_connection, const QuicCoreSendDatagramData &input) { |
575 | | return quic_connection.queue_datagram_send(input.bytes, input.priority); |
576 | | } |
577 | | |
578 | | COQUIC_NO_PROFILE CodecResult<bool> |
579 | | queue_legacy_local_command(QuicConnection &quic_connection, |
580 | | const QuicCoreSendSharedDatagramData &input) { |
581 | | return quic_connection.queue_datagram_send_shared(input.bytes, input.priority); |
582 | | } |
583 | | |
584 | | COQUIC_NO_PROFILE bool legacy_stream_send_batchable(const QuicCoreInput &input) { |
585 | | return std::holds_alternative<QuicCoreSendStreamData>(input) || |
586 | | std::holds_alternative<QuicCoreSendSharedStreamData>(input); |
587 | | } |
588 | | |
589 | | COQUIC_NO_PROFILE void append_sequential_result(QuicCoreResult &target, QuicCoreResult source) { |
590 | | if (target.effects.empty()) { |
591 | | target.effects = std::move(source.effects); |
592 | | } else if (!source.effects.empty()) { |
593 | | target.effects.reserve(target.effects.size() + source.effects.size()); |
594 | | target.effects.insert(target.effects.end(), std::make_move_iterator(source.effects.begin()), |
595 | | std::make_move_iterator(source.effects.end())); |
596 | | } |
597 | | target.next_wakeup = source.next_wakeup; |
598 | | target.send_continuation_pending = source.send_continuation_pending; |
599 | | target.send_sink_failed = target.send_sink_failed || source.send_sink_failed; |
600 | | if (!target.local_error.has_value() && source.local_error.has_value()) { |
601 | | target.local_error = source.local_error; |
602 | | } |
603 | | } |
604 | | |
605 | 73.4k | void append_result(QuicCoreResult &target, QuicCoreResult source) { |
606 | 73.4k | if (core_profile_enabled()) { |
607 | 18.3k | auto &profile = core_profile_counters(); |
608 | 18.3k | ++profile.append_result_calls; |
609 | 18.3k | profile.append_result_effects += source.effects.size(); |
610 | 18.3k | } |
611 | 73.4k | COQUIC_CORE_PROFILE_TIMER(append_result_timer, append_result_ns); |
612 | 73.4k | if (target.effects.empty()) { |
613 | 73.2k | target.effects = std::move(source.effects); |
614 | 73.2k | } else if (!source.effects.empty()) { |
615 | 140 | target.effects.reserve(target.effects.size() + source.effects.size()); |
616 | 140 | target.effects.insert(target.effects.end(), std::make_move_iterator(source.effects.begin()), |
617 | 140 | std::make_move_iterator(source.effects.end())); |
618 | 140 | } |
619 | 73.4k | merge_send_continuation_pending(target, source); |
620 | 73.4k | target.send_sink_failed = target.send_sink_failed || source.send_sink_failed; |
621 | 73.4k | if (source.next_wakeup.has_value()) { |
622 | 70.4k | target.next_wakeup = |
623 | 70.4k | std::min(target.next_wakeup.value_or(*source.next_wakeup), *source.next_wakeup); |
624 | 70.4k | } |
625 | 73.4k | if (!target.local_error.has_value() && source.local_error.has_value()) { |
626 | 0 | target.local_error = source.local_error; |
627 | 0 | } |
628 | 73.4k | } |
629 | | |
630 | 16.9k | bool has_closed_lifecycle_event(const QuicCoreResult &core_result) { |
631 | 16.9k | return std::any_of( |
632 | 26.5k | core_result.effects.begin(), core_result.effects.end(), [](const auto &effect) { |
633 | 26.5k | const auto *event = std::get_if<QuicCoreConnectionLifecycleEvent>(&effect); |
634 | 26.5k | return event != nullptr && event->event == QuicCoreConnectionLifecycle::closed; |
635 | 26.5k | }); |
636 | 16.9k | } |
637 | | |
638 | | COQUIC_NO_PROFILE bool |
639 | | should_remove_endpoint_connection_entry(const QuicConnection &quic_connection, |
640 | | const QuicCoreResult &drained_result, |
641 | | QuicCoreTimePoint now) { |
642 | | if (quic_connection.has_failed()) { |
643 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.2 |
644 | | // # Servers that retain an open socket for accepting new connections |
645 | | // # SHOULD NOT end the closing or draining state early. |
646 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.2 |
647 | | // # Once its closing or draining state ends, an endpoint SHOULD |
648 | | // # discard all connection state. |
649 | | return !quic_connection.close_state_active() || quic_connection.terminal_state_expired(now); |
650 | | } |
651 | | return has_closed_lifecycle_event(drained_result); |
652 | | } |
653 | | |
654 | | bool should_keep_endpoint_connection_entry(const QuicConnection &quic_connection, |
655 | | const QuicCoreResult &drained_result, |
656 | 228 | QuicCoreTimePoint now = QuicCoreTimePoint{}) { |
657 | 228 | const bool failed_before_handshake = |
658 | 228 | quic_connection.has_failed() && !quic_connection.has_processed_peer_packet(); |
659 | 228 | return !failed_before_handshake && |
660 | 228 | !should_remove_endpoint_connection_entry(quic_connection, drained_result, now); |
661 | 228 | } |
662 | | |
663 | 24 | bool is_reserved_version(std::uint32_t version) { |
664 | 24 | return (version & 0x0f0f0f0fu) == 0x0a0a0a0au; |
665 | 24 | } |
666 | | |
667 | 384 | void append_u16_be(std::vector<std::byte> &encoded_bytes, std::uint16_t value) { |
668 | 384 | encoded_bytes.push_back(static_cast<std::byte>((value >> 8) & 0xffu)); |
669 | 384 | encoded_bytes.push_back(static_cast<std::byte>(value & 0xffu)); |
670 | 384 | } |
671 | | |
672 | 136 | void append_u32_be(std::vector<std::byte> &encoded_bytes, std::uint32_t value) { |
673 | 136 | encoded_bytes.push_back(static_cast<std::byte>((value >> 24) & 0xffu)); |
674 | 136 | encoded_bytes.push_back(static_cast<std::byte>((value >> 16) & 0xffu)); |
675 | 136 | encoded_bytes.push_back(static_cast<std::byte>((value >> 8) & 0xffu)); |
676 | 136 | encoded_bytes.push_back(static_cast<std::byte>(value & 0xffu)); |
677 | 136 | } |
678 | | |
679 | 192 | void append_u64_be(std::vector<std::byte> &encoded_bytes, std::uint64_t value) { |
680 | 1.72k | for (int shift = 56; shift >= 0; shift -= 8) { |
681 | 1.53k | encoded_bytes.push_back( |
682 | 1.53k | static_cast<std::byte>((value >> static_cast<unsigned>(shift)) & 0xffu)); |
683 | 1.53k | } |
684 | 192 | } |
685 | | |
686 | | COQUIC_NO_PROFILE std::optional<std::array<unsigned char, EVP_MAX_MD_SIZE>> |
687 | | compute_hmac_sha256_for_core(std::span<const std::byte> secret, |
688 | | std::span<const unsigned char> input, unsigned int &produced, |
689 | | bool force_failure) { |
690 | | if (force_failure) { |
691 | | return std::nullopt; |
692 | | } |
693 | | |
694 | | std::array<unsigned char, EVP_MAX_MD_SIZE> digest{}; |
695 | | if (HMAC(EVP_sha256(), reinterpret_cast<const unsigned char *>(secret.data()), |
696 | | static_cast<int>(secret.size()), input.data(), input.size(), digest.data(), |
697 | | &produced) == nullptr) { |
698 | | return std::nullopt; |
699 | | } |
700 | | return digest; |
701 | | } |
702 | | |
703 | 672 | std::optional<std::uint16_t> read_u16_be(BufferReader &reader) { |
704 | 672 | const auto encoded_value = reader.read_exact(sizeof(std::uint16_t)); |
705 | 672 | if (!encoded_value.has_value()) { |
706 | 0 | return std::nullopt; |
707 | 0 | } |
708 | 672 | return static_cast<std::uint16_t>( |
709 | 672 | (static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(encoded_value.value()[0])) << 8) | |
710 | 672 | static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(encoded_value.value()[1]))); |
711 | 672 | } |
712 | | |
713 | 168 | std::optional<std::uint32_t> read_u32_be(BufferReader &reader) { |
714 | 168 | const auto encoded_value = reader.read_exact(sizeof(std::uint32_t)); |
715 | 168 | if (!encoded_value.has_value()) { |
716 | 0 | return std::nullopt; |
717 | 0 | } |
718 | 168 | const auto value_bytes = encoded_value.value(); |
719 | 168 | return (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(value_bytes[0])) << 24) | |
720 | 168 | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(value_bytes[1])) << 16) | |
721 | 168 | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(value_bytes[2])) << 8) | |
722 | 168 | static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(value_bytes[3])); |
723 | 168 | } |
724 | | |
725 | 336 | std::optional<std::uint64_t> read_u64_be(BufferReader &reader) { |
726 | 336 | const auto encoded_value = reader.read_exact(sizeof(std::uint64_t)); |
727 | 336 | if (!encoded_value.has_value()) { |
728 | 0 | return std::nullopt; |
729 | 0 | } |
730 | 336 | std::uint64_t value = 0; |
731 | 2.68k | for (const auto byte : encoded_value.value()) { |
732 | 2.68k | value = (value << 8) | std::to_integer<std::uint8_t>(byte); |
733 | 2.68k | } |
734 | 336 | return value; |
735 | 336 | } |
736 | | |
737 | | COQUIC_NO_PROFILE std::optional<std::vector<std::byte>> |
738 | | read_length_prefixed_bytes(BufferReader &reader) { |
739 | | const auto size = read_u16_be(reader); |
740 | | if (!size.has_value() || reader.remaining() < *size) { |
741 | | return std::nullopt; |
742 | | } |
743 | | const auto encoded_value = reader.read_exact(*size); |
744 | | if (!encoded_value.has_value()) { |
745 | | return std::nullopt; |
746 | | } |
747 | | return std::vector<std::byte>(encoded_value.value().begin(), encoded_value.value().end()); |
748 | | } |
749 | | |
750 | | COQUIC_NO_PROFILE bool append_length_prefixed_bytes(std::vector<std::byte> &encoded_bytes, |
751 | | std::span<const std::byte> value) { |
752 | | if (value.size() > std::numeric_limits<std::uint16_t>::max()) { |
753 | | return false; |
754 | | } |
755 | | append_u16_be(encoded_bytes, static_cast<std::uint16_t>(value.size())); |
756 | | encoded_bytes.insert(encoded_bytes.end(), value.begin(), value.end()); |
757 | | return true; |
758 | | } |
759 | | |
760 | 112 | std::uint64_t token_timestamp_us(QuicCoreTimePoint time) { |
761 | 112 | return static_cast<std::uint64_t>( |
762 | 112 | std::chrono::duration_cast<QuicCoreDuration>(time.time_since_epoch()).count()); |
763 | 112 | } |
764 | | |
765 | 192 | QuicCoreTimePoint token_time_from_us(std::uint64_t microseconds) { |
766 | 192 | return QuicCoreTimePoint{} + QuicCoreDuration(static_cast<QuicCoreDuration::rep>(microseconds)); |
767 | 192 | } |
768 | | |
769 | | COQUIC_NO_PROFILE std::optional<std::array<std::byte, kAddressValidationTokenTagLength>> |
770 | | compute_address_validation_token_tag( |
771 | | std::span<const std::byte> secret, // NOLINT(bugprone-easily-swappable-parameters) |
772 | | std::span<const std::byte> body) { |
773 | | constexpr std::array label{ |
774 | | std::byte{'c'}, std::byte{'o'}, std::byte{'q'}, std::byte{'u'}, std::byte{'i'}, |
775 | | std::byte{'c'}, std::byte{' '}, std::byte{'a'}, std::byte{'d'}, std::byte{'d'}, |
776 | | std::byte{'r'}, std::byte{' '}, std::byte{'t'}, std::byte{'o'}, std::byte{'k'}, |
777 | | std::byte{'e'}, std::byte{'n'}, |
778 | | }; |
779 | | std::vector<unsigned char> input; |
780 | | input.reserve(label.size() + body.size()); |
781 | | for (const auto byte : label) { |
782 | | input.push_back(std::to_integer<unsigned char>(byte)); |
783 | | } |
784 | | for (const auto byte : body) { |
785 | | input.push_back(std::to_integer<unsigned char>(byte)); |
786 | | } |
787 | | |
788 | | unsigned int produced = 0; |
789 | | const auto digest = compute_hmac_sha256_for_core( |
790 | | secret, input, produced, |
791 | | core_test_fault_state().force_address_validation_token_tag_failure); |
792 | | if (!digest.has_value() || produced < kAddressValidationTokenTagLength) { |
793 | | return std::nullopt; |
794 | | } |
795 | | |
796 | | std::array<std::byte, kAddressValidationTokenTagLength> token_tag{}; |
797 | | std::copy_n(reinterpret_cast<const std::byte *>(digest->data()), token_tag.size(), |
798 | | token_tag.begin()); |
799 | | return token_tag; |
800 | | } |
801 | | |
802 | | struct SelfContainedAddressValidationToken { |
803 | | std::byte kind = kAddressValidationNewTokenType; |
804 | | std::uint32_t version = kQuicVersion1; |
805 | | std::optional<QuicRouteHandle> route_handle; |
806 | | std::vector<std::byte> address_validation_identity; |
807 | | ConnectionId original_destination_connection_id; |
808 | | ConnectionId retry_source_connection_id; |
809 | | std::vector<std::byte> nonce; |
810 | | QuicCoreTimePoint expires_at{}; |
811 | | }; |
812 | | |
813 | | COQUIC_NO_PROFILE std::optional<std::vector<std::byte>> |
814 | | encode_address_validation_token_body(const SelfContainedAddressValidationToken &token_metadata) { |
815 | | std::vector<std::byte> body; |
816 | | body.reserve(96 + token_metadata.address_validation_identity.size() + |
817 | | token_metadata.original_destination_connection_id.size() + |
818 | | token_metadata.retry_source_connection_id.size() + token_metadata.nonce.size()); |
819 | | body.push_back(std::byte{'C'}); |
820 | | body.push_back(std::byte{'Q'}); |
821 | | body.push_back(std::byte{'A'}); |
822 | | body.push_back(std::byte{'V'}); |
823 | | body.push_back(std::byte{0x01}); |
824 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.1 |
825 | | // # A token sent in a NEW_TOKEN frame or a Retry packet MUST be |
826 | | // # constructed in a way that allows the server to identify how it was |
827 | | // # provided to a client. |
828 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
829 | | // # Information that allows the server to distinguish between tokens from |
830 | | // # Retry and NEW_TOKEN MAY be accessible to entities other than the |
831 | | // # server. |
832 | | body.push_back(token_metadata.kind); |
833 | | append_u32_be(body, token_metadata.version); |
834 | | body.push_back(token_metadata.route_handle.has_value() ? std::byte{0x01} : std::byte{0x00}); |
835 | | append_u64_be(body, token_metadata.route_handle.value_or(0)); |
836 | | append_u64_be(body, token_timestamp_us(token_metadata.expires_at)); |
837 | | if (!append_length_prefixed_bytes(body, token_metadata.address_validation_identity) || |
838 | | !append_length_prefixed_bytes(body, token_metadata.original_destination_connection_id) || |
839 | | !append_length_prefixed_bytes(body, token_metadata.retry_source_connection_id) || |
840 | | !append_length_prefixed_bytes(body, token_metadata.nonce)) { |
841 | | return std::nullopt; |
842 | | } |
843 | | return body; |
844 | | } |
845 | | |
846 | | COQUIC_NO_PROFILE std::optional<SelfContainedAddressValidationToken> |
847 | | decode_address_validation_token_body(std::span<const std::byte> body) { |
848 | | BufferReader reader(body); |
849 | | const auto magic = reader.read_exact(4); |
850 | | if (!magic.has_value() || magic.value().size() != 4 || magic.value()[0] != std::byte{'C'} || |
851 | | magic.value()[1] != std::byte{'Q'} || magic.value()[2] != std::byte{'A'} || |
852 | | magic.value()[3] != std::byte{'V'}) { |
853 | | return std::nullopt; |
854 | | } |
855 | | const auto format_version = reader.read_byte(); |
856 | | const auto kind = reader.read_byte(); |
857 | | const auto version = read_u32_be(reader); |
858 | | const auto route_present = reader.read_byte(); |
859 | | const auto route_handle = read_u64_be(reader); |
860 | | const auto expires_at = read_u64_be(reader); |
861 | | if (!format_version.has_value() || format_version.value() != std::byte{0x01} || |
862 | | !kind.has_value() || |
863 | | (kind.value() != kAddressValidationRetryTokenType && |
864 | | kind.value() != kAddressValidationNewTokenType) || |
865 | | !version.has_value() || !route_present.has_value() || !route_handle.has_value() || |
866 | | !expires_at.has_value()) { |
867 | | return std::nullopt; |
868 | | } |
869 | | |
870 | | auto address_validation_identity = read_length_prefixed_bytes(reader); |
871 | | auto original_destination_connection_id = read_length_prefixed_bytes(reader); |
872 | | auto retry_source_connection_id = read_length_prefixed_bytes(reader); |
873 | | auto nonce = read_length_prefixed_bytes(reader); |
874 | | if (!address_validation_identity.has_value() || |
875 | | !original_destination_connection_id.has_value() || |
876 | | !retry_source_connection_id.has_value() || !nonce.has_value() || reader.remaining() != 0) { |
877 | | return std::nullopt; |
878 | | } |
879 | | |
880 | | return SelfContainedAddressValidationToken{ |
881 | | .kind = kind.value(), |
882 | | .version = *version, |
883 | | .route_handle = route_present.value() == std::byte{0x01} ? route_handle : std::nullopt, |
884 | | .address_validation_identity = std::move(*address_validation_identity), |
885 | | .original_destination_connection_id = std::move(*original_destination_connection_id), |
886 | | .retry_source_connection_id = std::move(*retry_source_connection_id), |
887 | | .nonce = std::move(*nonce), |
888 | | .expires_at = token_time_from_us(*expires_at), |
889 | | }; |
890 | | } |
891 | | |
892 | | COQUIC_NO_PROFILE std::optional<std::vector<std::byte>> |
893 | | seal_address_validation_token(const QuicAddressValidationTokenSecret &secret, |
894 | | const SelfContainedAddressValidationToken &metadata) { |
895 | | auto body = encode_address_validation_token_body(metadata); |
896 | | if (!body.has_value()) { |
897 | | return std::nullopt; |
898 | | } |
899 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
900 | | // # For this design to work, the token MUST be covered by integrity |
901 | | // # protection against modification or falsification by clients. |
902 | | const auto tag = compute_address_validation_token_tag(secret, *body); |
903 | | if (!tag.has_value()) { |
904 | | return std::nullopt; |
905 | | } |
906 | | |
907 | | std::vector<std::byte> sealed_token; |
908 | | sealed_token.reserve(body->size() + tag->size()); |
909 | | sealed_token.insert(sealed_token.end(), body->begin(), body->end()); |
910 | | sealed_token.insert(sealed_token.end(), tag->begin(), tag->end()); |
911 | | return sealed_token; |
912 | | } |
913 | | |
914 | | COQUIC_NO_PROFILE std::optional<SelfContainedAddressValidationToken> |
915 | | open_address_validation_token(const QuicAddressValidationTokenSecret &secret, |
916 | | std::span<const std::byte> sealed_token) { |
917 | | if (sealed_token.size() <= kAddressValidationTokenTagLength) { |
918 | | return std::nullopt; |
919 | | } |
920 | | const auto body = sealed_token.first(sealed_token.size() - kAddressValidationTokenTagLength); |
921 | | const auto tag = sealed_token.last(kAddressValidationTokenTagLength); |
922 | | const auto expected = compute_address_validation_token_tag(secret, body); |
923 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
924 | | // # For this design to work, the token MUST be covered by integrity |
925 | | // # protection against modification or falsification by clients. |
926 | | if (!expected.has_value() || |
927 | | CRYPTO_memcmp(expected->data(), tag.data(), expected->size()) != 0) { |
928 | | return std::nullopt; |
929 | | } |
930 | | return decode_address_validation_token_body(body); |
931 | | } |
932 | | |
933 | | std::optional<AddressValidationTokenKind> |
934 | 40 | peek_self_contained_address_validation_token_kind(std::span<const std::byte> sealed_token) { |
935 | 40 | if (sealed_token.size() <= kAddressValidationTokenTagLength) { |
936 | 8 | return std::nullopt; |
937 | 8 | } |
938 | 32 | const auto body = sealed_token.first(sealed_token.size() - kAddressValidationTokenTagLength); |
939 | 32 | if (body.size() < 6 || body[0] != std::byte{'C'} || body[1] != std::byte{'Q'} || |
940 | 32 | body[2] != std::byte{'A'} || body[3] != std::byte{'V'} || body[4] != std::byte{0x01}) { |
941 | 0 | return std::nullopt; |
942 | 0 | } |
943 | 32 | if (body[5] == kAddressValidationRetryTokenType) { |
944 | 8 | return AddressValidationTokenKind::retry; |
945 | 8 | } |
946 | 24 | if (body[5] == kAddressValidationNewTokenType) { |
947 | 24 | return AddressValidationTokenKind::new_token; |
948 | 24 | } |
949 | 0 | return std::nullopt; |
950 | 24 | } |
951 | | |
952 | | std::optional<SelfContainedAddressValidationToken> |
953 | 16 | peek_self_contained_address_validation_token(std::span<const std::byte> sealed_token) { |
954 | 16 | if (sealed_token.size() <= kAddressValidationTokenTagLength) { |
955 | 8 | return std::nullopt; |
956 | 8 | } |
957 | 8 | const auto body = sealed_token.first(sealed_token.size() - kAddressValidationTokenTagLength); |
958 | 8 | return decode_address_validation_token_body(body); |
959 | 16 | } |
960 | | |
961 | 296 | std::string address_validation_token_replay_key(std::span<const std::byte> sealed_token) { |
962 | 296 | if (sealed_token.size() >= kAddressValidationTokenTagLength) { |
963 | 240 | const auto tag = sealed_token.last(kAddressValidationTokenTagLength); |
964 | 240 | return std::string(reinterpret_cast<const char *>(tag.data()), tag.size()); |
965 | 240 | } |
966 | 56 | return std::string(reinterpret_cast<const char *>(sealed_token.data()), sealed_token.size()); |
967 | 296 | } |
968 | | |
969 | 16 | std::string hex_encode_bytes(std::span<const std::byte> bytes) { |
970 | 16 | std::ostringstream hex; |
971 | 16 | hex << std::hex << std::setfill('0'); |
972 | 256 | for (const auto byte : bytes) { |
973 | 256 | hex << std::setw(2) << static_cast<unsigned>(std::to_integer<std::uint8_t>(byte)); |
974 | 256 | } |
975 | 16 | return hex.str(); |
976 | 16 | } |
977 | | |
978 | | COQUIC_NO_PROFILE std::optional<std::string> hex_decode_to_string(std::string_view hex) { |
979 | | if ((hex.size() % 2u) != 0u) { |
980 | | return std::nullopt; |
981 | | } |
982 | | |
983 | 624 | const auto nibble = [](char ch) -> std::optional<std::uint8_t> { |
984 | 624 | if (static_cast<unsigned>(ch - '0') <= 9u) { |
985 | 456 | return static_cast<std::uint8_t>(ch - '0'); |
986 | 456 | } |
987 | 168 | if (ch >= 'a' && ch <= 'f') { |
988 | 152 | return static_cast<std::uint8_t>(10u + static_cast<unsigned>(ch - 'a')); |
989 | 152 | } |
990 | 16 | if (ch >= 'A' && ch <= 'F') { |
991 | 0 | return static_cast<std::uint8_t>(10u + static_cast<unsigned>(ch - 'A')); |
992 | 0 | } |
993 | 16 | return std::nullopt; |
994 | 16 | }; |
995 | | |
996 | | std::string decoded; |
997 | | decoded.reserve(hex.size() / 2u); |
998 | | for (std::size_t offset = 0; offset < hex.size(); offset += 2u) { |
999 | | const auto high = nibble(hex[offset]); |
1000 | | auto low_nibble = nibble(hex[offset + 1u]); |
1001 | | if (!high.has_value() || !low_nibble.has_value()) { |
1002 | | return std::nullopt; |
1003 | | } |
1004 | | decoded.push_back(static_cast<char>(static_cast<unsigned>(*high << 4u) | |
1005 | | static_cast<unsigned>(*low_nibble))); |
1006 | | } |
1007 | | return decoded; |
1008 | | } |
1009 | | |
1010 | | COQUIC_NO_PROFILE std::optional<std::uint64_t> parse_unsigned_decimal(std::string_view value) { |
1011 | | if (value.empty()) { |
1012 | | return std::nullopt; |
1013 | | } |
1014 | | |
1015 | | std::uint64_t parsed = 0; |
1016 | | for (const char ch : value) { |
1017 | | if (ch < '0' || ch > '9') { |
1018 | | return std::nullopt; |
1019 | | } |
1020 | | const auto digit = static_cast<std::uint64_t>(ch - '0'); |
1021 | | if (parsed > (std::numeric_limits<std::uint64_t>::max() - digit) / 10u) { |
1022 | | return std::nullopt; |
1023 | | } |
1024 | | parsed = (parsed * 10u) + digit; |
1025 | | } |
1026 | | return parsed; |
1027 | | } |
1028 | | |
1029 | | COQUIC_NO_PROFILE bool token_route_matches(const std::optional<QuicRouteHandle> &expected, |
1030 | | const std::optional<QuicRouteHandle> &actual) { |
1031 | | return !expected.has_value() || expected == actual; |
1032 | | } |
1033 | | |
1034 | | bool token_identity_matches(std::span<const std::byte> expected, |
1035 | 120 | std::span<const std::byte> actual) { |
1036 | 120 | return expected.empty() || std::ranges::equal(expected, actual); |
1037 | 120 | } |
1038 | | |
1039 | | std::optional<std::uint16_t> |
1040 | | COQUIC_NO_PROFILE address_validation_identity_udp_port(std::span<const std::byte> identity) { |
1041 | | if (identity.size() == 7 && identity.front() == std::byte{0x04}) { |
1042 | | return static_cast<std::uint16_t>( |
1043 | | (static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(identity[5])) << 8) | |
1044 | | static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(identity[6]))); |
1045 | | } |
1046 | | if (identity.size() == 19 && identity.front() == std::byte{0x06}) { |
1047 | | return static_cast<std::uint16_t>( |
1048 | | (static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(identity[17])) << 8) | |
1049 | | static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(identity[18]))); |
1050 | | } |
1051 | | return std::nullopt; |
1052 | | } |
1053 | | |
1054 | | COQUIC_NO_PROFILE QuicRouteAddressFamily |
1055 | | route_address_family_from_identity(std::span<const std::byte> identity) { |
1056 | | if (identity.size() == 7 && identity.front() == std::byte{0x04}) { |
1057 | | return QuicRouteAddressFamily::ipv4; |
1058 | | } |
1059 | | if (identity.size() == 19 && identity.front() == std::byte{0x06}) { |
1060 | | return QuicRouteAddressFamily::ipv6; |
1061 | | } |
1062 | | return QuicRouteAddressFamily::unknown; |
1063 | | } |
1064 | | |
1065 | | COQUIC_NO_PROFILE bool preferred_address_has_family(const PreferredAddress &preferred_address, |
1066 | | QuicRouteAddressFamily family) { |
1067 | | switch (family) { |
1068 | | case QuicRouteAddressFamily::ipv4: |
1069 | | return preferred_address.ipv4_port != 0; |
1070 | | case QuicRouteAddressFamily::ipv6: |
1071 | | return preferred_address.ipv6_port != 0; |
1072 | | case QuicRouteAddressFamily::unknown: |
1073 | | return true; |
1074 | | } |
1075 | | return true; |
1076 | | } |
1077 | | |
1078 | | COQUIC_NO_PROFILE std::size_t |
1079 | | default_pmtud_search_ceiling_for_route_family(QuicRouteAddressFamily family) { |
1080 | | switch (family) { |
1081 | | case QuicRouteAddressFamily::ipv4: |
1082 | | return kPmtudIPv4EthernetUdpPayloadSize; |
1083 | | case QuicRouteAddressFamily::ipv6: |
1084 | | case QuicRouteAddressFamily::unknown: |
1085 | | return kPmtudIPv6EthernetUdpPayloadSize; |
1086 | | } |
1087 | | return kPmtudIPv6EthernetUdpPayloadSize; |
1088 | | } |
1089 | | |
1090 | | QuicAddressValidationIdentityClass COQUIC_NO_PROFILE |
1091 | | classify_ipv4_address_validation_identity(std::span<const std::byte> identity) { |
1092 | | if (identity.size() != 7 || identity.front() != std::byte{0x04}) { |
1093 | | return QuicAddressValidationIdentityClass::unknown; |
1094 | | } |
1095 | | |
1096 | | const auto a = std::to_integer<std::uint8_t>(identity[1]); |
1097 | | const auto b = std::to_integer<std::uint8_t>(identity[2]); |
1098 | | if (a == 127) { |
1099 | | return QuicAddressValidationIdentityClass::loopback; |
1100 | | } |
1101 | | if (a == 169 && b == 254) { |
1102 | | return QuicAddressValidationIdentityClass::link_local; |
1103 | | } |
1104 | | if (a == 10 || (a == 172 && b >= 16 && b <= 31) || (a == 192 && b == 168)) { |
1105 | | return QuicAddressValidationIdentityClass::private_use; |
1106 | | } |
1107 | | return QuicAddressValidationIdentityClass::global; |
1108 | | } |
1109 | | |
1110 | | QuicAddressValidationIdentityClass COQUIC_NO_PROFILE |
1111 | | classify_ipv6_address_validation_identity(std::span<const std::byte> identity) { |
1112 | | if (identity.size() != 19 || identity.front() != std::byte{0x06}) { |
1113 | | return QuicAddressValidationIdentityClass::unknown; |
1114 | | } |
1115 | | |
1116 | | bool loopback = true; |
1117 | | for (std::size_t index = 1; index < 16; ++index) { |
1118 | | loopback = loopback && identity[index] == std::byte{0x00}; |
1119 | | } |
1120 | | if (loopback && identity[16] == std::byte{0x01}) { |
1121 | | return QuicAddressValidationIdentityClass::loopback; |
1122 | | } |
1123 | | |
1124 | | const auto first = std::to_integer<std::uint8_t>(identity[1]); |
1125 | | const auto second = std::to_integer<std::uint8_t>(identity[2]); |
1126 | | if (first == 0xfe && (second & 0xc0u) == 0x80u) { |
1127 | | return QuicAddressValidationIdentityClass::link_local; |
1128 | | } |
1129 | | if ((first & 0xfeu) == 0xfcu) { |
1130 | | return QuicAddressValidationIdentityClass::unique_local; |
1131 | | } |
1132 | | return QuicAddressValidationIdentityClass::global; |
1133 | | } |
1134 | | |
1135 | | QuicAddressValidationIdentityClass |
1136 | 396 | classify_address_validation_identity(std::span<const std::byte> identity) { |
1137 | 396 | if (identity.empty()) { |
1138 | 0 | return QuicAddressValidationIdentityClass::unknown; |
1139 | 0 | } |
1140 | 396 | if (identity.front() == std::byte{0x04}) { |
1141 | 324 | return classify_ipv4_address_validation_identity(identity); |
1142 | 324 | } |
1143 | 72 | if (identity.front() == std::byte{0x06}) { |
1144 | 8 | return classify_ipv6_address_validation_identity(identity); |
1145 | 8 | } |
1146 | 64 | return QuicAddressValidationIdentityClass::unknown; |
1147 | 72 | } |
1148 | | |
1149 | | COQUIC_NO_PROFILE bool |
1150 | | address_class_is_private_like(QuicAddressValidationIdentityClass address_class) { |
1151 | | return address_class == QuicAddressValidationIdentityClass::loopback || |
1152 | | address_class == QuicAddressValidationIdentityClass::link_local || |
1153 | | address_class == QuicAddressValidationIdentityClass::private_use || |
1154 | | address_class == QuicAddressValidationIdentityClass::unique_local; |
1155 | | } |
1156 | | |
1157 | | COQUIC_NO_PROFILE bool |
1158 | | address_class_is_public_like(QuicAddressValidationIdentityClass address_class) { |
1159 | | return address_class == QuicAddressValidationIdentityClass::global || |
1160 | | address_class == QuicAddressValidationIdentityClass::unique_local; |
1161 | | } |
1162 | | |
1163 | | COQUIC_NO_PROFILE bool address_identity_allowed_by_request_forgery_policy( |
1164 | | const QuicRequestForgeryPolicyConfig &policy, |
1165 | | std::span<const std::byte> current_identity, // NOLINT(bugprone-easily-swappable-parameters) |
1166 | | std::span<const std::byte> candidate_identity) { |
1167 | | const auto candidate_class = classify_address_validation_identity(candidate_identity); |
1168 | | if (policy.reject_loopback_addresses && |
1169 | | candidate_class == QuicAddressValidationIdentityClass::loopback) { |
1170 | | return false; |
1171 | | } |
1172 | | if (policy.reject_link_local_addresses && |
1173 | | candidate_class == QuicAddressValidationIdentityClass::link_local) { |
1174 | | return false; |
1175 | | } |
1176 | | if (policy.reject_private_use_addresses && |
1177 | | (candidate_class == QuicAddressValidationIdentityClass::private_use || |
1178 | | candidate_class == QuicAddressValidationIdentityClass::unique_local)) { |
1179 | | return false; |
1180 | | } |
1181 | | if (policy.reject_address_space_downgrade && !current_identity.empty()) { |
1182 | | const auto current_class = classify_address_validation_identity(current_identity); |
1183 | | if (address_class_is_public_like(current_class) && |
1184 | | address_class_is_private_like(candidate_class)) { |
1185 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.5.6 |
1186 | | // # Endpoints SHOULD NOT allow connections or migration to a |
1187 | | // # loopback address if the same service was previously available |
1188 | | // # at a different interface or if the address was provided by a |
1189 | | // # service at a non-loopback address. |
1190 | | return false; |
1191 | | } |
1192 | | } |
1193 | | if (const auto port = address_validation_identity_udp_port(candidate_identity); |
1194 | | port.has_value() && |
1195 | | std::ranges::find(policy.blocked_udp_ports, *port) != policy.blocked_udp_ports.end()) { |
1196 | | return false; |
1197 | | } |
1198 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.5.6 |
1199 | | // # Endpoints SHOULD NOT refuse to use an address unless they have specific |
1200 | | // # knowledge about the network indicating that sending datagrams to |
1201 | | // # unvalidated addresses in a given range is not safe. |
1202 | | return true; |
1203 | | } |
1204 | | |
1205 | | COQUIC_NO_PROFILE bool fill_endpoint_connection_id_from_openssl(ConnectionId &connection_id, |
1206 | | bool force_failure) { |
1207 | | return !force_failure && connection_id.size() > 1 && |
1208 | | RAND_bytes(reinterpret_cast<unsigned char *>(connection_id.data() + 1), |
1209 | | static_cast<int>(connection_id.size() - 1)) == 1; |
1210 | | } |
1211 | | |
1212 | | ConnectionId make_endpoint_connection_id(std::byte prefix, std::uint64_t sequence, |
1213 | 308 | std::mt19937_64 &fallback_random) { |
1214 | 308 | ConnectionId connection_id(kEndpointConnectionIdLength, std::byte{0x00}); |
1215 | 308 | connection_id.front() = prefix; |
1216 | 308 | if (fill_endpoint_connection_id_from_openssl( |
1217 | 308 | connection_id, core_test_fault_state().force_endpoint_connection_id_rand_failure)) { |
1218 | 308 | return connection_id; |
1219 | 308 | } |
1220 | 0 | for (std::size_t index = 1; index < connection_id.size(); ++index) { |
1221 | 0 | connection_id[index] = static_cast<std::byte>(fallback_random()); |
1222 | 0 | } |
1223 | 0 | connection_id.back() = |
1224 | 0 | static_cast<std::byte>(std::to_integer<std::uint8_t>(connection_id.back()) ^ |
1225 | 0 | static_cast<std::uint8_t>(sequence & 0xffu)); |
1226 | 0 | return connection_id; |
1227 | 308 | } |
1228 | | |
1229 | | std::vector<std::byte> make_stateless_reset_token_context(std::span<const std::byte> connection_id, |
1230 | 8 | std::uint64_t sequence_number) { |
1231 | 8 | std::vector<std::byte> context; |
1232 | 8 | context.reserve(connection_id.size() + sizeof(sequence_number) + sizeof(std::uint64_t)); |
1233 | 8 | context.insert(context.end(), connection_id.begin(), connection_id.end()); |
1234 | 72 | for (int shift = 56; shift >= 0; shift -= 8) { |
1235 | 64 | context.push_back( |
1236 | 64 | static_cast<std::byte>((sequence_number >> static_cast<unsigned>(shift)) & 0xffu)); |
1237 | 64 | } |
1238 | 72 | for (int shift = 56; shift >= 0; shift -= 8) { |
1239 | 64 | context.push_back(std::byte{0x00}); |
1240 | 64 | } |
1241 | 8 | return context; |
1242 | 8 | } |
1243 | | |
1244 | | COQUIC_NO_PROFILE std::optional<std::array<std::byte, kStatelessResetTokenLength>> |
1245 | | derive_stateless_reset_token( |
1246 | | std::span<const std::byte> secret, // NOLINT(bugprone-easily-swappable-parameters) |
1247 | | std::span<const std::byte> connection_id, std::uint64_t sequence_number) { |
1248 | | constexpr std::array label{ |
1249 | | std::byte{'c'}, std::byte{'o'}, std::byte{'q'}, std::byte{'u'}, std::byte{'i'}, |
1250 | | std::byte{'c'}, std::byte{' '}, std::byte{'s'}, std::byte{'r'}, std::byte{'t'}, |
1251 | | }; |
1252 | | const auto context = make_stateless_reset_token_context(connection_id, sequence_number); |
1253 | | std::vector<unsigned char> input; |
1254 | | input.reserve(label.size() + context.size()); |
1255 | | for (const auto byte : label) { |
1256 | | input.push_back(std::to_integer<unsigned char>(byte)); |
1257 | | } |
1258 | | for (const auto byte : context) { |
1259 | | input.push_back(std::to_integer<unsigned char>(byte)); |
1260 | | } |
1261 | | |
1262 | | unsigned int produced = 0; |
1263 | | const auto digest = compute_hmac_sha256_for_core( |
1264 | | secret, input, produced, |
1265 | | core_test_fault_state().force_stateless_reset_token_derivation_failure); |
1266 | | if (!digest.has_value() || produced < kStatelessResetTokenLength) { |
1267 | | return std::nullopt; |
1268 | | } |
1269 | | |
1270 | | std::array<std::byte, kStatelessResetTokenLength> token{}; |
1271 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.2 |
1272 | | // # The stateless reset token MUST be difficult to guess. |
1273 | | std::copy_n(reinterpret_cast<const std::byte *>(digest->data()), token.size(), token.begin()); |
1274 | | return token; |
1275 | | } |
1276 | | |
1277 | | // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) |
1278 | 1.75k | bool is_initial_long_header_type(std::uint32_t version, std::uint8_t type) { |
1279 | 1.75k | if (version == kQuicVersion2) { |
1280 | 48 | return type == 0x01u; |
1281 | 48 | } |
1282 | 1.70k | return type == 0x00u; |
1283 | 1.75k | } |
1284 | | |
1285 | | // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) |
1286 | 288 | bool is_zero_rtt_long_header_type(std::uint32_t version, std::uint8_t type) { |
1287 | 288 | if (version == kQuicVersion2) { |
1288 | 16 | return type == 0x02u; |
1289 | 16 | } |
1290 | 272 | return type == 0x01u; |
1291 | 288 | } |
1292 | | |
1293 | | std::optional<VersionNegotiationPacket> |
1294 | 6.52k | parse_version_negotiation_packet(std::span<const std::byte> bytes) { |
1295 | 6.52k | if (bytes.size() < 5) { |
1296 | 192 | return std::nullopt; |
1297 | 192 | } |
1298 | 6.32k | const auto version = |
1299 | 6.32k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[1])) << 24) | |
1300 | 6.32k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[2])) << 16) | |
1301 | 6.32k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[3])) << 8) | |
1302 | 6.32k | static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[4])); |
1303 | 6.32k | if (version != kVersionNegotiationVersion) { |
1304 | 6.20k | return std::nullopt; |
1305 | 6.20k | } |
1306 | | |
1307 | 120 | const auto decoded = deserialize_packet(bytes, {}); |
1308 | 120 | if (!decoded.has_value()) { |
1309 | 8 | return std::nullopt; |
1310 | 8 | } |
1311 | | |
1312 | 112 | return std::get<VersionNegotiationPacket>(decoded.value().packet); |
1313 | 120 | } |
1314 | | |
1315 | 38.1k | std::optional<RetryPacket> parse_retry_packet(std::span<const std::byte> bytes) { |
1316 | 38.1k | if (bytes.size() < 5) { |
1317 | 96 | return std::nullopt; |
1318 | 96 | } |
1319 | 38.0k | const auto first_byte = std::to_integer<std::uint8_t>(bytes.front()); |
1320 | 38.0k | if ((first_byte & 0x80u) == 0) { |
1321 | 36.3k | return std::nullopt; |
1322 | 36.3k | } |
1323 | 1.70k | const auto version = |
1324 | 1.70k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[1])) << 24) | |
1325 | 1.70k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[2])) << 16) | |
1326 | 1.70k | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[3])) << 8) | |
1327 | 1.70k | static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[4])); |
1328 | 1.70k | if (version == kVersionNegotiationVersion) { |
1329 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.1 |
1330 | | // # An endpoint MUST NOT send a Version Negotiation packet |
1331 | | // # in response to receiving a Version Negotiation packet. |
1332 | 16 | return std::nullopt; |
1333 | 16 | } |
1334 | 1.68k | const auto encoded_type = static_cast<std::uint8_t>((first_byte >> 4) & 0x03u); |
1335 | 1.68k | const auto retry_type = version == kQuicVersion2 ? std::uint8_t{0x00} : std::uint8_t{0x03}; |
1336 | 1.68k | if (encoded_type != retry_type) { |
1337 | 1.56k | return std::nullopt; |
1338 | 1.56k | } |
1339 | | |
1340 | 120 | const auto decoded = deserialize_packet(bytes, {}); |
1341 | 120 | if (!decoded.has_value() || decoded.value().bytes_consumed != bytes.size()) { |
1342 | 0 | return std::nullopt; |
1343 | 0 | } |
1344 | | |
1345 | 120 | if (const auto *retry = std::get_if<RetryPacket>(&decoded.value().packet)) { |
1346 | 120 | return *retry; |
1347 | 120 | } |
1348 | | |
1349 | 0 | return std::nullopt; |
1350 | 120 | } |
1351 | | |
1352 | 296 | bool token_time_expired(QuicCoreTimePoint expires_at, QuicCoreTimePoint now) { |
1353 | 296 | return expires_at != QuicCoreTimePoint{} && now > expires_at; |
1354 | 296 | } |
1355 | | |
1356 | | void purge_expired_consumed_address_validation_tokens( |
1357 | 392 | std::unordered_map<std::string, QuicCoreTimePoint> &tokens, QuicCoreTimePoint now) { |
1358 | 392 | if (now == QuicCoreTimePoint{}) { |
1359 | 8 | return; |
1360 | 8 | } |
1361 | 456 | for (auto it = tokens.begin(); it != tokens.end();) { |
1362 | 72 | if (token_time_expired(it->second, now)) { |
1363 | 8 | it = tokens.erase(it); |
1364 | 64 | } else { |
1365 | 64 | ++it; |
1366 | 64 | } |
1367 | 72 | } |
1368 | 384 | } |
1369 | | |
1370 | | COQUIC_NO_PROFILE bool fill_random_bytes_from_openssl(std::span<std::byte> bytes, |
1371 | | bool force_failure) { |
1372 | | return !force_failure && RAND_bytes(reinterpret_cast<unsigned char *>(bytes.data()), |
1373 | | static_cast<int>(bytes.size())) == 1; |
1374 | | } |
1375 | | |
1376 | 364 | void fill_random_bytes(std::span<std::byte> bytes, std::mt19937_64 &fallback_random) { |
1377 | 364 | if (bytes.empty()) { |
1378 | 0 | return; |
1379 | 0 | } |
1380 | 364 | if (fill_random_bytes_from_openssl( |
1381 | 364 | bytes, core_test_fault_state().force_fill_random_bytes_rand_failure)) { |
1382 | 364 | return; |
1383 | 364 | } |
1384 | 0 | for (auto &byte : bytes) { |
1385 | 0 | byte = static_cast<std::byte>(fallback_random()); |
1386 | 0 | } |
1387 | 0 | } |
1388 | | |
1389 | | } // namespace |
1390 | | |
1391 | | namespace detail { |
1392 | | |
1393 | | COQUIC_NO_PROFILE void *allocate_core_effect_storage(CoreEffectStorageBytes bytes, |
1394 | | CoreEffectStorageAlignment alignment) { |
1395 | | if (bytes.value == 0) { |
1396 | | return nullptr; |
1397 | | } |
1398 | | |
1399 | | const auto allocation_bytes = core_effect_storage_allocation_bytes(bytes.value); |
1400 | | #if COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE == 0 |
1401 | | if (auto *cached = core_effect_storage_cache().take(allocation_bytes, alignment.value); |
1402 | | cached != nullptr) { |
1403 | | return cached; |
1404 | | } |
1405 | | |
1406 | | return allocate_aligned_cache_storage(allocation_bytes, alignment.value); |
1407 | | #else |
1408 | | return allocate_aligned_cache_storage(allocation_bytes, alignment.value); |
1409 | | #endif |
1410 | | } |
1411 | | |
1412 | | COQUIC_NO_PROFILE void |
1413 | | deallocate_core_effect_storage(void *pointer, CoreEffectStorageBytes bytes, |
1414 | | CoreEffectStorageAlignment alignment) noexcept { |
1415 | | if (pointer == nullptr || bytes.value == 0) { |
1416 | | return; |
1417 | | } |
1418 | | |
1419 | | const auto allocation_bytes = core_effect_storage_allocation_bytes(bytes.value); |
1420 | | #if COQUIC_DISABLE_CORE_EFFECT_STORAGE_CACHE == 0 |
1421 | | if (allocation_bytes <= kCoreEffectStorageCacheMaxBytes && |
1422 | | core_effect_storage_cache().put(pointer, allocation_bytes, alignment.value)) { |
1423 | | return; |
1424 | | } |
1425 | | |
1426 | | deallocate_aligned_cache_storage(pointer, alignment.value); |
1427 | | #else |
1428 | | deallocate_aligned_cache_storage(pointer, alignment.value); |
1429 | | #endif |
1430 | | } |
1431 | | |
1432 | | } // namespace detail |
1433 | | |
1434 | | QuicCore::LegacyConnectionView & |
1435 | 40 | QuicCore::LegacyConnectionView::operator=(std::unique_ptr<QuicConnection> connection) { |
1436 | 40 | owner->set_legacy_connection(std::move(connection)); |
1437 | 40 | return *this; |
1438 | 40 | } |
1439 | | |
1440 | 272k | QuicConnection *QuicCore::LegacyConnectionView::get() const { |
1441 | 272k | if (owner == nullptr) { |
1442 | 32 | return nullptr; |
1443 | 32 | } |
1444 | 272k | auto *entry = owner->legacy_entry(); |
1445 | 272k | return entry == nullptr ? nullptr : entry->connection.get(); |
1446 | 272k | } |
1447 | | |
1448 | 268k | QuicConnection *QuicCore::LegacyConnectionView::operator->() const { |
1449 | 268k | return get(); |
1450 | 268k | } |
1451 | | |
1452 | 3.64k | QuicConnection &QuicCore::LegacyConnectionView::operator*() const { |
1453 | 3.64k | return *get(); |
1454 | 3.64k | } |
1455 | | |
1456 | 16 | QuicCore::LegacyConnectionView::operator bool() const { |
1457 | 16 | return get() != nullptr; |
1458 | 16 | } |
1459 | | |
1460 | 16 | bool QuicCore::LegacyConnectionView::operator==(std::nullptr_t) const { |
1461 | 16 | return get() == nullptr; |
1462 | 16 | } |
1463 | | |
1464 | 192 | bool QuicCore::LegacyConnectionView::operator!=(std::nullptr_t) const { |
1465 | 192 | return get() != nullptr; |
1466 | 192 | } |
1467 | | |
1468 | 388k | QuicCore::ConnectionEntry *QuicCore::legacy_entry() { |
1469 | 388k | if (!legacy_connection_handle_.has_value()) { |
1470 | 2.68k | return nullptr; |
1471 | 2.68k | } |
1472 | 385k | const auto it = connections_.find(*legacy_connection_handle_); |
1473 | 385k | if (it == connections_.end()) { |
1474 | 40 | return nullptr; |
1475 | 40 | } |
1476 | 385k | return &it->second; |
1477 | 385k | } |
1478 | | |
1479 | 2.25k | const QuicCore::ConnectionEntry *QuicCore::legacy_entry() const { |
1480 | 2.25k | if (!legacy_connection_handle_.has_value()) { |
1481 | 24 | return nullptr; |
1482 | 24 | } |
1483 | 2.23k | const auto it = connections_.find(*legacy_connection_handle_); |
1484 | 2.23k | if (it == connections_.end()) { |
1485 | 8 | return nullptr; |
1486 | 8 | } |
1487 | 2.22k | return &it->second; |
1488 | 2.23k | } |
1489 | | |
1490 | 59.0k | QuicCore::ConnectionEntry *QuicCore::ensure_legacy_entry() { |
1491 | 59.0k | if (auto *entry = legacy_entry()) { |
1492 | 56.3k | return entry; |
1493 | 56.3k | } |
1494 | 2.68k | if (!legacy_config_.has_value()) { |
1495 | 16 | return nullptr; |
1496 | 16 | } |
1497 | 2.66k | if (!legacy_connection_handle_.has_value()) { |
1498 | 2.66k | legacy_connection_handle_ = next_connection_handle_++; |
1499 | 2.66k | } |
1500 | | |
1501 | 2.66k | const auto handle = *legacy_connection_handle_; |
1502 | 2.66k | auto [it, inserted] = connections_.try_emplace(handle); |
1503 | 2.66k | (void)inserted; |
1504 | 2.66k | auto &entry = it->second; |
1505 | 2.66k | entry.handle = handle; |
1506 | 2.66k | entry.connection = std::make_unique<QuicConnection>(*legacy_config_); |
1507 | 2.66k | refresh_entry_wakeup(entry); |
1508 | 2.66k | return &it->second; |
1509 | 2.68k | } |
1510 | | |
1511 | 48 | void QuicCore::set_legacy_connection(std::unique_ptr<QuicConnection> connection) { |
1512 | 48 | if (!legacy_connection_handle_.has_value()) { |
1513 | 8 | legacy_connection_handle_ = next_connection_handle_++; |
1514 | 8 | } |
1515 | 48 | if (connection == nullptr) { |
1516 | 8 | connections_.erase(*legacy_connection_handle_); |
1517 | 8 | return; |
1518 | 8 | } |
1519 | | |
1520 | 40 | auto &entry = connections_[*legacy_connection_handle_]; |
1521 | 40 | entry = {}; |
1522 | 40 | entry.handle = *legacy_connection_handle_; |
1523 | 40 | entry.connection = std::move(connection); |
1524 | 40 | refresh_entry_wakeup(entry); |
1525 | 40 | } |
1526 | | |
1527 | 19.6k | std::string QuicCore::connection_id_key(std::span<const std::byte> connection_id) { |
1528 | 19.6k | if (connection_id.empty()) { |
1529 | 136 | return {}; |
1530 | 136 | } |
1531 | 19.4k | return std::string(reinterpret_cast<const char *>(connection_id.data()), connection_id.size()); |
1532 | 19.6k | } |
1533 | | |
1534 | | std::string |
1535 | 660 | QuicCore::stateless_reset_token_key(const std::array<std::byte, 16> &stateless_reset_token) { |
1536 | 660 | return std::string(reinterpret_cast<const char *>(stateless_reset_token.data()), |
1537 | 660 | stateless_reset_token.size()); |
1538 | 660 | } |
1539 | | |
1540 | | std::optional<QuicCore::ParsedEndpointDatagram> |
1541 | 13.8k | QuicCore::parse_endpoint_datagram(std::span<const std::byte> bytes, bool accept_greased_quic_bit) { |
1542 | 13.8k | if (bytes.empty()) { |
1543 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
1544 | | // # Endpoints MUST discard packets that are too small to be valid QUIC |
1545 | | // # packets. |
1546 | 104 | return std::nullopt; |
1547 | 104 | } |
1548 | | |
1549 | 13.7k | const auto first_byte = std::to_integer<std::uint8_t>(bytes.front()); |
1550 | 13.7k | if ((first_byte & 0x80u) == 0) { |
1551 | 12.7k | if ((first_byte & 0x40u) == 0 && !accept_greased_quic_bit) { |
1552 | | //= https://www.rfc-editor.org/rfc/rfc9287#section-3 |
1553 | | // # An endpoint that advertises the grease_quic_bit transport parameter |
1554 | | // # MUST accept packets with the QUIC Bit set to a value of 0. |
1555 | 16 | return std::nullopt; |
1556 | 16 | } |
1557 | 12.7k | if (bytes.size() < 1 + kEndpointConnectionIdLength) { |
1558 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
1559 | | // # Endpoints MUST discard packets that are too small to be valid |
1560 | | // # QUIC packets. |
1561 | 24 | return std::nullopt; |
1562 | 24 | } |
1563 | | |
1564 | 12.7k | return ParsedEndpointDatagram{ |
1565 | 12.7k | .kind = ParsedEndpointDatagram::Kind::short_header, |
1566 | 12.7k | .destination_connection_id = |
1567 | 12.7k | ConnectionId(bytes.begin() + 1, bytes.begin() + 1 + kEndpointConnectionIdLength), |
1568 | 12.7k | }; |
1569 | 12.7k | } |
1570 | | |
1571 | 984 | if ((first_byte & 0x40u) == 0 && !accept_greased_quic_bit) { |
1572 | | //= https://www.rfc-editor.org/rfc/rfc9287#section-3 |
1573 | | // # An endpoint that advertises the grease_quic_bit transport parameter |
1574 | | // # MUST accept packets with the QUIC Bit set to a value of 0. |
1575 | 8 | return std::nullopt; |
1576 | 8 | } |
1577 | 976 | if (bytes.size() < 7) { |
1578 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
1579 | | // # Endpoints MUST discard packets that are too small to be valid QUIC |
1580 | | // # packets. |
1581 | 16 | return std::nullopt; |
1582 | 16 | } |
1583 | | |
1584 | 960 | const auto version = |
1585 | 960 | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[1])) << 24) | |
1586 | 960 | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[2])) << 16) | |
1587 | 960 | (static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[3])) << 8) | |
1588 | 960 | static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(bytes[4])); |
1589 | 960 | if (version == kVersionNegotiationVersion) { |
1590 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
1591 | | // # Clients |
1592 | | // # MUST NOT send Version Negotiation packets and servers MUST ignore all |
1593 | | // # received Version Negotiation packets. |
1594 | 24 | return std::nullopt; |
1595 | 24 | } |
1596 | | |
1597 | 936 | std::size_t offset = 5; |
1598 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2 |
1599 | | // # In order to properly form a Version Negotiation packet, servers |
1600 | | // # SHOULD be able to read longer connection IDs from other QUIC versions. |
1601 | 936 | auto destination_connection_id_length = |
1602 | 936 | static_cast<std::size_t>(std::to_integer<std::uint8_t>(bytes[offset++])); |
1603 | 936 | if (offset + destination_connection_id_length + 1 > bytes.size()) { |
1604 | 8 | return std::nullopt; |
1605 | 8 | } |
1606 | 928 | ConnectionId destination_connection_id( |
1607 | 928 | bytes.begin() + static_cast<std::ptrdiff_t>(offset), |
1608 | 928 | bytes.begin() + static_cast<std::ptrdiff_t>(offset + destination_connection_id_length)); |
1609 | 928 | offset += destination_connection_id_length; |
1610 | | |
1611 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2 |
1612 | | // # In order to properly form a Version Negotiation packet, servers |
1613 | | // # SHOULD be able to read longer connection IDs from other QUIC versions. |
1614 | 928 | auto source_connection_id_length = |
1615 | 928 | static_cast<std::size_t>(std::to_integer<std::uint8_t>(bytes[offset++])); |
1616 | 928 | if (offset + source_connection_id_length > bytes.size()) { |
1617 | 8 | return std::nullopt; |
1618 | 8 | } |
1619 | 920 | ConnectionId source_connection_id( |
1620 | 920 | bytes.begin() + static_cast<std::ptrdiff_t>(offset), |
1621 | 920 | bytes.begin() + static_cast<std::ptrdiff_t>(offset + source_connection_id_length)); |
1622 | 920 | offset += source_connection_id_length; |
1623 | | |
1624 | 920 | if (!is_supported_quic_version(version)) { |
1625 | 36 | return ParsedEndpointDatagram{ |
1626 | 36 | .kind = ParsedEndpointDatagram::Kind::unsupported_version_long_header, |
1627 | 36 | .destination_connection_id = std::move(destination_connection_id), |
1628 | 36 | .source_connection_id = std::move(source_connection_id), |
1629 | 36 | .version = version, |
1630 | 36 | }; |
1631 | 36 | } |
1632 | | |
1633 | 884 | const auto type = static_cast<std::uint8_t>((first_byte >> 4) & 0x03u); |
1634 | 884 | std::vector<std::byte> token; |
1635 | 884 | if (is_initial_long_header_type(version, type)) { |
1636 | 596 | BufferReader reader(bytes.subspan(offset)); |
1637 | 596 | const auto token_length = decode_varint(reader); |
1638 | 596 | if (!token_length.has_value()) { |
1639 | 8 | return std::nullopt; |
1640 | 8 | } |
1641 | 588 | if (token_length.value().value > static_cast<std::uint64_t>(reader.remaining())) { |
1642 | 8 | return std::nullopt; |
1643 | 8 | } |
1644 | 580 | const auto token_bytes = |
1645 | 580 | reader.read_exact(static_cast<std::size_t>(token_length.value().value)).value(); |
1646 | 580 | token.assign(token_bytes.begin(), token_bytes.end()); |
1647 | 580 | } |
1648 | | |
1649 | 868 | auto kind = ParsedEndpointDatagram::Kind::supported_long_header; |
1650 | 868 | if (is_initial_long_header_type(version, type)) { |
1651 | 580 | kind = ParsedEndpointDatagram::Kind::supported_initial; |
1652 | 580 | } else if (is_zero_rtt_long_header_type(version, type)) { |
1653 | 92 | kind = ParsedEndpointDatagram::Kind::supported_zero_rtt; |
1654 | 92 | } |
1655 | | |
1656 | 868 | return ParsedEndpointDatagram{ |
1657 | 868 | .kind = kind, |
1658 | 868 | .destination_connection_id = std::move(destination_connection_id), |
1659 | 868 | .source_connection_id = std::move(source_connection_id), |
1660 | 868 | .version = version, |
1661 | 868 | .token = std::move(token), |
1662 | 868 | }; |
1663 | 884 | } |
1664 | | |
1665 | | COQUIC_NO_PROFILE std::vector<std::byte> QuicCore::make_endpoint_retry_token( |
1666 | | std::uint64_t sequence, const ParsedEndpointDatagram *parsed, |
1667 | | const ConnectionId *retry_source_connection_id, std::optional<QuicRouteHandle> route_handle, |
1668 | | std::span<const std::byte> address_validation_identity, QuicCoreTimePoint now) { |
1669 | | if (endpoint_config_.address_validation_token_secret.has_value() && parsed != nullptr && |
1670 | | retry_source_connection_id != nullptr) { |
1671 | | std::vector<std::byte> nonce(16, std::byte{0x00}); |
1672 | | fill_random_bytes(nonce, endpoint_random_); |
1673 | | nonce.back() = static_cast<std::byte>(std::to_integer<std::uint8_t>(nonce.back()) ^ |
1674 | | static_cast<std::uint8_t>(sequence & 0xffu)); |
1675 | | if (const auto token = seal_address_validation_token( |
1676 | | *endpoint_config_.address_validation_token_secret, |
1677 | | SelfContainedAddressValidationToken{ |
1678 | | .kind = kAddressValidationRetryTokenType, |
1679 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-4.1 |
1680 | | // # The server MAY encode the QUIC |
1681 | | // # version in its Retry token to validate that the client did not switch |
1682 | | // # versions, and drop the packet if it switched, to enforce client |
1683 | | // # compliance. |
1684 | | .version = parsed->version, |
1685 | | .route_handle = route_handle, |
1686 | | .address_validation_identity = std::vector<std::byte>( |
1687 | | address_validation_identity.begin(), address_validation_identity.end()), |
1688 | | .original_destination_connection_id = parsed->destination_connection_id, |
1689 | | .retry_source_connection_id = *retry_source_connection_id, |
1690 | | .nonce = std::move(nonce), |
1691 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.3 |
1692 | | // # Servers SHOULD provide mitigations for this attack by |
1693 | | // # limiting the usage and lifetime of address validation |
1694 | | // # tokens; see Section 8.1.3. |
1695 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1696 | | // # Servers SHOULD ensure that tokens sent in Retry packets |
1697 | | // # are only accepted for a short time, as they are returned |
1698 | | // # immediately by clients. |
1699 | | .expires_at = now + kRetryTokenLifetime, |
1700 | | })) { |
1701 | | return *token; |
1702 | | } |
1703 | | } |
1704 | | |
1705 | | std::vector<std::byte> token(16, std::byte{0x00}); |
1706 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1707 | | // # An address validation token MUST be difficult to guess. |
1708 | | fill_random_bytes(token, endpoint_random_); |
1709 | | token.back() = static_cast<std::byte>(std::to_integer<std::uint8_t>(token.back()) ^ |
1710 | | static_cast<std::uint8_t>(sequence & 0xffu)); |
1711 | | return token; |
1712 | | } |
1713 | | |
1714 | | COQUIC_NO_PROFILE std::vector<std::byte> QuicCore::make_endpoint_new_token( |
1715 | | std::uint64_t sequence, // NOLINT(bugprone-easily-swappable-parameters) |
1716 | | std::uint32_t version, std::optional<QuicRouteHandle> route_handle, |
1717 | | std::span<const std::byte> address_validation_identity, QuicCoreTimePoint now) { |
1718 | | if (endpoint_config_.address_validation_token_secret.has_value()) { |
1719 | | std::vector<std::byte> nonce(16, std::byte{0x00}); |
1720 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1721 | | // # A server MUST ensure that every NEW_TOKEN frame it sends is unique |
1722 | | // # across all clients, with the exception of those sent to repair |
1723 | | // # losses of previously sent NEW_TOKEN frames. |
1724 | | fill_random_bytes(nonce, endpoint_random_); |
1725 | | nonce.back() = static_cast<std::byte>(std::to_integer<std::uint8_t>(nonce.back()) ^ |
1726 | | static_cast<std::uint8_t>(sequence & 0xffu)); |
1727 | | if (const auto token = seal_address_validation_token( |
1728 | | *endpoint_config_.address_validation_token_secret, |
1729 | | SelfContainedAddressValidationToken{ |
1730 | | .kind = kAddressValidationNewTokenType, |
1731 | | .version = version, |
1732 | | .route_handle = route_handle, |
1733 | | .address_validation_identity = std::vector<std::byte>( |
1734 | | address_validation_identity.begin(), address_validation_identity.end()), |
1735 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1736 | | // # A token issued with NEW_TOKEN MUST NOT include |
1737 | | // # information that would allow values to be linked by an |
1738 | | // # observer to the connection on which it was issued. |
1739 | | .nonce = std::move(nonce), |
1740 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.3 |
1741 | | // # Servers SHOULD provide mitigations for this attack by |
1742 | | // # limiting the usage and lifetime of address validation |
1743 | | // # tokens; see Section 8.1.3. |
1744 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1745 | | // # Thus, a token SHOULD have an expiration time, which |
1746 | | // # could be either an explicit expiration time or an |
1747 | | // # issued timestamp that can be used to dynamically |
1748 | | // # calculate the expiration time. |
1749 | | .expires_at = now + kNewTokenLifetime, |
1750 | | })) { |
1751 | | return *token; |
1752 | | } |
1753 | | } |
1754 | | |
1755 | | std::vector<std::byte> token(24, std::byte{0x00}); |
1756 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1757 | | // # A token issued with NEW_TOKEN MUST NOT include information that would |
1758 | | // # allow values to be linked by an observer to the connection on which it |
1759 | | // # was issued. |
1760 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1761 | | // # A server MUST ensure that every NEW_TOKEN frame it sends is unique |
1762 | | // # across all clients, with the exception of those sent to repair losses |
1763 | | // # of previously sent NEW_TOKEN frames. |
1764 | | fill_random_bytes(token, endpoint_random_); |
1765 | | token.front() = static_cast<std::byte>(std::to_integer<std::uint8_t>(token.front()) ^ 0x4eu); |
1766 | | token.back() = static_cast<std::byte>(std::to_integer<std::uint8_t>(token.back()) ^ |
1767 | | static_cast<std::uint8_t>(sequence & 0xffu)); |
1768 | | return token; |
1769 | | } |
1770 | | |
1771 | | COQUIC_NO_PROFILE std::vector<std::byte> |
1772 | | QuicCore::make_initial_transport_close_packet_bytes(const QuicCore::ParsedEndpointDatagram &parsed, |
1773 | | std::uint64_t error_code, |
1774 | | ConnectionId source_connection_id) { |
1775 | | if (!parsed.source_connection_id.has_value()) { |
1776 | | return {}; |
1777 | | } |
1778 | | |
1779 | | auto client_initial_destination_connection_id = parsed.destination_connection_id; |
1780 | | if (const auto token_metadata = peek_self_contained_address_validation_token(parsed.token); |
1781 | | token_metadata.has_value() && token_metadata->kind == kAddressValidationRetryTokenType && |
1782 | | !token_metadata->original_destination_connection_id.empty()) { |
1783 | | client_initial_destination_connection_id = |
1784 | | token_metadata->original_destination_connection_id; |
1785 | | } |
1786 | | const auto encoded = serialize_protected_datagram( |
1787 | | std::array<ProtectedPacket, 1>{ |
1788 | | ProtectedInitialPacket{ |
1789 | | .version = parsed.version, |
1790 | | .destination_connection_id = parsed.source_connection_id.value(), |
1791 | | .source_connection_id = std::move(source_connection_id), |
1792 | | .packet_number_length = 2, |
1793 | | .packet_number = 0, |
1794 | | .frames = |
1795 | | { |
1796 | | TransportConnectionCloseFrame{ |
1797 | | .error_code = error_code, |
1798 | | }, |
1799 | | }, |
1800 | | }, |
1801 | | }, |
1802 | | SerializeProtectionContext{ |
1803 | | .local_role = EndpointRole::server, |
1804 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-7.2 |
1805 | | // # The Destination Connection ID field from the first Initial |
1806 | | // # packet sent by a client is used to determine packet protection |
1807 | | // # keys for Initial packets. |
1808 | | .client_initial_destination_connection_id = client_initial_destination_connection_id, |
1809 | | }); |
1810 | | if (!encoded.has_value()) { |
1811 | | return {}; |
1812 | | } |
1813 | | return encoded.value(); |
1814 | | } |
1815 | | |
1816 | | COQUIC_NO_PROFILE std::vector<std::byte> |
1817 | | QuicCore::make_invalid_retry_token_close_packet_bytes(const ParsedEndpointDatagram &parsed) { |
1818 | | return make_initial_transport_close_packet_bytes( |
1819 | | parsed, static_cast<std::uint64_t>(QuicTransportErrorCode::invalid_token), |
1820 | | make_endpoint_connection_id(kServerConnectionIdPrefix, |
1821 | | next_server_connection_id_sequence_++, endpoint_random_)); |
1822 | | } |
1823 | | |
1824 | | COQUIC_NO_PROFILE std::vector<std::byte> |
1825 | | QuicCore::make_connection_refused_close_packet_bytes(const ParsedEndpointDatagram &parsed) { |
1826 | | return make_initial_transport_close_packet_bytes( |
1827 | | parsed, static_cast<std::uint64_t>(QuicTransportErrorCode::connection_refused), |
1828 | | make_endpoint_connection_id(kServerConnectionIdPrefix, |
1829 | | next_server_connection_id_sequence_++, endpoint_random_)); |
1830 | | } |
1831 | | |
1832 | | COQUIC_NO_PROFILE std::optional<QuicCore::PendingRetryToken> QuicCore::take_retry_context( |
1833 | | const ParsedEndpointDatagram &parsed, const std::optional<QuicRouteHandle> &route_handle, |
1834 | | QuicCoreTimePoint now, std::span<const std::byte> address_validation_identity) { |
1835 | | purge_expired_consumed_address_validation_tokens(consumed_address_validation_tokens_, now); |
1836 | | persist_consumed_address_validation_tokens(); |
1837 | | const auto it = retry_tokens_.find(connection_id_key(parsed.token)); |
1838 | | if (it == retry_tokens_.end()) { |
1839 | | if (!endpoint_config_.address_validation_token_secret.has_value()) { |
1840 | | return std::nullopt; |
1841 | | } |
1842 | | |
1843 | | auto metadata = open_address_validation_token( |
1844 | | *endpoint_config_.address_validation_token_secret, parsed.token); |
1845 | | if (!metadata.has_value()) { |
1846 | | return std::nullopt; |
1847 | | } |
1848 | | |
1849 | | if (metadata->kind != kAddressValidationRetryTokenType || |
1850 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.3 |
1851 | | // # Servers SHOULD provide mitigations for this attack by limiting |
1852 | | // # the usage and lifetime of address validation tokens; see |
1853 | | // # Section 8.1.3. |
1854 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1855 | | // # Servers SHOULD ensure that tokens sent in Retry packets are only |
1856 | | // # accepted for a short time, as they are returned immediately by clients. |
1857 | | token_time_expired(metadata->expires_at, now) || |
1858 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1859 | | // # To protect against such attacks, servers MUST ensure that replay |
1860 | | // # of tokens is prevented or limited. |
1861 | | address_validation_token_consumed(parsed.token) || |
1862 | | !token_route_matches(metadata->route_handle, route_handle) || |
1863 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1864 | | // # Tokens sent in Retry packets SHOULD include information that |
1865 | | // # allows the server to verify that the source IP address and port |
1866 | | // # in client packets remain constant. |
1867 | | !token_identity_matches(metadata->address_validation_identity, |
1868 | | address_validation_identity) || |
1869 | | parsed.destination_connection_id != metadata->retry_source_connection_id || |
1870 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-4.1 |
1871 | | // # The server MAY encode the QUIC |
1872 | | // # version in its Retry token to validate that the client did not switch |
1873 | | // # versions, and drop the packet if it switched, to enforce client |
1874 | | // # compliance. |
1875 | | parsed.version != metadata->version) { |
1876 | | return std::nullopt; |
1877 | | } |
1878 | | |
1879 | | mark_address_validation_token_consumed(parsed.token, metadata->expires_at); |
1880 | | return PendingRetryToken{ |
1881 | | .original_destination_connection_id = metadata->original_destination_connection_id, |
1882 | | .retry_source_connection_id = metadata->retry_source_connection_id, |
1883 | | .original_version = metadata->version, |
1884 | | .token = parsed.token, |
1885 | | .route_handle = metadata->route_handle, |
1886 | | .address_validation_identity = metadata->address_validation_identity, |
1887 | | .expires_at = metadata->expires_at, |
1888 | | }; |
1889 | | } |
1890 | | |
1891 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1892 | | // # To protect against such attacks, servers MUST ensure that replay of |
1893 | | // # tokens is prevented or limited. |
1894 | | if (address_validation_token_consumed(parsed.token)) { |
1895 | | return std::nullopt; |
1896 | | } |
1897 | | |
1898 | | const auto &pending = it->second; |
1899 | | if (token_time_expired(pending.expires_at, now)) { |
1900 | | retry_tokens_.erase(it); |
1901 | | return std::nullopt; |
1902 | | } |
1903 | | if (pending.route_handle != route_handle || |
1904 | | !token_identity_matches(pending.address_validation_identity, address_validation_identity) || |
1905 | | parsed.destination_connection_id != pending.retry_source_connection_id || |
1906 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-4.1 |
1907 | | // # The server MAY encode the QUIC |
1908 | | // # version in its Retry token to validate that the client did not switch |
1909 | | // # versions, and drop the packet if it switched, to enforce client |
1910 | | // # compliance. |
1911 | | parsed.version != pending.original_version) { |
1912 | | return std::nullopt; |
1913 | | } |
1914 | | |
1915 | | auto retry_context = pending; |
1916 | | retry_tokens_.erase(it); |
1917 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1918 | | // # To protect against such attacks, servers MUST ensure that replay of |
1919 | | // # tokens is prevented or limited. |
1920 | | mark_address_validation_token_consumed(parsed.token, retry_context.expires_at); |
1921 | | return retry_context; |
1922 | | } |
1923 | | |
1924 | | COQUIC_NO_PROFILE std::optional<QuicCore::StoredEndpointNewToken> QuicCore::take_new_token_context( |
1925 | | const ParsedEndpointDatagram &parsed, const std::optional<QuicRouteHandle> &route_handle, |
1926 | | QuicCoreTimePoint now, std::span<const std::byte> address_validation_identity) { |
1927 | | purge_expired_consumed_address_validation_tokens(consumed_address_validation_tokens_, now); |
1928 | | persist_consumed_address_validation_tokens(); |
1929 | | const auto token_key = connection_id_key(parsed.token); |
1930 | | const auto it = new_tokens_.find(token_key); |
1931 | | if (it == new_tokens_.end()) { |
1932 | | if (!endpoint_config_.address_validation_token_secret.has_value()) { |
1933 | | return std::nullopt; |
1934 | | } |
1935 | | |
1936 | | std::optional<SelfContainedAddressValidationToken> metadata; |
1937 | | if (auto opened = open_address_validation_token( |
1938 | | *endpoint_config_.address_validation_token_secret, parsed.token)) { |
1939 | | metadata = std::move(opened); |
1940 | | } else { |
1941 | | for (const auto &previous_secret : |
1942 | | endpoint_config_.previous_address_validation_token_secrets) { |
1943 | | if (auto opened_previous = |
1944 | | open_address_validation_token(previous_secret, parsed.token)) { |
1945 | | metadata = std::move(opened_previous); |
1946 | | break; |
1947 | | } |
1948 | | } |
1949 | | } |
1950 | | |
1951 | | if (!metadata.has_value() || metadata->kind != kAddressValidationNewTokenType || |
1952 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.3 |
1953 | | // # Servers SHOULD provide mitigations for this attack by limiting |
1954 | | // # the usage and lifetime of address validation tokens; see |
1955 | | // # Section 8.1.3. |
1956 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
1957 | | // # Thus, a token SHOULD have an expiration time, which could be |
1958 | | // # either an explicit expiration time or an issued timestamp that |
1959 | | // # can be used to dynamically calculate the expiration time. |
1960 | | token_time_expired(metadata->expires_at, now) || |
1961 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1962 | | // # Tokens that are provided in NEW_TOKEN frames (Section 19.7) need |
1963 | | // # to be valid for longer but SHOULD NOT be accepted multiple times. |
1964 | | address_validation_token_consumed(parsed.token) || |
1965 | | !token_route_matches(metadata->route_handle, route_handle) || |
1966 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1967 | | // # Tokens sent in NEW_TOKEN frames MUST include information that |
1968 | | // # allows the server to verify that the client IP address has not |
1969 | | // # changed from when the token was issued. |
1970 | | !token_identity_matches(metadata->address_validation_identity, |
1971 | | address_validation_identity) || |
1972 | | metadata->version != parsed.version) { |
1973 | | return std::nullopt; |
1974 | | } |
1975 | | |
1976 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1977 | | // # Tokens that are provided in NEW_TOKEN frames (Section 19.7) need to |
1978 | | // # be valid for longer but SHOULD NOT be accepted multiple times. |
1979 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
1980 | | // # Servers are encouraged to allow tokens to be used only once, if |
1981 | | // # possible; tokens MAY include additional information about clients to |
1982 | | // # further narrow applicability or reuse. |
1983 | | mark_address_validation_token_consumed(parsed.token, metadata->expires_at); |
1984 | | return StoredEndpointNewToken{ |
1985 | | .token = parsed.token, |
1986 | | .route_handle = metadata->route_handle, |
1987 | | .address_validation_identity = metadata->address_validation_identity, |
1988 | | .version = metadata->version, |
1989 | | .expires_at = metadata->expires_at, |
1990 | | .used = true, |
1991 | | }; |
1992 | | } |
1993 | | |
1994 | | if (address_validation_token_consumed(parsed.token)) { |
1995 | | return std::nullopt; |
1996 | | } |
1997 | | |
1998 | | auto token = it->second; |
1999 | | if (token.used || token.version != parsed.version || |
2000 | | (token.route_handle.has_value() && token.route_handle != route_handle) || |
2001 | | !token_identity_matches(token.address_validation_identity, address_validation_identity) || |
2002 | | token_time_expired(token.expires_at, now)) { |
2003 | | if (token_time_expired(token.expires_at, now)) { |
2004 | | new_tokens_.erase(it); |
2005 | | } |
2006 | | return std::nullopt; |
2007 | | } |
2008 | | |
2009 | | token.used = true; |
2010 | | new_tokens_.erase(it); |
2011 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.4 |
2012 | | // # Servers are encouraged to allow tokens to be used only once, if |
2013 | | // # possible; tokens MAY include additional information about clients to |
2014 | | // # further narrow applicability or reuse. |
2015 | | mark_address_validation_token_consumed(parsed.token, token.expires_at); |
2016 | | return token; |
2017 | | } |
2018 | | |
2019 | | COQUIC_NO_PROFILE QuicCore::AddressValidationTokenClassification |
2020 | | QuicCore::classify_address_validation_token(const ParsedEndpointDatagram &parsed) const { |
2021 | | if (parsed.token.empty()) { |
2022 | | return AddressValidationTokenClassification::missing; |
2023 | | } |
2024 | | |
2025 | | const auto token_key = connection_id_key(parsed.token); |
2026 | | if (new_tokens_.contains(token_key)) { |
2027 | | return AddressValidationTokenClassification::new_token; |
2028 | | } |
2029 | | if (retry_tokens_.contains(token_key)) { |
2030 | | return AddressValidationTokenClassification::retry; |
2031 | | } |
2032 | | if (const auto kind = peek_self_contained_address_validation_token_kind(parsed.token)) { |
2033 | | if (*kind == AddressValidationTokenKind::new_token) { |
2034 | | return AddressValidationTokenClassification::new_token; |
2035 | | } |
2036 | | if (*kind == AddressValidationTokenKind::retry) { |
2037 | | return AddressValidationTokenClassification::retry; |
2038 | | } |
2039 | | } |
2040 | | |
2041 | | return AddressValidationTokenClassification::unknown; |
2042 | | } |
2043 | | |
2044 | | COQUIC_NO_PROFILE void QuicCore::maybe_queue_server_new_token(ConnectionEntry &entry, |
2045 | | QuicCoreTimePoint now) { |
2046 | | if (endpoint_config_.role != EndpointRole::server || entry.connection == nullptr || |
2047 | | !entry.connection->is_handshake_complete() || !entry.connection->peer_address_validated_) { |
2048 | | return; |
2049 | | } |
2050 | | |
2051 | | const auto route_handle = route_handle_for_path(entry, entry.connection->current_send_path_id_); |
2052 | | if (!route_handle.has_value()) { |
2053 | | return; |
2054 | | } |
2055 | | |
2056 | | if (std::find(entry.new_token_issued_routes.begin(), entry.new_token_issued_routes.end(), |
2057 | | *route_handle) != entry.new_token_issued_routes.end()) { |
2058 | | return; |
2059 | | } |
2060 | | |
2061 | | const auto path_id = entry.connection->current_send_path_id_; |
2062 | | const auto identity_it = path_id.has_value() |
2063 | | ? entry.address_validation_identity_by_path_id.find(*path_id) |
2064 | | : entry.address_validation_identity_by_path_id.end(); |
2065 | | const auto address_validation_identity = |
2066 | | identity_it != entry.address_validation_identity_by_path_id.end() |
2067 | | ? std::span<const std::byte>(identity_it->second) |
2068 | | : std::span<const std::byte>{}; |
2069 | | |
2070 | | auto sequence = next_server_connection_id_sequence_++; |
2071 | | auto token = make_endpoint_new_token(sequence, entry.connection->current_version_, route_handle, |
2072 | | address_validation_identity, now); |
2073 | | if (token.empty()) { |
2074 | | return; |
2075 | | } |
2076 | | |
2077 | | new_tokens_.insert_or_assign(connection_id_key(token), |
2078 | | StoredEndpointNewToken{ |
2079 | | .token = token, |
2080 | | .route_handle = route_handle, |
2081 | | .address_validation_identity = |
2082 | | std::vector<std::byte>(address_validation_identity.begin(), |
2083 | | address_validation_identity.end()), |
2084 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-5 |
2085 | | // # When a connection |
2086 | | // # includes compatible version negotiation, any issued server |
2087 | | // # tokens are considered to originate from the |
2088 | | // # negotiated version, not the original one. |
2089 | | .version = entry.connection->current_version_, |
2090 | | .expires_at = now + kNewTokenLifetime, |
2091 | | .used = false, |
2092 | | }); |
2093 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-9.3 |
2094 | | // # After verifying a new client address, the server SHOULD send new |
2095 | | // # address validation tokens (Section 8) to the client. |
2096 | | entry.connection->queue_new_token(std::move(token)); |
2097 | | entry.new_token_issued_routes.push_back(*route_handle); |
2098 | | } |
2099 | | |
2100 | | COQUIC_NO_PROFILE void |
2101 | | QuicCore::drain_queued_server_new_token(ConnectionEntry &entry, QuicCoreResult &drained, |
2102 | | QuicCoreTimePoint now, |
2103 | | QuicCoreSendDatagramSink *send_sink) { |
2104 | | maybe_queue_server_new_token(entry, now); |
2105 | | if (!entry.connection->has_sendable_datagram(now)) { |
2106 | | return; |
2107 | | } |
2108 | | auto token_drained = drain_connection_effects(entry.handle, entry.default_route_handle, |
2109 | | entry.route_handle_by_path_id, *entry.connection, |
2110 | | now, /*continue_paced_burst=*/false, send_sink); |
2111 | | append_result(drained, std::move(token_drained)); |
2112 | | } |
2113 | | |
2114 | | COQUIC_NO_PROFILE void QuicCore::remember_client_new_tokens(ConnectionEntry &entry, |
2115 | | const QuicCoreResult &result) { |
2116 | | if (endpoint_config_.role != EndpointRole::client || entry.connection == nullptr) { |
2117 | | return; |
2118 | | } |
2119 | | |
2120 | | for (const auto &effect : result.effects) { |
2121 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2122 | | // # The client MUST NOT use the token provided in a Retry for future |
2123 | | // # connections. |
2124 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2125 | | // # In comparison, a token obtained in a Retry packet MUST be used |
2126 | | // # immediately during the connection attempt and cannot be used in |
2127 | | // # subsequent connection attempts. |
2128 | | const auto *new_token = std::get_if<QuicCoreNewTokenAvailable>(&effect); |
2129 | | if (new_token == nullptr || new_token->token.empty() || |
2130 | | new_token->connection != entry.handle) { |
2131 | | continue; |
2132 | | } |
2133 | | |
2134 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2135 | | // # A client MAY use a token from any previous connection to that server. |
2136 | | const bool already_stored = |
2137 | 8 | std::ranges::any_of(client_new_tokens_, [&](const ClientStoredNewToken &stored) { |
2138 | 8 | return stored.server_name == entry.connection->config_.server_name && |
2139 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-5 |
2140 | | // # Clients MUST NOT use a |
2141 | | // # session ticket or token from a QUIC version 1 connection to initiate |
2142 | | // # a QUIC version 2 connection, and vice versa. |
2143 | 8 | stored.version == entry.connection->current_version_ && |
2144 | 8 | stored.token == new_token->token; |
2145 | 8 | }); |
2146 | | if (already_stored) { |
2147 | | continue; |
2148 | | } |
2149 | | |
2150 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2151 | | // # A server MAY provide clients with an address validation token |
2152 | | // # during one connection that can be used on a subsequent connection. |
2153 | | client_new_tokens_.push_back(ClientStoredNewToken{ |
2154 | | .server_name = entry.connection->config_.server_name, |
2155 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-5 |
2156 | | // # When a connection |
2157 | | // # includes compatible version negotiation, any issued server tokens are |
2158 | | // # considered to originate from the negotiated version, not the original |
2159 | | // # one. |
2160 | | .version = entry.connection->current_version_, |
2161 | | .token = new_token->token, |
2162 | | .used = false, |
2163 | | }); |
2164 | | } |
2165 | | } |
2166 | | |
2167 | | std::optional<std::vector<std::byte>> COQUIC_NO_PROFILE |
2168 | | QuicCore::take_client_new_token_for_open(const QuicCoreClientConnectionConfig &connection) { |
2169 | | for (auto it = client_new_tokens_.rbegin(); it != client_new_tokens_.rend(); ++it) { |
2170 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2171 | | // # When connecting to a server for which the client retains an |
2172 | | // # applicable and unused token, it SHOULD include that token |
2173 | | // # in the Token field of its Initial packet. |
2174 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2175 | | // # A client MUST NOT include a token that is not applicable to the |
2176 | | // # server that it is connecting to, unless the client has the |
2177 | | // # knowledge that the server that issued the token and the server the |
2178 | | // # client is connecting to are jointly managing the tokens. |
2179 | | if (it->used || it->server_name != connection.server_name || |
2180 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-5 |
2181 | | // # Clients MUST NOT use a |
2182 | | // # session ticket or token from a QUIC version 1 connection to initiate |
2183 | | // # a QUIC version 2 connection, and vice versa. |
2184 | | it->version != connection.initial_version || it->token.empty()) { |
2185 | | continue; |
2186 | | } |
2187 | | |
2188 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2189 | | // # Clients MAY use tokens obtained on one connection for any |
2190 | | // # connection attempt using the same version. |
2191 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
2192 | | // # A client SHOULD NOT reuse a token from a NEW_TOKEN frame for |
2193 | | // # different connection attempts. |
2194 | | it->used = true; |
2195 | | return it->token; |
2196 | | } |
2197 | | return std::nullopt; |
2198 | | } |
2199 | | |
2200 | | std::optional<QuicCoreResult> |
2201 | | COQUIC_NO_PROFILE QuicCore::maybe_process_client_endpoint_version_negotiation( |
2202 | | ConnectionEntry &entry, std::span<const std::byte> inbound_payload, |
2203 | | const std::optional<QuicRouteHandle> &route_handle, QuicPathId path_id, |
2204 | | QuicCoreTimePoint now, QuicCoreSendDatagramSink *send_sink) { |
2205 | | if (endpoint_config_.role != EndpointRole::client || entry.connection == nullptr || |
2206 | | entry.connection->is_handshake_complete() || |
2207 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.2 |
2208 | | // # A client MUST discard any Version Negotiation packet if it has |
2209 | | // # received and successfully processed any other packet, including an |
2210 | | // # earlier Version Negotiation packet. |
2211 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-4 |
2212 | | // # A client that makes a connection |
2213 | | // # attempt based on information received from a Version Negotiation |
2214 | | // # packet MUST ignore any Version Negotiation packets it receives in |
2215 | | // # response to that connection attempt. |
2216 | | entry.connection->has_processed_peer_packet() || |
2217 | | entry.connection->config_.reacted_to_version_negotiation) { |
2218 | | return std::nullopt; |
2219 | | } |
2220 | | |
2221 | | const auto version_negotiation = parse_version_negotiation_packet(inbound_payload); |
2222 | | if (!version_negotiation.has_value()) { |
2223 | | return std::nullopt; |
2224 | | } |
2225 | | |
2226 | | auto config = entry.connection->config_; |
2227 | | const bool valid_destination_connection_id = |
2228 | | version_negotiation->destination_connection_id == config.source_connection_id; |
2229 | | const bool valid_source_connection_id = |
2230 | | version_negotiation->source_connection_id == config.initial_destination_connection_id; |
2231 | | const bool echoes_original_version = |
2232 | | std::find(version_negotiation->supported_versions.begin(), |
2233 | | version_negotiation->supported_versions.end(), |
2234 | | config.original_version) != version_negotiation->supported_versions.end(); |
2235 | | if (!valid_destination_connection_id || !valid_source_connection_id) { |
2236 | | return std::nullopt; |
2237 | | } |
2238 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.2 |
2239 | | // # A client MUST discard a Version Negotiation packet that |
2240 | | // # lists the QUIC version selected by the client. |
2241 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-4 |
2242 | | // # Clients MUST ignore any received Version Negotiation packets that |
2243 | | // # contain the Original Version. |
2244 | | if (echoes_original_version) { |
2245 | | return QuicCoreResult{}; |
2246 | | } |
2247 | | |
2248 | | for (const auto supported_version : config.supported_versions) { |
2249 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
2250 | | // # Upon receiving the Version Negotiation packet, the client SHALL |
2251 | | // # search for a version it supports in the list provided by the server. |
2252 | | if (std::find(version_negotiation->supported_versions.begin(), |
2253 | | version_negotiation->supported_versions.end(), |
2254 | | supported_version) == version_negotiation->supported_versions.end()) { |
2255 | | continue; |
2256 | | } |
2257 | | |
2258 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
2259 | | // # Otherwise, it SHALL select a mutually supported version and send a |
2260 | | // # new first flight with that version -- this version is now the |
2261 | | // # Negotiated Version. |
2262 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-6 |
2263 | | // # When incompatible version negotiation is in use, the second |
2264 | | // # connection that is created in response to the received Version |
2265 | | // # Negotiation packet MUST restart its application-layer protocol |
2266 | | // # negotiation process without taking into account the Original Version. |
2267 | | config.initial_version = supported_version; |
2268 | | config.reacted_to_version_negotiation = true; |
2269 | | erase_endpoint_connection_routes(entry); |
2270 | | entry.connection = std::make_unique<QuicConnection>(std::move(config)); |
2271 | | entry.active_connection_id_keys.clear(); |
2272 | | entry.local_stateless_reset_connection_id_keys.clear(); |
2273 | | entry.peer_stateless_reset_token_keys.clear(); |
2274 | | entry.initial_destination_connection_id_key.reset(); |
2275 | | entry.endpoint_route_generation = 0; |
2276 | | entry.default_route_handle = route_handle; |
2277 | | if (route_handle.has_value()) { |
2278 | | entry.path_id_by_route_handle[*route_handle] = path_id; |
2279 | | entry.route_handle_by_path_id[path_id] = *route_handle; |
2280 | | } else { |
2281 | | entry.route_handle_by_path_id.erase(path_id); |
2282 | | } |
2283 | | entry.connection->last_inbound_path_id_ = path_id; |
2284 | | entry.connection->current_send_path_id_ = path_id; |
2285 | | entry.connection->ensure_path_state(path_id).is_current_send_path = true; |
2286 | | remember_path_address_family( |
2287 | | entry, path_id, |
2288 | | route_address_family_from_identity(current_address_validation_identity(entry))); |
2289 | | entry.connection->start(now); |
2290 | | |
2291 | | auto result = drain_connection_effects(entry.handle, entry.default_route_handle, |
2292 | | entry.route_handle_by_path_id, *entry.connection, |
2293 | | now, /*continue_paced_burst=*/false, send_sink); |
2294 | | remember_client_new_tokens(entry, result); |
2295 | | note_send_continuation(entry, result, now); |
2296 | | refresh_server_connection_routes(entry); |
2297 | | return result; |
2298 | | } |
2299 | | |
2300 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
2301 | | // # If it doesn't find one, it SHALL abort the connection attempt. |
2302 | | return QuicCoreResult{}; |
2303 | | } |
2304 | | |
2305 | | std::optional<QuicConnectionHandle> |
2306 | | COQUIC_NO_PROFILE QuicCore::detect_stateless_reset(std::span<const std::byte> bytes) const { |
2307 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2308 | | // # However, endpoints MUST treat any packet ending in a valid stateless |
2309 | | // # reset token as a Stateless Reset, as other QUIC versions might allow |
2310 | | // # the use of a long header. |
2311 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
2312 | | // # If the last 16 bytes of the datagram are identical in value to a |
2313 | | // # stateless reset token, the endpoint MUST enter the draining period and |
2314 | | // # not send any further packets on this connection. |
2315 | | if (bytes.size() < kMinimumStatelessResetDatagramSize) { |
2316 | | return std::nullopt; |
2317 | | } |
2318 | | |
2319 | | std::array<std::byte, kStatelessResetTokenLength> token{}; |
2320 | | std::copy_n(bytes.end() - static_cast<std::ptrdiff_t>(token.size()), token.size(), |
2321 | | token.begin()); |
2322 | | for (const auto &[key, route] : peer_stateless_reset_tokens_) { |
2323 | | if (key.size() != token.size()) { |
2324 | | continue; |
2325 | | } |
2326 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
2327 | | // # When comparing a datagram to stateless reset token values, endpoints |
2328 | | // # MUST perform the comparison without leaking information about the |
2329 | | // # value of the token. |
2330 | | if (CRYPTO_memcmp(key.data(), token.data(), token.size()) == 0) { |
2331 | | return route.owner; |
2332 | | } |
2333 | | } |
2334 | | return std::nullopt; |
2335 | | } |
2336 | | |
2337 | | COQUIC_NO_PROFILE std::optional<QuicCoreSendDatagram> |
2338 | | QuicCore::make_stateless_reset_for_unknown_cid(const ParsedEndpointDatagram &parsed, |
2339 | | std::span<const std::byte> inbound_bytes, |
2340 | | const std::optional<QuicRouteHandle> &route_handle, |
2341 | | QuicCoreTimePoint now) { |
2342 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2343 | | // # An endpoint MAY send a Stateless Reset in response to receiving a |
2344 | | // # packet that it cannot associate with an active connection. |
2345 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2346 | | // # Endpoints MUST send Stateless Resets formatted as a packet with a |
2347 | | // # short header. |
2348 | | if (parsed.kind != ParsedEndpointDatagram::Kind::short_header || |
2349 | | inbound_bytes.size() < kMinimumStatelessResetDatagramSize) { |
2350 | | return std::nullopt; |
2351 | | } |
2352 | | |
2353 | | purge_expired_local_stateless_reset_tokens(now); |
2354 | | |
2355 | | const auto token_it = local_stateless_reset_tokens_by_cid_.find( |
2356 | | connection_id_key(parsed.destination_connection_id)); |
2357 | | std::optional<LocalStatelessResetTokenRoute> derived_token_route; |
2358 | | if (token_it == local_stateless_reset_tokens_by_cid_.end() && |
2359 | | endpoint_config_.stateless_reset_secret.has_value() && |
2360 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.2 |
2361 | | // # An endpoint that uses this design MUST either use the same |
2362 | | // # connection ID length for all connections or encode the length of |
2363 | | // # the connection ID such that it can be recovered without state. |
2364 | | parsed.destination_connection_id.size() == kEndpointConnectionIdLength && |
2365 | | parsed.destination_connection_id.front() == kServerConnectionIdPrefix) { |
2366 | | if (const auto token = derive_stateless_reset_token( |
2367 | | *endpoint_config_.stateless_reset_secret, parsed.destination_connection_id, |
2368 | | /*sequence_number=*/0)) { |
2369 | | derived_token_route = LocalStatelessResetTokenRoute{ |
2370 | | .owner = 0, |
2371 | | .stateless_reset_token = *token, |
2372 | | }; |
2373 | | } |
2374 | | } |
2375 | | const auto *token_route = token_it != local_stateless_reset_tokens_by_cid_.end() |
2376 | | ? &token_it->second |
2377 | | : derived_token_route ? &*derived_token_route |
2378 | | : nullptr; |
2379 | | if (token_route == nullptr) { |
2380 | | return std::nullopt; |
2381 | | } |
2382 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.3 |
2383 | | // # An endpoint MUST ensure that every Stateless Reset that it sends is |
2384 | | // # smaller than the packet that triggered it, unless it maintains state |
2385 | | // # sufficient to prevent looping. |
2386 | | if (inbound_bytes.size() <= kMinimumStatelessResetDatagramSize) { |
2387 | | return std::nullopt; |
2388 | | } |
2389 | | |
2390 | | std::size_t reset_size = inbound_bytes.size() <= 43 |
2391 | | ? inbound_bytes.size() - 1u |
2392 | | : std::min<std::size_t>(inbound_bytes.size() - 1u, 64u); |
2393 | | reset_size = std::max(reset_size, kMinimumStatelessResetDatagramSize); |
2394 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2395 | | // # An endpoint that sends a Stateless Reset in response to a packet that |
2396 | | // # is 43 bytes or shorter SHOULD send a Stateless Reset that is one byte |
2397 | | // # shorter than the packet it responds to. |
2398 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2399 | | // # An endpoint MUST NOT send a Stateless Reset that is three times or |
2400 | | // # more larger than the packet it receives to avoid being used for |
2401 | | // # amplification. |
2402 | | if (reset_size >= inbound_bytes.size() * 3u) { |
2403 | | reset_size = std::max(kMinimumStatelessResetDatagramSize, inbound_bytes.size() - 1u); |
2404 | | } |
2405 | | |
2406 | | DatagramBuffer bytes; |
2407 | | bytes.resize(reset_size, std::byte{0x00}); |
2408 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
2409 | | // # The remainder of the first byte and an arbitrary number of bytes |
2410 | | // # following it are set to values that SHOULD be indistinguishable from |
2411 | | // # random. |
2412 | | fill_random_bytes(bytes.span(), endpoint_random_); |
2413 | | auto reset_bytes = bytes.span(); |
2414 | | reset_bytes.front() = static_cast<std::byte>( |
2415 | | 0x40u | (std::to_integer<std::uint8_t>(reset_bytes.front()) & 0x3fu)); |
2416 | | std::copy(token_route->stateless_reset_token.begin(), token_route->stateless_reset_token.end(), |
2417 | | bytes.end() - static_cast<std::ptrdiff_t>(kStatelessResetTokenLength)); |
2418 | | |
2419 | | return QuicCoreSendDatagram{ |
2420 | | .connection = token_route->owner, |
2421 | | .route_handle = route_handle, |
2422 | | .bytes = std::move(bytes), |
2423 | | }; |
2424 | | } |
2425 | | |
2426 | | COQUIC_NO_PROFILE void QuicCore::load_consumed_address_validation_tokens() { |
2427 | | consumed_address_validation_tokens_.clear(); |
2428 | | #if defined(COQUIC_WASM_NO_FILESYSTEM) |
2429 | | return; |
2430 | | #else |
2431 | | if (!endpoint_config_.address_validation_replay_store_path.has_value()) { |
2432 | | return; |
2433 | | } |
2434 | | |
2435 | | std::ifstream input(*endpoint_config_.address_validation_replay_store_path); |
2436 | | if (!input.is_open()) { |
2437 | | return; |
2438 | | } |
2439 | | |
2440 | | std::string line; |
2441 | | while (std::getline(input, line)) { |
2442 | | const auto separator = line.find(' '); |
2443 | | if (separator == std::string::npos) { |
2444 | | continue; |
2445 | | } |
2446 | | const auto key = hex_decode_to_string(std::string_view(line).substr(0, separator)); |
2447 | | const auto expires_at_us = |
2448 | | parse_unsigned_decimal(std::string_view(line).substr(separator + 1u)); |
2449 | | if (!key.has_value() || !expires_at_us.has_value() || key->empty()) { |
2450 | | continue; |
2451 | | } |
2452 | | consumed_address_validation_tokens_[*key] = token_time_from_us(*expires_at_us); |
2453 | | } |
2454 | | #endif |
2455 | | } |
2456 | | |
2457 | | COQUIC_NO_PROFILE void QuicCore::persist_consumed_address_validation_tokens() { |
2458 | | #if defined(COQUIC_WASM_NO_FILESYSTEM) |
2459 | | return; |
2460 | | #else |
2461 | | if (!endpoint_config_.address_validation_replay_store_path.has_value()) { |
2462 | | return; |
2463 | | } |
2464 | | |
2465 | | const auto &path = *endpoint_config_.address_validation_replay_store_path; |
2466 | | std::error_code ignored; |
2467 | | if (path.has_parent_path()) { |
2468 | | std::filesystem::create_directories(path.parent_path(), ignored); |
2469 | | } |
2470 | | |
2471 | | const auto temporary = path.string() + ".tmp"; |
2472 | | { |
2473 | | std::ofstream output(temporary, std::ios::trunc); |
2474 | | if (!output.is_open()) { |
2475 | | return; |
2476 | | } |
2477 | | for (const auto &[key, expires_at] : consumed_address_validation_tokens_) { |
2478 | | const auto key_bytes = std::as_bytes(std::span(key.data(), key.size())); |
2479 | | output << hex_encode_bytes(key_bytes) << ' ' << token_timestamp_us(expires_at) << '\n'; |
2480 | | } |
2481 | | } |
2482 | | |
2483 | | std::filesystem::rename(temporary, path, ignored); |
2484 | | if (ignored) { |
2485 | | std::filesystem::remove(path, ignored); |
2486 | | std::filesystem::rename(temporary, path, ignored); |
2487 | | } |
2488 | | #endif |
2489 | | } |
2490 | | |
2491 | 208 | bool QuicCore::address_validation_token_consumed(std::span<const std::byte> token) const { |
2492 | 208 | return consumed_address_validation_tokens_.contains(address_validation_token_replay_key(token)); |
2493 | 208 | } |
2494 | | |
2495 | | void QuicCore::mark_address_validation_token_consumed(std::span<const std::byte> token, |
2496 | 88 | QuicCoreTimePoint expires_at) { |
2497 | 88 | consumed_address_validation_tokens_[address_validation_token_replay_key(token)] = expires_at; |
2498 | 88 | persist_consumed_address_validation_tokens(); |
2499 | 88 | } |
2500 | | |
2501 | | std::span<const std::byte> |
2502 | 80 | QuicCore::current_address_validation_identity(const ConnectionEntry &entry) const { |
2503 | 80 | if (entry.connection == nullptr || !entry.connection->current_send_path_id_.has_value()) { |
2504 | 16 | return {}; |
2505 | 16 | } |
2506 | 64 | const auto identity_it = |
2507 | 64 | entry.address_validation_identity_by_path_id.find(*entry.connection->current_send_path_id_); |
2508 | 64 | if (identity_it == entry.address_validation_identity_by_path_id.end()) { |
2509 | 24 | return {}; |
2510 | 24 | } |
2511 | 40 | return identity_it->second; |
2512 | 64 | } |
2513 | | |
2514 | | COQUIC_NO_PROFILE std::vector<std::byte> QuicCore::effective_address_validation_identity_for_route( |
2515 | | const ConnectionEntry &entry, QuicRouteHandle route_handle, |
2516 | | std::span<const std::byte> proposed_identity) const { |
2517 | | if (!proposed_identity.empty()) { |
2518 | | return std::vector<std::byte>(proposed_identity.begin(), proposed_identity.end()); |
2519 | | } |
2520 | | |
2521 | | const auto path_it = entry.path_id_by_route_handle.find(route_handle); |
2522 | | if (path_it == entry.path_id_by_route_handle.end()) { |
2523 | | return {}; |
2524 | | } |
2525 | | const auto identity_it = entry.address_validation_identity_by_path_id.find(path_it->second); |
2526 | | if (identity_it == entry.address_validation_identity_by_path_id.end()) { |
2527 | | return {}; |
2528 | | } |
2529 | | return identity_it->second; |
2530 | | } |
2531 | | |
2532 | | bool QuicCore::address_validation_identity_allowed_for_new_route( |
2533 | 540 | const ConnectionEntry *entry, std::span<const std::byte> address_validation_identity) const { |
2534 | 540 | if (address_validation_identity.empty()) { |
2535 | 160 | return true; |
2536 | 160 | } |
2537 | | |
2538 | 380 | const auto current_identity = entry != nullptr ? current_address_validation_identity(*entry) |
2539 | 380 | : std::span<const std::byte>{}; |
2540 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.5.6 |
2541 | | // # Endpoints MAY prevent connection attempts or migration to a loopback |
2542 | | // # address. |
2543 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-21.5.6 |
2544 | | // # Endpoints MAY choose to avoid sending datagrams to these ports or not |
2545 | | // # send datagrams that match these patterns prior to validating the |
2546 | | // # destination address. |
2547 | 380 | return address_identity_allowed_by_request_forgery_policy( |
2548 | 380 | endpoint_config_.request_forgery_policy, current_identity, address_validation_identity); |
2549 | 540 | } |
2550 | | |
2551 | | bool QuicCore::preferred_address_migration_route_family_allowed( |
2552 | 28 | const ConnectionEntry &entry, std::span<const std::byte> address_validation_identity) { |
2553 | 28 | if (entry.connection == nullptr || !entry.connection->peer_transport_parameters_.has_value() || |
2554 | 28 | !entry.connection->peer_transport_parameters_->preferred_address.has_value()) { |
2555 | 8 | return true; |
2556 | 8 | } |
2557 | | |
2558 | 20 | const auto family = route_address_family_from_identity(address_validation_identity); |
2559 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-9.6.3 |
2560 | | // # A client that migrates to a new address SHOULD use a preferred |
2561 | | // # address from the same address family for the server. |
2562 | 20 | return preferred_address_has_family( |
2563 | 20 | *entry.connection->peer_transport_parameters_->preferred_address, family); |
2564 | 28 | } |
2565 | | |
2566 | | std::vector<std::byte> COQUIC_NO_PROFILE QuicCore::make_version_negotiation_packet_bytes( |
2567 | | const ParsedEndpointDatagram &parsed, std::span<const std::uint32_t> supported_versions, |
2568 | | bool grease_reserved_versions) { |
2569 | | if (!parsed.source_connection_id.has_value() || supported_versions.empty()) { |
2570 | | return {}; |
2571 | | } |
2572 | | |
2573 | | std::vector<std::uint32_t> advertised_versions(supported_versions.begin(), |
2574 | | supported_versions.end()); |
2575 | | if (grease_reserved_versions && |
2576 | | std::none_of(advertised_versions.begin(), advertised_versions.end(), is_reserved_version)) { |
2577 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-15 |
2578 | | // # Reserved version numbers will never represent a real protocol; a |
2579 | | // # client MAY use one of these version numbers with the expectation that |
2580 | | // # the server will initiate version negotiation; a server MAY advertise |
2581 | | // # support for one of these versions and can expect that clients ignore |
2582 | | // # the value. |
2583 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-15 |
2584 | | // # A client or server MAY advertise support for any of these reserved |
2585 | | // # versions. |
2586 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-15 |
2587 | | // # a server MAY advertise support for one of these versions and can |
2588 | | // # expect that clients ignore the value. |
2589 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.3 |
2590 | | // # Endpoints MAY add reserved versions to any field where unknown or |
2591 | | // # unsupported versions are ignored to test that a peer correctly |
2592 | | // # ignores the value. |
2593 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
2594 | | // # The server MAY add reserved versions (as |
2595 | | // # defined in Section 6.3 of [QUIC]) in Supported Version fields. |
2596 | | advertised_versions.push_back(kGreasedReservedVersion); |
2597 | | } |
2598 | | |
2599 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.1 |
2600 | | // # The server MUST include the value from the Source Connection ID field |
2601 | | // # of the packet it receives in the Destination Connection ID field. |
2602 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.1 |
2603 | | // # The value for Source Connection ID MUST be copied from the |
2604 | | // # Destination Connection ID of the received packet, which is initially |
2605 | | // # randomly selected by a client. |
2606 | | const auto encoded = serialize_packet(VersionNegotiationPacket{ |
2607 | | .destination_connection_id = *parsed.source_connection_id, |
2608 | | .source_connection_id = parsed.destination_connection_id, |
2609 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
2610 | | // # This packet SHALL include each entry |
2611 | | // # from the server's set of Offered Versions (see Section 5) in a |
2612 | | // # Supported Version field. |
2613 | | .supported_versions = std::move(advertised_versions), |
2614 | | }); |
2615 | | return encoded.has_value() ? DatagramBuffer(encoded.value()) : DatagramBuffer{}; |
2616 | | } |
2617 | | |
2618 | | std::vector<std::byte> QuicCore::make_reserved_version_probe_packet_bytes( |
2619 | 40 | const QuicCoreClientConnectionConfig &connection) { |
2620 | 40 | if (connection.initial_destination_connection_id.size() > 255 || |
2621 | 40 | connection.source_connection_id.size() > 255) { |
2622 | 0 | return {}; |
2623 | 0 | } |
2624 | | |
2625 | 40 | std::vector<std::byte> bytes; |
2626 | 40 | bytes.reserve(7 + connection.initial_destination_connection_id.size() + |
2627 | 40 | connection.source_connection_id.size()); |
2628 | 40 | bytes.push_back(std::byte{0xc0}); |
2629 | 40 | append_u32_be(bytes, kGreasedReservedVersion); |
2630 | 40 | bytes.push_back(static_cast<std::byte>(connection.initial_destination_connection_id.size())); |
2631 | 40 | bytes.insert(bytes.end(), connection.initial_destination_connection_id.begin(), |
2632 | 40 | connection.initial_destination_connection_id.end()); |
2633 | 40 | bytes.push_back(static_cast<std::byte>(connection.source_connection_id.size())); |
2634 | 40 | bytes.insert(bytes.end(), connection.source_connection_id.begin(), |
2635 | 40 | connection.source_connection_id.end()); |
2636 | 40 | return bytes; |
2637 | 40 | } |
2638 | | |
2639 | | std::vector<std::byte> QuicCore::make_retry_packet_bytes(const ParsedEndpointDatagram &parsed, |
2640 | 104 | const PendingRetryToken &pending) { |
2641 | 104 | if (!parsed.source_connection_id.has_value()) { |
2642 | 8 | return {}; |
2643 | 8 | } |
2644 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.1 |
2645 | | // # This value MUST NOT be equal to the Destination |
2646 | | // # Connection ID field of the packet sent by the client. |
2647 | 96 | if (pending.retry_source_connection_id == parsed.destination_connection_id) { |
2648 | 16 | return {}; |
2649 | 16 | } |
2650 | | |
2651 | 80 | RetryPacket packet{ |
2652 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-4.1 |
2653 | | // # If the server sends a Retry packet, it MUST use the original version. |
2654 | 80 | .version = parsed.version, |
2655 | 80 | .retry_unused_bits = 0, |
2656 | 80 | .destination_connection_id = *parsed.source_connection_id, |
2657 | 80 | .source_connection_id = pending.retry_source_connection_id, |
2658 | 80 | .retry_token = pending.token, |
2659 | 80 | }; |
2660 | 80 | const auto integrity_tag = |
2661 | 80 | compute_retry_integrity_tag(packet, parsed.destination_connection_id); |
2662 | 80 | if (!integrity_tag.has_value()) { |
2663 | 8 | return {}; |
2664 | 8 | } |
2665 | 72 | packet.retry_integrity_tag = integrity_tag.value(); |
2666 | | |
2667 | | // Computing the Retry integrity tag already serialized the same validated Retry packet. |
2668 | 72 | return DatagramBuffer(serialize_packet(packet).value()); |
2669 | 80 | } |
2670 | | |
2671 | 113k | bool QuicCore::orphan_zero_rtt_buffer_enabled() const { |
2672 | 113k | return endpoint_config_.role == EndpointRole::server && |
2673 | 113k | endpoint_config_.orphan_zero_rtt_buffer.max_packets != 0 && |
2674 | 113k | endpoint_config_.orphan_zero_rtt_buffer.max_bytes != 0 && |
2675 | 113k | endpoint_config_.orphan_zero_rtt_buffer.max_age > QuicCoreDuration{0}; |
2676 | 113k | } |
2677 | | |
2678 | 16.7k | void QuicCore::purge_expired_orphan_zero_rtt(QuicCoreTimePoint now) { |
2679 | 16.7k | if (orphan_zero_rtt_buffer_.empty()) { |
2680 | 16.5k | return; |
2681 | 16.5k | } |
2682 | 136 | if (!orphan_zero_rtt_buffer_enabled()) { |
2683 | 0 | orphan_zero_rtt_buffer_.clear(); |
2684 | 0 | orphan_zero_rtt_buffer_bytes_ = 0; |
2685 | 0 | return; |
2686 | 0 | } |
2687 | | |
2688 | 136 | const auto max_age = endpoint_config_.orphan_zero_rtt_buffer.max_age; |
2689 | 136 | auto retained_bytes = std::size_t{0}; |
2690 | 136 | auto out = std::vector<BufferedOrphanZeroRttDatagram>{}; |
2691 | 136 | out.reserve(orphan_zero_rtt_buffer_.size()); |
2692 | 168 | for (auto &buffered : orphan_zero_rtt_buffer_) { |
2693 | 168 | if (buffered.received_at + max_age <= now) { |
2694 | 16 | continue; |
2695 | 16 | } |
2696 | 152 | retained_bytes += buffered.bytes.size(); |
2697 | 152 | out.push_back(std::move(buffered)); |
2698 | 152 | } |
2699 | 136 | orphan_zero_rtt_buffer_ = std::move(out); |
2700 | 136 | orphan_zero_rtt_buffer_bytes_ = retained_bytes; |
2701 | 136 | } |
2702 | | |
2703 | | bool QuicCore::orphan_zero_rtt_route_matches( |
2704 | | const BufferedOrphanZeroRttDatagram &buffered, |
2705 | | const std::optional<QuicRouteHandle> &route_handle, |
2706 | 40 | std::span<const std::byte> address_validation_identity) { |
2707 | 40 | return buffered.route_handle == route_handle && |
2708 | 40 | std::ranges::equal(buffered.address_validation_identity, address_validation_identity); |
2709 | 40 | } |
2710 | | |
2711 | | bool QuicCore::orphan_zero_rtt_connection_tuple_matches( |
2712 | | const BufferedOrphanZeroRttDatagram &buffered, const ParsedEndpointDatagram &parsed, |
2713 | | const std::optional<QuicRouteHandle> &route_handle, |
2714 | 48 | std::span<const std::byte> address_validation_identity) { |
2715 | 48 | return buffered.destination_connection_id == parsed.destination_connection_id && |
2716 | 48 | buffered.source_connection_id == parsed.source_connection_id && |
2717 | 48 | orphan_zero_rtt_route_matches(buffered, route_handle, address_validation_identity); |
2718 | 48 | } |
2719 | | |
2720 | | bool QuicCore::orphan_zero_rtt_tuple_matches( |
2721 | | const BufferedOrphanZeroRttDatagram &buffered, const ParsedEndpointDatagram &parsed, |
2722 | | const std::optional<QuicRouteHandle> &route_handle, |
2723 | 0 | std::span<const std::byte> address_validation_identity) { |
2724 | 0 | return buffered.version == parsed.version && |
2725 | 0 | orphan_zero_rtt_connection_tuple_matches(buffered, parsed, route_handle, |
2726 | 0 | address_validation_identity); |
2727 | 0 | } |
2728 | | |
2729 | | void QuicCore::drop_matching_orphan_zero_rtt(const ParsedEndpointDatagram &parsed, |
2730 | | const std::optional<QuicRouteHandle> &route_handle, |
2731 | | std::span<const std::byte> address_validation_identity, |
2732 | 128 | QuicCoreTimePoint now) { |
2733 | 128 | purge_expired_orphan_zero_rtt(now); |
2734 | 128 | if (orphan_zero_rtt_buffer_.empty()) { |
2735 | 120 | return; |
2736 | 120 | } |
2737 | | |
2738 | 8 | auto retained_bytes = std::size_t{0}; |
2739 | 8 | auto out = std::vector<BufferedOrphanZeroRttDatagram>{}; |
2740 | 8 | out.reserve(orphan_zero_rtt_buffer_.size()); |
2741 | 8 | for (auto &buffered : orphan_zero_rtt_buffer_) { |
2742 | 8 | if (orphan_zero_rtt_connection_tuple_matches(buffered, parsed, route_handle, |
2743 | 8 | address_validation_identity)) { |
2744 | 8 | continue; |
2745 | 8 | } |
2746 | 0 | retained_bytes += buffered.bytes.size(); |
2747 | 0 | out.push_back(std::move(buffered)); |
2748 | 0 | } |
2749 | 8 | orphan_zero_rtt_buffer_ = std::move(out); |
2750 | 8 | orphan_zero_rtt_buffer_bytes_ = retained_bytes; |
2751 | 8 | } |
2752 | | |
2753 | | bool QuicCore::maybe_buffer_orphan_zero_rtt(const ParsedEndpointDatagram &parsed, |
2754 | | const QuicCoreInboundDatagram &inbound, |
2755 | 100 | QuicCoreTimePoint now) { |
2756 | 100 | purge_expired_orphan_zero_rtt(now); |
2757 | 100 | if (!orphan_zero_rtt_buffer_enabled() || |
2758 | 100 | parsed.kind != ParsedEndpointDatagram::Kind::supported_zero_rtt) { |
2759 | 28 | return false; |
2760 | 28 | } |
2761 | | |
2762 | 72 | auto bytes = inbound.materialize(); |
2763 | 72 | const auto &limits = endpoint_config_.orphan_zero_rtt_buffer; |
2764 | 72 | if (bytes.empty() || bytes.size() > limits.max_bytes || |
2765 | 72 | orphan_zero_rtt_buffer_.size() >= limits.max_packets || |
2766 | 72 | orphan_zero_rtt_buffer_bytes_ > limits.max_bytes - bytes.size()) { |
2767 | 16 | return false; |
2768 | 16 | } |
2769 | | |
2770 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
2771 | | // # If the packet is a 0-RTT packet, the server MAY buffer a limited |
2772 | | // # number of these packets in anticipation of a late-arriving Initial packet. |
2773 | 56 | orphan_zero_rtt_buffer_bytes_ += bytes.size(); |
2774 | 56 | orphan_zero_rtt_buffer_.push_back(BufferedOrphanZeroRttDatagram{ |
2775 | 56 | .destination_connection_id = parsed.destination_connection_id, |
2776 | 56 | .source_connection_id = parsed.source_connection_id, |
2777 | 56 | .version = parsed.version, |
2778 | 56 | .route_handle = inbound.route_handle, |
2779 | 56 | .address_validation_identity = inbound.address_validation_identity, |
2780 | 56 | .bytes = std::move(bytes), |
2781 | 56 | .ecn = inbound.ecn, |
2782 | 56 | .received_at = now, |
2783 | 56 | }); |
2784 | 56 | return true; |
2785 | 72 | } |
2786 | | |
2787 | | std::vector<QuicCore::BufferedOrphanZeroRttDatagram> |
2788 | | QuicCore::take_matching_orphan_zero_rtt(const ParsedEndpointDatagram &parsed, |
2789 | | const QuicCoreInboundDatagram &inbound, |
2790 | 228 | QuicCoreTimePoint now) { |
2791 | 228 | purge_expired_orphan_zero_rtt(now); |
2792 | 228 | auto matched = std::vector<BufferedOrphanZeroRttDatagram>{}; |
2793 | 228 | if (orphan_zero_rtt_buffer_.empty()) { |
2794 | 204 | return matched; |
2795 | 204 | } |
2796 | | |
2797 | 24 | auto retained_bytes = std::size_t{0}; |
2798 | 24 | auto out = std::vector<BufferedOrphanZeroRttDatagram>{}; |
2799 | 24 | out.reserve(orphan_zero_rtt_buffer_.size()); |
2800 | 40 | for (auto &buffered : orphan_zero_rtt_buffer_) { |
2801 | 40 | if (orphan_zero_rtt_connection_tuple_matches(buffered, parsed, inbound.route_handle, |
2802 | 40 | inbound.address_validation_identity)) { |
2803 | 24 | if (buffered.version == parsed.version) { |
2804 | 16 | matched.push_back(std::move(buffered)); |
2805 | 16 | } |
2806 | 24 | continue; |
2807 | 24 | } |
2808 | 16 | retained_bytes += buffered.bytes.size(); |
2809 | 16 | out.push_back(std::move(buffered)); |
2810 | 16 | } |
2811 | 24 | orphan_zero_rtt_buffer_ = std::move(out); |
2812 | 24 | orphan_zero_rtt_buffer_bytes_ = retained_bytes; |
2813 | 24 | return matched; |
2814 | 228 | } |
2815 | | |
2816 | 113k | std::optional<QuicCoreTimePoint> QuicCore::next_orphan_zero_rtt_expiry() const { |
2817 | 113k | if (!orphan_zero_rtt_buffer_enabled() || orphan_zero_rtt_buffer_.empty()) { |
2818 | 113k | return std::nullopt; |
2819 | 113k | } |
2820 | | |
2821 | 97 | const auto max_age = endpoint_config_.orphan_zero_rtt_buffer.max_age; |
2822 | 97 | std::optional<QuicCoreTimePoint> earliest; |
2823 | 112 | for (const auto &buffered : orphan_zero_rtt_buffer_) { |
2824 | 112 | const auto expiry = buffered.received_at + max_age; |
2825 | 112 | if (!earliest.has_value() || expiry < *earliest) { |
2826 | 96 | earliest = expiry; |
2827 | 96 | } |
2828 | 112 | } |
2829 | 97 | return earliest; |
2830 | 113k | } |
2831 | | |
2832 | | std::optional<QuicConnectionHandle> |
2833 | 13.5k | QuicCore::find_endpoint_connection_for_datagram(const ParsedEndpointDatagram &parsed) const { |
2834 | 13.5k | const auto destination_connection_id_key = connection_id_key(parsed.destination_connection_id); |
2835 | 13.5k | const auto connection_it = connection_id_routes_.find(destination_connection_id_key); |
2836 | 13.5k | if (connection_it != connection_id_routes_.end()) { |
2837 | 12.9k | return connection_it->second; |
2838 | 12.9k | } |
2839 | | |
2840 | 548 | if (parsed.kind != ParsedEndpointDatagram::Kind::supported_initial && |
2841 | 548 | parsed.kind != ParsedEndpointDatagram::Kind::supported_zero_rtt && |
2842 | 548 | parsed.kind != ParsedEndpointDatagram::Kind::supported_long_header) { |
2843 | 60 | return std::nullopt; |
2844 | 60 | } |
2845 | | |
2846 | 488 | const auto initial_it = initial_destination_routes_.find(destination_connection_id_key); |
2847 | 488 | if (initial_it == initial_destination_routes_.end()) { |
2848 | 456 | return std::nullopt; |
2849 | 456 | } |
2850 | 32 | return initial_it->second; |
2851 | 488 | } |
2852 | | |
2853 | | std::optional<QuicConnectionHandle> QuicCore::find_endpoint_connection_for_connection_id( |
2854 | 24 | std::span<const std::byte> connection_id) const { |
2855 | 24 | const auto key = connection_id_key(connection_id); |
2856 | 24 | if (const auto connection_it = connection_id_routes_.find(key); |
2857 | 24 | connection_it != connection_id_routes_.end()) { |
2858 | 16 | return connection_it->second; |
2859 | 16 | } |
2860 | 8 | if (const auto initial_it = initial_destination_routes_.find(key); |
2861 | 8 | initial_it != initial_destination_routes_.end()) { |
2862 | 8 | return initial_it->second; |
2863 | 8 | } |
2864 | 0 | return std::nullopt; |
2865 | 8 | } |
2866 | | |
2867 | | bool QuicCore::quoted_packet_matches_connection(const ParsedEndpointDatagram "ed, |
2868 | 16 | QuicConnectionHandle handle) const { |
2869 | 16 | if (const auto destination_match = |
2870 | 16 | find_endpoint_connection_for_connection_id(quoted.destination_connection_id); |
2871 | 16 | destination_match.has_value() && *destination_match == handle) { |
2872 | 8 | return true; |
2873 | 8 | } |
2874 | 8 | if (quoted.source_connection_id.has_value()) { |
2875 | 8 | const auto source_match = |
2876 | 8 | find_endpoint_connection_for_connection_id(*quoted.source_connection_id); |
2877 | 8 | if (source_match.has_value() && *source_match == handle) { |
2878 | 0 | return true; |
2879 | 0 | } |
2880 | 8 | } |
2881 | 8 | return false; |
2882 | 8 | } |
2883 | | |
2884 | | bool QuicCore::path_mtu_update_matches_connection(const ConnectionEntry &entry, |
2885 | 40 | const QuicCorePathMtuUpdate &update) const { |
2886 | 40 | if (update.quoted_packet.empty()) { |
2887 | 24 | return true; |
2888 | 24 | } |
2889 | | |
2890 | 16 | const auto quoted = |
2891 | 16 | parse_endpoint_datagram(update.quoted_packet, endpoint_config_.transport.grease_quic_bit); |
2892 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14.2.1 |
2893 | | // # ICMP message validation MUST include matching IP addresses and UDP |
2894 | | // # ports [RFC8085] and, when possible, connection IDs to an active QUIC |
2895 | | // # session. |
2896 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14.2.1 |
2897 | | // # The endpoint SHOULD ignore all ICMP messages that fail validation. |
2898 | 16 | return quoted.has_value() && quoted_packet_matches_connection(*quoted, entry.handle); |
2899 | 40 | } |
2900 | | |
2901 | | COQUIC_NO_PROFILE void QuicCore::erase_endpoint_connection_routes(const ConnectionEntry &entry) { |
2902 | | for (const auto &connection_id_key_value : entry.active_connection_id_keys) { |
2903 | | const auto it = connection_id_routes_.find(connection_id_key_value); |
2904 | | if (it != connection_id_routes_.end() && it->second == entry.handle) { |
2905 | | connection_id_routes_.erase(it); |
2906 | | } |
2907 | | } |
2908 | | if (entry.initial_destination_connection_id_key.has_value()) { |
2909 | | const auto it = |
2910 | | initial_destination_routes_.find(*entry.initial_destination_connection_id_key); |
2911 | | if (it != initial_destination_routes_.end() && it->second == entry.handle) { |
2912 | | initial_destination_routes_.erase(it); |
2913 | | } |
2914 | | } |
2915 | | for (const auto &connection_id_key_value : entry.local_stateless_reset_connection_id_keys) { |
2916 | | const auto it = local_stateless_reset_tokens_by_cid_.find(connection_id_key_value); |
2917 | | if (it != local_stateless_reset_tokens_by_cid_.end() && it->second.owner == entry.handle) { |
2918 | | local_stateless_reset_tokens_by_cid_.erase(it); |
2919 | | } |
2920 | | } |
2921 | | for (const auto &token_key : entry.peer_stateless_reset_token_keys) { |
2922 | | const auto it = peer_stateless_reset_tokens_.find(token_key); |
2923 | | if (it != peer_stateless_reset_tokens_.end() && it->second.owner == entry.handle) { |
2924 | | peer_stateless_reset_tokens_.erase(it); |
2925 | | } |
2926 | | } |
2927 | | } |
2928 | | |
2929 | | COQUIC_NO_PROFILE void QuicCore::retire_endpoint_connection_routes(const ConnectionEntry &entry, |
2930 | | QuicCoreTimePoint now) { |
2931 | | for (const auto &connection_id_key_value : entry.active_connection_id_keys) { |
2932 | | const auto it = connection_id_routes_.find(connection_id_key_value); |
2933 | | if (it != connection_id_routes_.end() && it->second == entry.handle) { |
2934 | | connection_id_routes_.erase(it); |
2935 | | } |
2936 | | } |
2937 | | if (entry.initial_destination_connection_id_key.has_value()) { |
2938 | | const auto it = |
2939 | | initial_destination_routes_.find(*entry.initial_destination_connection_id_key); |
2940 | | if (it != initial_destination_routes_.end() && it->second == entry.handle) { |
2941 | | initial_destination_routes_.erase(it); |
2942 | | } |
2943 | | } |
2944 | | |
2945 | | const auto reset_token_expiry = |
2946 | | endpoint_config_.retain_stateless_reset_tokens_after_connection_close |
2947 | | ? std::optional<QuicCoreTimePoint>{now + |
2948 | | endpoint_config_.stateless_reset_token_retention} |
2949 | | : std::nullopt; |
2950 | | for (const auto &connection_id_key_value : entry.local_stateless_reset_connection_id_keys) { |
2951 | | const auto it = local_stateless_reset_tokens_by_cid_.find(connection_id_key_value); |
2952 | | if (it == local_stateless_reset_tokens_by_cid_.end() || it->second.owner != entry.handle) { |
2953 | | continue; |
2954 | | } |
2955 | | if (reset_token_expiry.has_value()) { |
2956 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.2 |
2957 | | // # The endpoint MAY send a Stateless Reset in |
2958 | | // # response to any further incoming packets belonging to this |
2959 | | // # connection. |
2960 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-9.3.2 |
2961 | | // # For instance, an endpoint MAY send a Stateless Reset in |
2962 | | // # response to any further incoming packets. |
2963 | | it->second.expires_at = reset_token_expiry; |
2964 | | } else { |
2965 | | local_stateless_reset_tokens_by_cid_.erase(it); |
2966 | | } |
2967 | | } |
2968 | | for (const auto &token_key : entry.peer_stateless_reset_token_keys) { |
2969 | | const auto it = peer_stateless_reset_tokens_.find(token_key); |
2970 | | if (it != peer_stateless_reset_tokens_.end() && it->second.owner == entry.handle) { |
2971 | | peer_stateless_reset_tokens_.erase(it); |
2972 | | } |
2973 | | } |
2974 | | } |
2975 | | |
2976 | | COQUIC_NO_PROFILE void QuicCore::purge_expired_local_stateless_reset_tokens(QuicCoreTimePoint now) { |
2977 | | if (now == QuicCoreTimePoint{}) { |
2978 | | return; |
2979 | | } |
2980 | | for (auto it = local_stateless_reset_tokens_by_cid_.begin(); |
2981 | | it != local_stateless_reset_tokens_by_cid_.end();) { |
2982 | | if (it->second.expires_at.has_value() && *it->second.expires_at <= now) { |
2983 | | it = local_stateless_reset_tokens_by_cid_.erase(it); |
2984 | | } else { |
2985 | | ++it; |
2986 | | } |
2987 | | } |
2988 | | } |
2989 | | |
2990 | | COQUIC_NO_PROFILE void QuicCore::refresh_server_connection_routes(ConnectionEntry &entry) { |
2991 | | const auto current_generation = entry.connection->endpoint_route_generation(); |
2992 | | if (entry.endpoint_route_generation == current_generation) { |
2993 | | return; |
2994 | | } |
2995 | | entry.endpoint_route_generation = current_generation; |
2996 | | |
2997 | | std::vector<std::string> active_connection_id_keys; |
2998 | | for (const auto &connection_id : entry.connection->active_local_connection_ids()) { |
2999 | | auto key = connection_id_key(connection_id); |
3000 | | if (key.empty()) { |
3001 | | continue; |
3002 | | } |
3003 | | connection_id_routes_[key] = entry.handle; |
3004 | | active_connection_id_keys.push_back(std::move(key)); |
3005 | | } |
3006 | | if (entry.connection->config_.role == EndpointRole::server && |
3007 | | entry.connection->config_.retry_source_connection_id.has_value() && |
3008 | | entry.connection->config_.original_destination_connection_id.has_value()) { |
3009 | | auto key = connection_id_key(*entry.connection->config_.original_destination_connection_id); |
3010 | | const auto key_already_active = |
3011 | | std::find(active_connection_id_keys.begin(), active_connection_id_keys.end(), key) != |
3012 | | active_connection_id_keys.end(); |
3013 | | const auto existing_route = connection_id_routes_.find(key); |
3014 | | if (!key.empty() && !key_already_active && |
3015 | | (existing_route == connection_id_routes_.end() || |
3016 | | existing_route->second == entry.handle)) { |
3017 | | connection_id_routes_[key] = entry.handle; |
3018 | | active_connection_id_keys.push_back(std::move(key)); |
3019 | | } |
3020 | | } |
3021 | | |
3022 | | for (const auto &existing_key : entry.active_connection_id_keys) { |
3023 | | if (std::find(active_connection_id_keys.begin(), active_connection_id_keys.end(), |
3024 | | existing_key) != active_connection_id_keys.end()) { |
3025 | | continue; |
3026 | | } |
3027 | | const auto route_it = connection_id_routes_.find(existing_key); |
3028 | | if (route_it != connection_id_routes_.end() && route_it->second == entry.handle) { |
3029 | | connection_id_routes_.erase(route_it); |
3030 | | } |
3031 | | } |
3032 | | entry.active_connection_id_keys = std::move(active_connection_id_keys); |
3033 | | |
3034 | | std::vector<std::string> local_stateless_reset_connection_id_keys; |
3035 | | for (const auto &record : entry.connection->active_local_stateless_reset_tokens()) { |
3036 | | auto key = connection_id_key(record.connection_id); |
3037 | | if (key.empty()) { |
3038 | | continue; |
3039 | | } |
3040 | | local_stateless_reset_tokens_by_cid_[key] = LocalStatelessResetTokenRoute{ |
3041 | | .owner = entry.handle, |
3042 | | .stateless_reset_token = record.stateless_reset_token, |
3043 | | }; |
3044 | | local_stateless_reset_connection_id_keys.push_back(std::move(key)); |
3045 | | } |
3046 | | for (const auto &existing_key : entry.local_stateless_reset_connection_id_keys) { |
3047 | | if (std::find(local_stateless_reset_connection_id_keys.begin(), |
3048 | | local_stateless_reset_connection_id_keys.end(), |
3049 | | existing_key) != local_stateless_reset_connection_id_keys.end()) { |
3050 | | continue; |
3051 | | } |
3052 | | const auto route_it = local_stateless_reset_tokens_by_cid_.find(existing_key); |
3053 | | if (route_it != local_stateless_reset_tokens_by_cid_.end() && |
3054 | | route_it->second.owner == entry.handle) { |
3055 | | local_stateless_reset_tokens_by_cid_.erase(route_it); |
3056 | | } |
3057 | | } |
3058 | | entry.local_stateless_reset_connection_id_keys = |
3059 | | std::move(local_stateless_reset_connection_id_keys); |
3060 | | |
3061 | | std::vector<std::string> peer_stateless_reset_token_keys; |
3062 | | for (const auto &record : entry.connection->peer_stateless_reset_tokens()) { |
3063 | | auto key = stateless_reset_token_key(record.stateless_reset_token); |
3064 | | peer_stateless_reset_tokens_[key] = PeerStatelessResetTokenRoute{ |
3065 | | .owner = entry.handle, |
3066 | | }; |
3067 | | peer_stateless_reset_token_keys.push_back(std::move(key)); |
3068 | | } |
3069 | | for (const auto &existing_key : entry.peer_stateless_reset_token_keys) { |
3070 | | if (std::find(peer_stateless_reset_token_keys.begin(), |
3071 | | peer_stateless_reset_token_keys.end(), |
3072 | | existing_key) != peer_stateless_reset_token_keys.end()) { |
3073 | | continue; |
3074 | | } |
3075 | | const auto route_it = peer_stateless_reset_tokens_.find(existing_key); |
3076 | | if (route_it != peer_stateless_reset_tokens_.end() && |
3077 | | route_it->second.owner == entry.handle) { |
3078 | | peer_stateless_reset_tokens_.erase(route_it); |
3079 | | } |
3080 | | } |
3081 | | entry.peer_stateless_reset_token_keys = std::move(peer_stateless_reset_token_keys); |
3082 | | |
3083 | | auto next_initial_destination_key = |
3084 | | connection_id_key(entry.connection->client_initial_destination_connection_id()); |
3085 | | if (entry.initial_destination_connection_id_key.has_value() && |
3086 | | entry.initial_destination_connection_id_key != next_initial_destination_key) { |
3087 | | const auto initial_it = |
3088 | | initial_destination_routes_.find(*entry.initial_destination_connection_id_key); |
3089 | | if (initial_it != initial_destination_routes_.end() && initial_it->second == entry.handle) { |
3090 | | initial_destination_routes_.erase(initial_it); |
3091 | | } |
3092 | | } |
3093 | | |
3094 | | if (next_initial_destination_key.empty()) { |
3095 | | entry.initial_destination_connection_id_key.reset(); |
3096 | | return; |
3097 | | } |
3098 | | |
3099 | | initial_destination_routes_[next_initial_destination_key] = entry.handle; |
3100 | | entry.initial_destination_connection_id_key = next_initial_destination_key; |
3101 | | } |
3102 | | |
3103 | | void QuicCore::remember_address_validation_identity( |
3104 | | ConnectionEntry &entry, QuicPathId path_id, |
3105 | 26.6k | std::span<const std::byte> address_validation_identity) { |
3106 | 26.6k | if (address_validation_identity.empty()) { |
3107 | 216 | return; |
3108 | 216 | } |
3109 | | |
3110 | 26.4k | auto identity_it = entry.address_validation_identity_by_path_id.find(path_id); |
3111 | 26.4k | if (identity_it != entry.address_validation_identity_by_path_id.end()) { |
3112 | 26.1k | if (std::ranges::equal(identity_it->second, address_validation_identity)) { |
3113 | 26.1k | return; |
3114 | 26.1k | } |
3115 | 13 | identity_it->second.assign(address_validation_identity.begin(), |
3116 | 13 | address_validation_identity.end()); |
3117 | 13 | return; |
3118 | 26.1k | } |
3119 | | |
3120 | 308 | entry.address_validation_identity_by_path_id.emplace( |
3121 | 308 | path_id, std::vector<std::byte>(address_validation_identity.begin(), |
3122 | 308 | address_validation_identity.end())); |
3123 | 308 | } |
3124 | | |
3125 | | void QuicCore::remember_path_address_family(ConnectionEntry &entry, QuicPathId path_id, |
3126 | 1.04k | QuicRouteAddressFamily family) { |
3127 | 1.04k | if (family == QuicRouteAddressFamily::unknown) { |
3128 | 704 | return; |
3129 | 704 | } |
3130 | | |
3131 | 340 | const auto [it, inserted] = entry.address_family_by_path_id.try_emplace(path_id, family); |
3132 | 340 | if (!inserted && it->second == family) { |
3133 | 0 | return; |
3134 | 0 | } |
3135 | 340 | if (!inserted) { |
3136 | 0 | it->second = family; |
3137 | 0 | } |
3138 | 340 | if (entry.connection != nullptr) { |
3139 | 340 | entry.connection->set_path_default_pmtud_search_ceiling( |
3140 | 340 | path_id, QuicDefaultPmtudSearchCeiling{ |
3141 | 340 | .value = default_pmtud_search_ceiling_for_route_family(family), |
3142 | 340 | }); |
3143 | 340 | } |
3144 | 340 | } |
3145 | | |
3146 | | COQUIC_NO_PROFILE QuicPathId |
3147 | | QuicCore::remember_inbound_path(ConnectionEntry &entry, QuicRouteHandle route_handle, |
3148 | | std::span<const std::byte> address_validation_identity) { |
3149 | | const auto address_family = route_address_family_from_identity(address_validation_identity); |
3150 | | if (!entry.default_route_handle.has_value()) { |
3151 | | entry.default_route_handle = route_handle; |
3152 | | } |
3153 | | |
3154 | | const auto existing = entry.path_id_by_route_handle.find(route_handle); |
3155 | | if (existing != entry.path_id_by_route_handle.end()) { |
3156 | | remember_address_validation_identity(entry, existing->second, address_validation_identity); |
3157 | | remember_path_address_family(entry, existing->second, address_family); |
3158 | | return existing->second; |
3159 | | } |
3160 | | |
3161 | | QuicPathId path_id = |
3162 | | entry.route_handle_by_path_id.empty() ? kDefaultPathId : entry.next_path_id++; |
3163 | | while (entry.route_handle_by_path_id.contains(path_id)) { |
3164 | | path_id = entry.next_path_id++; |
3165 | | } |
3166 | | |
3167 | | entry.path_id_by_route_handle.emplace(route_handle, path_id); |
3168 | | entry.route_handle_by_path_id.emplace(path_id, route_handle); |
3169 | | remember_address_validation_identity(entry, path_id, address_validation_identity); |
3170 | | remember_path_address_family(entry, path_id, address_family); |
3171 | | return path_id; |
3172 | | } |
3173 | | |
3174 | | std::optional<QuicPathId> COQUIC_NO_PROFILE QuicCore::path_id_for_inbound_route( |
3175 | | ConnectionEntry &entry, const std::optional<QuicRouteHandle> &route_handle, |
3176 | | std::span<const std::byte> address_validation_identity) { |
3177 | | if (route_handle.has_value()) { |
3178 | | const auto existing = entry.path_id_by_route_handle.find(*route_handle); |
3179 | | if (existing != entry.path_id_by_route_handle.end()) { |
3180 | | remember_address_validation_identity(entry, existing->second, |
3181 | | address_validation_identity); |
3182 | | return existing->second; |
3183 | | } |
3184 | | if (entry.connection != nullptr && entry.connection->config_.role == EndpointRole::client) { |
3185 | | if (!entry.default_route_handle.has_value() && entry.path_id_by_route_handle.empty()) { |
3186 | | return remember_inbound_path(entry, *route_handle, address_validation_identity); |
3187 | | } |
3188 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-9 |
3189 | | // # If a client receives packets from an unknown server address, |
3190 | | // # the client MUST discard these packets. |
3191 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-9.6 |
3192 | | // # If a client receives packets from a new server address when |
3193 | | // # the client has not initiated a migration to that address, the |
3194 | | // # client SHOULD discard these packets. |
3195 | | return std::nullopt; |
3196 | | } |
3197 | | if (!endpoint_config_.allow_peer_address_change) { |
3198 | | return std::nullopt; |
3199 | | } |
3200 | | if (!address_validation_identity_allowed_for_new_route(&entry, |
3201 | | address_validation_identity)) { |
3202 | | return std::nullopt; |
3203 | | } |
3204 | | return remember_inbound_path(entry, *route_handle, address_validation_identity); |
3205 | | } |
3206 | | |
3207 | | if (entry.default_route_handle.has_value()) { |
3208 | | return remember_inbound_path(entry, *entry.default_route_handle, |
3209 | | address_validation_identity); |
3210 | | } |
3211 | | return kDefaultPathId; |
3212 | | } |
3213 | | |
3214 | | std::optional<QuicRouteHandle> |
3215 | | QuicCore::route_handle_for_path(const ConnectionEntry &entry, |
3216 | 7.60k | const std::optional<QuicPathId> &path_id) { |
3217 | 7.60k | if (path_id.has_value()) { |
3218 | 7.60k | const auto route_it = entry.route_handle_by_path_id.find(*path_id); |
3219 | 7.60k | if (route_it != entry.route_handle_by_path_id.end()) { |
3220 | 7.60k | return route_it->second; |
3221 | 7.60k | } |
3222 | 7.60k | } |
3223 | 0 | return entry.default_route_handle; |
3224 | 7.60k | } |
3225 | | |
3226 | 3.91k | bool QuicCore::should_run_connection_timeout(const ConnectionEntry &entry, QuicCoreTimePoint now) { |
3227 | 3.91k | return !entry.send_continuation_wakeup.has_value() && |
3228 | 3.91k | entry.connection->non_pacing_wakeup_due(now); |
3229 | 3.91k | } |
3230 | | |
3231 | 3.90k | void QuicCore::maybe_run_connection_timeout(ConnectionEntry &entry, QuicCoreTimePoint now) { |
3232 | 3.90k | if (should_run_connection_timeout(entry, now)) { |
3233 | 1.93k | entry.connection->on_timeout(now); |
3234 | 1.93k | } |
3235 | 3.90k | } |
3236 | | |
3237 | | template <typename Entry> |
3238 | | COQUIC_NO_PROFILE void store_send_continuation_wakeup(Entry &entry, bool send_continuation_pending, |
3239 | | QuicCoreTimePoint now) { |
3240 | | entry.send_continuation_wakeup = |
3241 | | send_continuation_pending ? std::optional<QuicCoreTimePoint>{now} : std::nullopt; |
3242 | | entry.send_continuation_drain = send_continuation_pending; |
3243 | | } |
3244 | | |
3245 | | template <typename Entry> |
3246 | | COQUIC_NO_PROFILE std::optional<QuicCoreTimePoint> next_entry_wakeup(const Entry &entry) { |
3247 | | return entry.send_continuation_wakeup.has_value() ? entry.send_continuation_wakeup |
3248 | | : entry.connection->next_wakeup(); |
3249 | | } |
3250 | | |
3251 | | template <typename Entry> |
3252 | | COQUIC_NO_PROFILE std::optional<QuicPathId> |
3253 | | path_id_for_route_handle(const Entry &entry, const std::optional<QuicRouteHandle> &route_handle) { |
3254 | | if (!has_route_handle(route_handle)) { |
3255 | | return std::nullopt; |
3256 | | } |
3257 | | const auto route_handle_value = optional_ref_or_abort(route_handle); |
3258 | | const auto path_it = entry.path_id_by_route_handle.find(route_handle_value); |
3259 | | if (path_it == entry.path_id_by_route_handle.end()) { |
3260 | | return std::nullopt; |
3261 | | } |
3262 | | return path_it->second; |
3263 | | } |
3264 | | |
3265 | | QuicCore::QuicCore(QuicCoreEndpointConfig config) |
3266 | 1.44k | : endpoint_config_(std::move(config)), endpoint_random_(std::random_device{}()), |
3267 | 1.44k | connection_(this) { |
3268 | 1.44k | load_consumed_address_validation_tokens(); |
3269 | 1.44k | } |
3270 | | |
3271 | | QuicCore::QuicCore(QuicCoreConfig config) |
3272 | 2.66k | : endpoint_config_(QuicCoreEndpointConfig{ |
3273 | 2.66k | .role = config.role, |
3274 | 2.66k | .supported_versions = config.supported_versions, |
3275 | 2.66k | .verify_peer = config.verify_peer, |
3276 | 2.66k | .application_protocol = config.application_protocol, |
3277 | 2.66k | .identity = config.identity, |
3278 | 2.66k | .transport = config.transport, |
3279 | 2.66k | .allowed_tls_cipher_suites = config.allowed_tls_cipher_suites, |
3280 | 2.66k | .zero_rtt = config.zero_rtt, |
3281 | 2.66k | .qlog = config.qlog, |
3282 | 2.66k | .tls_keylog_path = config.tls_keylog_path, |
3283 | 2.66k | .stateless_reset_secret = config.stateless_reset_secret, |
3284 | 2.66k | .address_validation_token_secret = config.address_validation_token_secret, |
3285 | 2.66k | .previous_address_validation_token_secrets = |
3286 | 2.66k | config.previous_address_validation_token_secrets, |
3287 | 2.66k | .address_validation_replay_store_path = config.address_validation_replay_store_path, |
3288 | 2.66k | .request_forgery_policy = config.request_forgery_policy, |
3289 | 2.66k | .emit_shared_receive_stream_data = config.emit_shared_receive_stream_data, |
3290 | 2.66k | .enable_out_of_order_receive = config.enable_out_of_order_receive, |
3291 | 2.66k | .enable_packet_inspection = config.enable_packet_inspection, |
3292 | 2.66k | .enable_reserved_version_probe = config.enable_reserved_version_probe, |
3293 | 2.66k | .defer_inbound_application_send_drain = config.defer_inbound_application_send_drain, |
3294 | 2.66k | }), |
3295 | 2.66k | legacy_config_(std::move(config)), endpoint_random_(std::random_device{}()), |
3296 | 2.66k | connection_(this) { |
3297 | 2.66k | load_consumed_address_validation_tokens(); |
3298 | 2.66k | static_cast<void>(ensure_legacy_entry()); |
3299 | 2.66k | } |
3300 | | |
3301 | 4.29k | QuicCore::~QuicCore() = default; |
3302 | | |
3303 | | QuicCore::QuicCore(QuicCore &&other) noexcept |
3304 | 184 | : endpoint_config_(std::move(other.endpoint_config_)), |
3305 | 184 | legacy_config_(std::move(other.legacy_config_)), connections_(std::move(other.connections_)), |
3306 | 184 | connection_id_routes_(std::move(other.connection_id_routes_)), |
3307 | 184 | initial_destination_routes_(std::move(other.initial_destination_routes_)), |
3308 | 184 | retry_tokens_(std::move(other.retry_tokens_)), new_tokens_(std::move(other.new_tokens_)), |
3309 | 184 | consumed_address_validation_tokens_(std::move(other.consumed_address_validation_tokens_)), |
3310 | 184 | client_new_tokens_(std::move(other.client_new_tokens_)), |
3311 | 184 | local_stateless_reset_tokens_by_cid_(std::move(other.local_stateless_reset_tokens_by_cid_)), |
3312 | 184 | peer_stateless_reset_tokens_(std::move(other.peer_stateless_reset_tokens_)), |
3313 | 184 | orphan_zero_rtt_buffer_(std::move(other.orphan_zero_rtt_buffer_)), |
3314 | 184 | orphan_zero_rtt_buffer_bytes_(other.orphan_zero_rtt_buffer_bytes_), |
3315 | 184 | legacy_connection_handle_(other.legacy_connection_handle_), |
3316 | 184 | next_connection_handle_(other.next_connection_handle_), |
3317 | 184 | next_server_connection_id_sequence_(other.next_server_connection_id_sequence_), |
3318 | 184 | endpoint_random_(other.endpoint_random_), connection_(this), |
3319 | 184 | wakeup_heap_(std::move(other.wakeup_heap_)), |
3320 | 184 | wakeup_cache_initialized_(other.wakeup_cache_initialized_) { |
3321 | 184 | other.connection_.owner = &other; |
3322 | 184 | other.orphan_zero_rtt_buffer_bytes_ = 0; |
3323 | 184 | other.wakeup_cache_initialized_ = false; |
3324 | 184 | other.wakeup_heap_ = {}; |
3325 | 184 | } |
3326 | | |
3327 | 24 | QuicCore &QuicCore::operator=(QuicCore &&other) noexcept { |
3328 | 24 | if (this == &other) { |
3329 | 8 | return *this; |
3330 | 8 | } |
3331 | 16 | endpoint_config_ = std::move(other.endpoint_config_); |
3332 | 16 | legacy_config_ = std::move(other.legacy_config_); |
3333 | 16 | connections_ = std::move(other.connections_); |
3334 | 16 | connection_id_routes_ = std::move(other.connection_id_routes_); |
3335 | 16 | initial_destination_routes_ = std::move(other.initial_destination_routes_); |
3336 | 16 | retry_tokens_ = std::move(other.retry_tokens_); |
3337 | 16 | new_tokens_ = std::move(other.new_tokens_); |
3338 | 16 | consumed_address_validation_tokens_ = std::move(other.consumed_address_validation_tokens_); |
3339 | 16 | client_new_tokens_ = std::move(other.client_new_tokens_); |
3340 | 16 | local_stateless_reset_tokens_by_cid_ = std::move(other.local_stateless_reset_tokens_by_cid_); |
3341 | 16 | peer_stateless_reset_tokens_ = std::move(other.peer_stateless_reset_tokens_); |
3342 | 16 | orphan_zero_rtt_buffer_ = std::move(other.orphan_zero_rtt_buffer_); |
3343 | 16 | orphan_zero_rtt_buffer_bytes_ = other.orphan_zero_rtt_buffer_bytes_; |
3344 | 16 | legacy_connection_handle_ = other.legacy_connection_handle_; |
3345 | 16 | next_connection_handle_ = other.next_connection_handle_; |
3346 | 16 | next_server_connection_id_sequence_ = other.next_server_connection_id_sequence_; |
3347 | 16 | endpoint_random_ = other.endpoint_random_; |
3348 | 16 | connection_.owner = this; |
3349 | 16 | other.connection_.owner = &other; |
3350 | 16 | other.orphan_zero_rtt_buffer_bytes_ = 0; |
3351 | 16 | wakeup_heap_ = std::move(other.wakeup_heap_); |
3352 | 16 | wakeup_cache_initialized_ = other.wakeup_cache_initialized_; |
3353 | 16 | other.wakeup_cache_initialized_ = false; |
3354 | 16 | other.wakeup_heap_ = {}; |
3355 | 16 | return *this; |
3356 | 24 | } |
3357 | | |
3358 | 3.64k | void QuicCore::rebuild_wakeup_cache() const { |
3359 | 3.64k | wakeup_heap_ = {}; |
3360 | 3.64k | for (const auto &[handle, entry] : connections_) { |
3361 | 3.20k | (void)handle; |
3362 | 3.20k | if (entry.connection == nullptr) { |
3363 | 16 | entry.cached_next_wakeup = std::nullopt; |
3364 | 16 | ++entry.wakeup_generation; |
3365 | 16 | continue; |
3366 | 16 | } |
3367 | 3.19k | entry.cached_next_wakeup = next_entry_wakeup(entry); |
3368 | 3.19k | ++entry.wakeup_generation; |
3369 | 3.19k | if (entry.cached_next_wakeup.has_value()) { |
3370 | 2.86k | wakeup_heap_.push(WakeupHeapEntry{ |
3371 | 2.86k | .wakeup = *entry.cached_next_wakeup, |
3372 | 2.86k | .connection = entry.handle, |
3373 | 2.86k | .generation = entry.wakeup_generation, |
3374 | 2.86k | }); |
3375 | 2.86k | } |
3376 | 3.19k | } |
3377 | 3.64k | wakeup_cache_initialized_ = true; |
3378 | 3.64k | } |
3379 | | |
3380 | 76.5k | void QuicCore::refresh_entry_wakeup(const ConnectionEntry &entry) const { |
3381 | 76.5k | if (!wakeup_cache_initialized_) { |
3382 | 5.67k | return; |
3383 | 5.67k | } |
3384 | 70.9k | entry.cached_next_wakeup = |
3385 | 70.9k | entry.connection == nullptr ? std::optional<QuicCoreTimePoint>{} : next_entry_wakeup(entry); |
3386 | 70.9k | ++entry.wakeup_generation; |
3387 | 70.9k | if (entry.cached_next_wakeup.has_value()) { |
3388 | 68.2k | wakeup_heap_.push(WakeupHeapEntry{ |
3389 | 68.2k | .wakeup = *entry.cached_next_wakeup, |
3390 | 68.2k | .connection = entry.handle, |
3391 | 68.2k | .generation = entry.wakeup_generation, |
3392 | 68.2k | }); |
3393 | 68.2k | } |
3394 | 70.9k | } |
3395 | | |
3396 | 116k | void QuicCore::ensure_wakeup_cache() const { |
3397 | 116k | if (!wakeup_cache_initialized_) { |
3398 | 3.42k | rebuild_wakeup_cache(); |
3399 | 3.42k | } |
3400 | 116k | } |
3401 | | |
3402 | 113k | std::optional<QuicCoreTimePoint> QuicCore::next_wakeup() const { |
3403 | 113k | ensure_wakeup_cache(); |
3404 | 113k | const auto orphan_zero_rtt_expiry = next_orphan_zero_rtt_expiry(); |
3405 | 177k | while (!wakeup_heap_.empty()) { |
3406 | 169k | const auto top = wakeup_heap_.top(); |
3407 | 169k | const auto entry_it = connections_.find(top.connection); |
3408 | 169k | if (entry_it == connections_.end() || entry_it->second.connection == nullptr || |
3409 | 169k | entry_it->second.wakeup_generation != top.generation || |
3410 | 169k | !entry_it->second.cached_next_wakeup.has_value() || |
3411 | 169k | *entry_it->second.cached_next_wakeup != top.wakeup) { |
3412 | 64.1k | wakeup_heap_.pop(); |
3413 | 64.1k | continue; |
3414 | 64.1k | } |
3415 | 105k | if (orphan_zero_rtt_expiry.has_value() && *orphan_zero_rtt_expiry < top.wakeup) { |
3416 | 16 | return orphan_zero_rtt_expiry; |
3417 | 16 | } |
3418 | 105k | return top.wakeup; |
3419 | 105k | } |
3420 | 7.91k | return orphan_zero_rtt_expiry; |
3421 | 113k | } |
3422 | | |
3423 | 2.57k | std::vector<QuicConnectionHandle> QuicCore::due_connection_handles(QuicCoreTimePoint now) const { |
3424 | 2.57k | ensure_wakeup_cache(); |
3425 | 2.57k | std::vector<QuicConnectionHandle> due; |
3426 | 2.78k | for (std::uint8_t attempt = 0; attempt < 2; ++attempt) { |
3427 | 7.19k | while (!wakeup_heap_.empty()) { |
3428 | 4.75k | const auto top = wakeup_heap_.top(); |
3429 | 4.75k | const auto entry_it = connections_.find(top.connection); |
3430 | 4.75k | if (entry_it == connections_.end() || entry_it->second.connection == nullptr || |
3431 | 4.75k | entry_it->second.wakeup_generation != top.generation || |
3432 | 4.75k | !entry_it->second.cached_next_wakeup.has_value() || |
3433 | 4.75k | *entry_it->second.cached_next_wakeup != top.wakeup) { |
3434 | 2.01k | wakeup_heap_.pop(); |
3435 | 2.01k | continue; |
3436 | 2.01k | } |
3437 | 2.74k | if (top.wakeup > now) { |
3438 | 344 | break; |
3439 | 344 | } |
3440 | 2.39k | due.push_back(top.connection); |
3441 | 2.39k | wakeup_heap_.pop(); |
3442 | 2.39k | ++entry_it->second.wakeup_generation; |
3443 | 2.39k | entry_it->second.cached_next_wakeup = std::nullopt; |
3444 | 2.39k | } |
3445 | 2.78k | if (!due.empty() || attempt != 0) { |
3446 | 2.57k | break; |
3447 | 2.57k | } |
3448 | 204 | rebuild_wakeup_cache(); |
3449 | 204 | } |
3450 | 2.57k | return due; |
3451 | 2.57k | } |
3452 | | |
3453 | | void QuicCore::note_send_continuation(ConnectionEntry &entry, const QuicCoreResult &result, |
3454 | 73.6k | QuicCoreTimePoint now) const { |
3455 | 73.6k | store_send_continuation_wakeup(entry, result.send_continuation_pending, now); |
3456 | 73.6k | refresh_entry_wakeup(entry); |
3457 | 73.6k | } |
3458 | | |
3459 | 73.0k | bool QuicCore::take_send_continuation_drain(ConnectionEntry &entry) { |
3460 | 73.0k | const bool continue_paced_burst = entry.send_continuation_drain; |
3461 | 73.0k | entry.send_continuation_drain = false; |
3462 | 73.0k | return continue_paced_burst; |
3463 | 73.0k | } |
3464 | | |
3465 | 18.2k | QuicCoreResult QuicCore::finalize_endpoint_result(QuicCoreResult result, QuicCoreTimePoint now) { |
3466 | 18.2k | result.next_wakeup = next_wakeup(); |
3467 | 18.2k | clamp_result_wakeup_to_now_if_continuation_pending(result, now); |
3468 | 18.2k | return result; |
3469 | 18.2k | } |
3470 | | |
3471 | 56.2k | QuicCoreResult QuicCore::finalize_legacy_result(QuicCoreResult result, QuicCoreTimePoint now) { |
3472 | 56.2k | maybe_note_legacy_send_continuation( |
3473 | 56.2k | legacy_entry(), result, now, |
3474 | 56.2k | [&](ConnectionEntry &entry, const QuicCoreResult &legacy_result, |
3475 | 56.2k | QuicCoreTimePoint wakeup_time) { |
3476 | 56.2k | note_send_continuation(entry, legacy_result, wakeup_time); |
3477 | 56.2k | }); |
3478 | 56.2k | result.next_wakeup = next_wakeup(); |
3479 | 56.2k | clamp_result_wakeup_to_now_if_continuation_pending(result, now); |
3480 | 56.2k | return result; |
3481 | 56.2k | } |
3482 | | |
3483 | 488 | std::size_t QuicCore::connection_count() const { |
3484 | 488 | return connections_.size(); |
3485 | 488 | } |
3486 | | |
3487 | 128 | std::vector<QuicCoreConnectionDiagnostics> QuicCore::connection_diagnostics() const { |
3488 | 128 | std::vector<QuicCoreConnectionDiagnostics> out; |
3489 | 128 | out.reserve(connections_.size()); |
3490 | 128 | for (const auto &[handle, entry] : connections_) { |
3491 | 48 | if (entry.connection == nullptr) { |
3492 | 8 | continue; |
3493 | 8 | } |
3494 | 40 | out.push_back(entry.connection->diagnostics(handle)); |
3495 | 40 | } |
3496 | 128 | return out; |
3497 | 128 | } |
3498 | | |
3499 | 80 | bool QuicCore::has_send_continuation_pending() const { |
3500 | 80 | const bool pending = |
3501 | 80 | std::any_of(connections_.begin(), connections_.end(), [](const auto &entry) { |
3502 | 48 | return entry.second.connection != nullptr && |
3503 | 48 | entry.second.send_continuation_wakeup.has_value(); |
3504 | 48 | }); |
3505 | 80 | if (pending) { |
3506 | 16 | rebuild_wakeup_cache(); |
3507 | 16 | } |
3508 | 80 | return pending; |
3509 | 80 | } |
3510 | | |
3511 | 18.4k | QuicCoreResult QuicCore::advance_endpoint(QuicCoreEndpointInput input, QuicCoreTimePoint now) { |
3512 | 18.4k | return advance_endpoint_impl(std::move(input), now, nullptr); |
3513 | 18.4k | } |
3514 | | |
3515 | | QuicCoreResult QuicCore::advance_endpoint(QuicCoreEndpointInput input, QuicCoreTimePoint now, |
3516 | 8 | QuicCoreSendDatagramSink &send_sink) { |
3517 | 8 | return advance_endpoint_impl(std::move(input), now, &send_sink); |
3518 | 8 | } |
3519 | | |
3520 | | QuicCoreResult QuicCore::advance_endpoint_impl(QuicCoreEndpointInput input, QuicCoreTimePoint now, |
3521 | 18.4k | QuicCoreSendDatagramSink *send_sink) { |
3522 | 18.4k | if (const auto *open = std::get_if<QuicCoreOpenConnection>(&input)) { |
3523 | 624 | if (endpoint_config_.role != EndpointRole::client) { |
3524 | 16 | (void)now; |
3525 | 16 | QuicCoreResult result; |
3526 | 16 | result.local_error = QuicCoreLocalError{ |
3527 | 16 | .connection = std::nullopt, |
3528 | 16 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
3529 | 16 | .stream_id = std::nullopt, |
3530 | 16 | }; |
3531 | 16 | result.next_wakeup = next_wakeup(); |
3532 | 16 | return result; |
3533 | 16 | } |
3534 | | |
3535 | 608 | if (open->connection.source_connection_id.empty() && |
3536 | 608 | std::any_of(connections_.begin(), connections_.end(), [](const auto &connection) { |
3537 | 8 | const auto &entry = connection.second; |
3538 | 8 | return entry.connection != nullptr && |
3539 | 8 | entry.connection->config_.source_connection_id.empty(); |
3540 | 8 | })) { |
3541 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.1 |
3542 | | // # An endpoint MUST NOT use the same IP address and port for |
3543 | | // # multiple concurrent connections with zero-length connection |
3544 | | // # IDs, unless it is certain that those protocol features are not |
3545 | | // # in use. |
3546 | 8 | QuicCoreResult result; |
3547 | 8 | result.local_error = QuicCoreLocalError{ |
3548 | 8 | .connection = std::nullopt, |
3549 | 8 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
3550 | 8 | .stream_id = std::nullopt, |
3551 | 8 | }; |
3552 | 8 | result.next_wakeup = next_wakeup(); |
3553 | 8 | return result; |
3554 | 8 | } |
3555 | | |
3556 | 600 | auto retry_token = open->connection.retry_token; |
3557 | 600 | if (retry_token.empty()) { |
3558 | 552 | if (auto stored_token = take_client_new_token_for_open(open->connection)) { |
3559 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
3560 | | // # When connecting to a server for which the client retains an |
3561 | | // # applicable and unused token, it SHOULD include that token |
3562 | | // # in the Token field of its Initial packet. |
3563 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
3564 | | // # The client MUST NOT use the token provided in a Retry for |
3565 | | // # future connections. |
3566 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
3567 | | // # In comparison, a token obtained in a Retry packet MUST be |
3568 | | // # used immediately during the connection attempt and cannot be |
3569 | | // # used in subsequent connection attempts. |
3570 | 8 | retry_token = std::move(*stored_token); |
3571 | 8 | } |
3572 | 552 | } |
3573 | | |
3574 | 600 | QuicCoreConfig config{ |
3575 | 600 | .role = endpoint_config_.role, |
3576 | 600 | .source_connection_id = open->connection.source_connection_id, |
3577 | 600 | .initial_destination_connection_id = open->connection.initial_destination_connection_id, |
3578 | 600 | .original_destination_connection_id = |
3579 | 600 | open->connection.original_destination_connection_id, |
3580 | 600 | .retry_source_connection_id = open->connection.retry_source_connection_id, |
3581 | 600 | .retry_token = std::move(retry_token), |
3582 | 600 | .original_version = open->connection.original_version, |
3583 | 600 | .initial_version = open->connection.initial_version, |
3584 | 600 | .supported_versions = endpoint_config_.supported_versions, |
3585 | 600 | .reacted_to_version_negotiation = open->connection.reacted_to_version_negotiation, |
3586 | 600 | .verify_peer = endpoint_config_.verify_peer, |
3587 | 600 | .server_name = open->connection.server_name, |
3588 | 600 | .application_protocol = endpoint_config_.application_protocol, |
3589 | 600 | .identity = endpoint_config_.identity, |
3590 | 600 | .transport = endpoint_config_.transport, |
3591 | 600 | .max_outbound_datagram_size = endpoint_config_.max_outbound_datagram_size, |
3592 | 600 | .allowed_tls_cipher_suites = endpoint_config_.allowed_tls_cipher_suites, |
3593 | 600 | .resumption_state = open->connection.resumption_state, |
3594 | 600 | .zero_rtt = open->connection.zero_rtt, |
3595 | 600 | .qlog = endpoint_config_.qlog, |
3596 | 600 | .tls_keylog_path = endpoint_config_.tls_keylog_path, |
3597 | 600 | .stateless_reset_secret = endpoint_config_.stateless_reset_secret, |
3598 | 600 | .address_validation_token_secret = endpoint_config_.address_validation_token_secret, |
3599 | 600 | .previous_address_validation_token_secrets = |
3600 | 600 | endpoint_config_.previous_address_validation_token_secrets, |
3601 | 600 | .address_validation_replay_store_path = |
3602 | 600 | endpoint_config_.address_validation_replay_store_path, |
3603 | 600 | .request_forgery_policy = endpoint_config_.request_forgery_policy, |
3604 | 600 | .emit_shared_receive_stream_data = endpoint_config_.emit_shared_receive_stream_data, |
3605 | 600 | .enable_out_of_order_receive = endpoint_config_.enable_out_of_order_receive, |
3606 | 600 | .enable_packet_inspection = endpoint_config_.enable_packet_inspection, |
3607 | 600 | .enable_reserved_version_probe = endpoint_config_.enable_reserved_version_probe, |
3608 | 600 | }; |
3609 | | |
3610 | 600 | auto handle = next_connection_handle_++; |
3611 | 600 | auto inserted_connection = connections_.try_emplace(handle); |
3612 | 600 | auto connection_iter = inserted_connection.first; |
3613 | 600 | auto &entry = connection_iter->second; |
3614 | 600 | entry = {}; |
3615 | 600 | entry.handle = handle; |
3616 | 600 | entry.default_route_handle = open->initial_route_handle; |
3617 | 600 | entry.connection = std::make_unique<QuicConnection>(std::move(config)); |
3618 | 600 | entry.path_id_by_route_handle.emplace(open->initial_route_handle, 0); |
3619 | 600 | entry.route_handle_by_path_id.emplace(0, open->initial_route_handle); |
3620 | 600 | if (!open->address_validation_identity.empty()) { |
3621 | 112 | if (!address_validation_identity_allowed_for_new_route( |
3622 | 112 | nullptr, open->address_validation_identity)) { |
3623 | 16 | connections_.erase(connection_iter); |
3624 | 16 | QuicCoreResult denied; |
3625 | 16 | denied.local_error = QuicCoreLocalError{ |
3626 | 16 | .connection = handle, |
3627 | 16 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
3628 | 16 | .stream_id = std::nullopt, |
3629 | 16 | }; |
3630 | 16 | return finalize_endpoint_result(std::move(denied), now); |
3631 | 16 | } |
3632 | 96 | entry.address_validation_identity_by_path_id.emplace(0, |
3633 | 96 | open->address_validation_identity); |
3634 | 96 | } |
3635 | 584 | remember_path_address_family( |
3636 | 584 | entry, kDefaultPathId, |
3637 | 584 | route_address_family_from_identity(open->address_validation_identity)); |
3638 | 584 | entry.connection->start(now); |
3639 | 584 | refresh_server_connection_routes(entry); |
3640 | | |
3641 | 584 | auto result = |
3642 | 584 | drain_connection_effects(handle, entry.default_route_handle, |
3643 | 584 | entry.route_handle_by_path_id, *entry.connection, now, |
3644 | 584 | /*continue_paced_burst=*/false, send_sink); |
3645 | 584 | if (endpoint_config_.enable_reserved_version_probe) { |
3646 | 16 | auto probe_bytes = make_reserved_version_probe_packet_bytes(open->connection); |
3647 | 16 | if (!probe_bytes.empty()) { |
3648 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.3 |
3649 | | // # Endpoints MAY send packets with a reserved version to test that a |
3650 | | // # peer correctly discards the packet. |
3651 | 16 | emit_send_datagram(result, |
3652 | 16 | QuicCoreSendDatagram{ |
3653 | 16 | .connection = 0, |
3654 | 16 | .route_handle = open->initial_route_handle, |
3655 | 16 | .bytes = DatagramBuffer(std::move(probe_bytes)), |
3656 | 16 | }, |
3657 | 16 | send_sink); |
3658 | 16 | } |
3659 | 16 | } |
3660 | 584 | result.effects.emplace_back(QuicCoreConnectionLifecycleEvent{ |
3661 | 584 | .connection = handle, |
3662 | 584 | .event = QuicCoreConnectionLifecycle::created, |
3663 | 584 | }); |
3664 | 584 | note_send_continuation(entry, result, now); |
3665 | 584 | return finalize_endpoint_result(std::move(result), now); |
3666 | 600 | } |
3667 | | |
3668 | 17.8k | if (auto *inbound = std::get_if<QuicCoreInboundDatagram>(&input); inbound != nullptr) { |
3669 | 13.6k | QuicCoreResult result; |
3670 | 13.6k | purge_expired_orphan_zero_rtt(now); |
3671 | 13.6k | const auto inbound_payload = inbound->payload(); |
3672 | 13.6k | const auto drain_stateless_reset_owner = [&](QuicConnectionHandle owner) -> bool { |
3673 | 8 | const auto entry_it = connections_.find(owner); |
3674 | 8 | if (entry_it == connections_.end() || entry_it->second.connection == nullptr) { |
3675 | 0 | return false; |
3676 | 0 | } |
3677 | | |
3678 | 8 | entry_it->second.connection->enter_stateless_reset_draining(now); |
3679 | 8 | auto drained = drain_connection_effects( |
3680 | 8 | entry_it->second.handle, entry_it->second.default_route_handle, |
3681 | 8 | entry_it->second.route_handle_by_path_id, *entry_it->second.connection, now, |
3682 | 8 | /*continue_paced_burst=*/false, send_sink); |
3683 | 8 | append_result(result, std::move(drained)); |
3684 | 8 | bool remove_entry = |
3685 | 8 | should_remove_endpoint_connection_entry(*entry_it->second.connection, result, now); |
3686 | 8 | refresh_server_connection_routes(entry_it->second); |
3687 | 8 | if (remove_entry) { |
3688 | 0 | retire_endpoint_connection_routes(entry_it->second, now); |
3689 | 0 | ++entry_it->second.wakeup_generation; |
3690 | 0 | connections_.erase(entry_it); |
3691 | 8 | } else { |
3692 | 8 | refresh_entry_wakeup(entry_it->second); |
3693 | 8 | } |
3694 | 8 | return true; |
3695 | 8 | }; |
3696 | | |
3697 | 13.6k | if (endpoint_config_.role == EndpointRole::client && |
3698 | 13.6k | parse_version_negotiation_packet(inbound_payload).has_value()) { |
3699 | 40 | for (auto &[handle, entry] : connections_) { |
3700 | 40 | (void)handle; |
3701 | 40 | if (entry.connection == nullptr) { |
3702 | 0 | continue; |
3703 | 0 | } |
3704 | 40 | const auto path_id = path_id_for_inbound_route( |
3705 | 40 | entry, inbound->route_handle, inbound->address_validation_identity); |
3706 | 40 | if (!path_id.has_value()) { |
3707 | 0 | continue; |
3708 | 0 | } |
3709 | 40 | auto version_negotiation = maybe_process_client_endpoint_version_negotiation( |
3710 | 40 | entry, inbound_payload, inbound->route_handle, *path_id, now, send_sink); |
3711 | 40 | if (!version_negotiation.has_value()) { |
3712 | 16 | continue; |
3713 | 16 | } |
3714 | 24 | if (should_remove_endpoint_connection_entry(*entry.connection, *version_negotiation, |
3715 | 24 | now)) { |
3716 | 0 | retire_endpoint_connection_routes(entry, now); |
3717 | 0 | ++entry.wakeup_generation; |
3718 | 0 | connections_.erase(handle); |
3719 | 24 | } else { |
3720 | 24 | refresh_entry_wakeup(entry); |
3721 | 24 | } |
3722 | 24 | return finalize_endpoint_result(std::move(*version_negotiation), now); |
3723 | 40 | } |
3724 | 40 | } |
3725 | | |
3726 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-12.2 |
3727 | | // # Receivers MAY route based on the information in the first packet |
3728 | | // # contained in a UDP datagram. |
3729 | 13.6k | auto parsed = |
3730 | 13.6k | parse_endpoint_datagram(inbound_payload, endpoint_config_.transport.grease_quic_bit); |
3731 | 13.6k | if (!parsed.has_value()) { |
3732 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
3733 | | // # However, the comparison MUST be performed when the first |
3734 | | // # packet in an incoming datagram either cannot be associated with |
3735 | | // # a connection or cannot be decrypted. |
3736 | 144 | if (const auto reset_owner = detect_stateless_reset(inbound_payload); |
3737 | 144 | reset_owner.has_value()) { |
3738 | 0 | static_cast<void>(drain_stateless_reset_owner(*reset_owner)); |
3739 | 0 | } |
3740 | 144 | return finalize_endpoint_result(std::move(result), now); |
3741 | 144 | } |
3742 | | |
3743 | 13.5k | if (const auto handle = find_endpoint_connection_for_datagram(*parsed); |
3744 | 13.5k | handle.has_value()) { |
3745 | 12.9k | auto entry_it = connections_.find(*handle); |
3746 | 12.9k | if (entry_it != connections_.end()) { |
3747 | 12.9k | auto &entry = entry_it->second; |
3748 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-11 |
3749 | | // # A stateless reset MUST NOT be used by an endpoint that has |
3750 | | // # the state necessary to send a frame on the connection. |
3751 | 12.9k | const auto path_id = path_id_for_inbound_route( |
3752 | 12.9k | entry, inbound->route_handle, inbound->address_validation_identity); |
3753 | 12.9k | if (!path_id.has_value()) { |
3754 | 0 | return finalize_endpoint_result(std::move(result), now); |
3755 | 0 | } |
3756 | 12.9k | QuicInboundDatagramResult inbound_result; |
3757 | 12.9k | if (inbound->shared_bytes != nullptr) { |
3758 | 0 | inbound_result = entry.connection->process_inbound_datagram_shared( |
3759 | 0 | std::move(inbound->shared_bytes), inbound->begin, inbound->end, now, |
3760 | 0 | *path_id, inbound->ecn); |
3761 | 12.9k | } else { |
3762 | 12.9k | inbound_result = entry.connection->process_inbound_datagram_owned( |
3763 | 12.9k | std::move(inbound->bytes), now, *path_id, inbound->ecn); |
3764 | 12.9k | } |
3765 | 12.9k | if (!inbound_result.processed_any_packet) { |
3766 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
3767 | | // # However, the comparison MUST be performed when the |
3768 | | // # first packet in an incoming datagram either cannot be |
3769 | | // # associated with a connection or cannot be decrypted. |
3770 | 8 | if (const auto reset_owner = detect_stateless_reset(inbound_payload); |
3771 | 8 | reset_owner.has_value()) { |
3772 | 0 | static_cast<void>(drain_stateless_reset_owner(*reset_owner)); |
3773 | 0 | return finalize_endpoint_result(std::move(result), now); |
3774 | 0 | } |
3775 | 12.9k | } else { |
3776 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
3777 | | // # Endpoints MAY skip this check if any packet from a |
3778 | | // # datagram is successfully processed. |
3779 | 12.9k | } |
3780 | | |
3781 | 12.9k | const bool defer_send_drain = |
3782 | 12.9k | endpoint_config_.defer_inbound_application_send_drain && |
3783 | 12.9k | inbound_result.processed_any_packet && |
3784 | 12.9k | (!entry.connection->pending_stream_receive_effects_.empty() || |
3785 | 8 | !entry.connection->pending_datagram_receive_effects_.empty()); |
3786 | 12.9k | auto drained = |
3787 | 12.9k | defer_send_drain |
3788 | 12.9k | ? take_connection_non_send_effects(entry.handle, *entry.connection) |
3789 | 12.9k | : drain_connection_effects(entry.handle, entry.default_route_handle, |
3790 | 12.9k | entry.route_handle_by_path_id, *entry.connection, |
3791 | 12.9k | now, take_send_continuation_drain(entry), |
3792 | 12.9k | send_sink); |
3793 | 12.9k | if (!defer_send_drain) { |
3794 | 12.9k | drain_queued_server_new_token(entry, drained, now, send_sink); |
3795 | 12.9k | } |
3796 | 12.9k | bool remove_entry = |
3797 | 12.9k | should_remove_endpoint_connection_entry(*entry.connection, drained, now); |
3798 | 12.9k | remember_client_new_tokens(entry, drained); |
3799 | 12.9k | if (defer_send_drain) { |
3800 | 8 | refresh_entry_wakeup(entry); |
3801 | 12.9k | } else { |
3802 | 12.9k | note_send_continuation(entry, drained, now); |
3803 | 12.9k | } |
3804 | 12.9k | append_result(result, std::move(drained)); |
3805 | 12.9k | refresh_server_connection_routes(entry); |
3806 | 12.9k | if (remove_entry) { |
3807 | 8 | retire_endpoint_connection_routes(entry, now); |
3808 | 8 | ++entry.wakeup_generation; |
3809 | 8 | connections_.erase(entry_it); |
3810 | 8 | } |
3811 | 12.9k | return finalize_endpoint_result(std::move(result), now); |
3812 | 12.9k | } |
3813 | 12.9k | } |
3814 | | |
3815 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1 |
3816 | | // # However, the comparison MUST be performed when the first packet in |
3817 | | // # an incoming datagram either cannot be associated with a connection |
3818 | | // # or cannot be decrypted. |
3819 | 516 | if (const auto reset_owner = detect_stateless_reset(inbound_payload); |
3820 | 516 | reset_owner.has_value()) { |
3821 | 8 | static_cast<void>(drain_stateless_reset_owner(*reset_owner)); |
3822 | 8 | return finalize_endpoint_result(std::move(result), now); |
3823 | 8 | } |
3824 | | |
3825 | 508 | if (endpoint_config_.role != EndpointRole::server) { |
3826 | 8 | return finalize_endpoint_result(std::move(result), now); |
3827 | 8 | } |
3828 | | |
3829 | 500 | const bool endpoint_supports_version = |
3830 | 500 | std::find(endpoint_config_.supported_versions.begin(), |
3831 | 500 | endpoint_config_.supported_versions.end(), |
3832 | 500 | parsed->version) != endpoint_config_.supported_versions.end(); |
3833 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.1 |
3834 | | // # Version-specific rules for the connection ID therefore MUST NOT |
3835 | | // # influence a decision about whether to send a Version Negotiation |
3836 | | // # packet. |
3837 | 500 | bool should_send_version_negotiation = |
3838 | 500 | parsed->kind == ParsedEndpointDatagram::Kind::unsupported_version_long_header || |
3839 | 500 | ((parsed->kind == ParsedEndpointDatagram::Kind::supported_initial || |
3840 | 464 | parsed->kind == ParsedEndpointDatagram::Kind::supported_zero_rtt || |
3841 | 464 | parsed->kind == ParsedEndpointDatagram::Kind::supported_long_header) && |
3842 | 464 | !endpoint_supports_version); |
3843 | 500 | if (should_send_version_negotiation) { |
3844 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3845 | | // # A server MAY limit the number of |
3846 | | // # packets to which it responds with a Version Negotiation packet. |
3847 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.1 |
3848 | | // # A server MAY limit the number of Version Negotiation packets it |
3849 | | // # sends. |
3850 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3851 | | // # If a server receives a packet that indicates an unsupported version |
3852 | | // # and if the packet is large enough to initiate a new connection for |
3853 | | // # any supported version, the server SHOULD send a Version Negotiation |
3854 | | // # packet as described in Section 6.1. |
3855 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3856 | | // # Servers SHOULD respond with a Version |
3857 | | // # Negotiation packet, provided that the datagram is sufficiently long. |
3858 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3859 | | // # Servers MUST drop smaller packets that specify unsupported versions. |
3860 | 44 | if (inbound_payload.size() >= kMinimumClientInitialDatagramBytes) { |
3861 | 32 | const auto advertised_versions = endpoint_config_.supported_versions; |
3862 | 32 | auto bytes = make_version_negotiation_packet_bytes( |
3863 | 32 | *parsed, advertised_versions, |
3864 | 32 | endpoint_config_.transport.grease_reserved_versions); |
3865 | 32 | if (!bytes.empty()) { |
3866 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.1 |
3867 | | // # A server MUST NOT send more than one Version Negotiation packet |
3868 | | // # in response to a single UDP datagram. |
3869 | 24 | emit_send_datagram(result, |
3870 | 24 | QuicCoreSendDatagram{ |
3871 | 24 | .connection = 0, |
3872 | 24 | .route_handle = inbound->route_handle, |
3873 | 24 | .bytes = DatagramBuffer(std::move(bytes)), |
3874 | 24 | }, |
3875 | 24 | send_sink); |
3876 | 24 | } |
3877 | 32 | } |
3878 | 44 | return finalize_endpoint_result(std::move(result), now); |
3879 | 44 | } |
3880 | | |
3881 | 456 | if (parsed->kind != ParsedEndpointDatagram::Kind::supported_initial) { |
3882 | 100 | if (maybe_buffer_orphan_zero_rtt(*parsed, *inbound, now)) { |
3883 | 56 | return finalize_endpoint_result(std::move(result), now); |
3884 | 56 | } |
3885 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3886 | | // # Clients are not able to send Handshake packets prior to |
3887 | | // # receiving a server response, so servers SHOULD ignore any such |
3888 | | // # packets. |
3889 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3890 | | // # Servers MUST drop incoming packets under all other circumstances. |
3891 | 44 | if (auto reset = make_stateless_reset_for_unknown_cid(*parsed, inbound_payload, |
3892 | 44 | inbound->route_handle, now)) { |
3893 | 0 | emit_send_datagram(result, std::move(*reset), send_sink); |
3894 | 0 | } |
3895 | 44 | return finalize_endpoint_result(std::move(result), now); |
3896 | 100 | } |
3897 | | |
3898 | 356 | if (parsed->destination_connection_id.size() < 8) { |
3899 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-7.2 |
3900 | | // # This Destination Connection ID MUST be at least 8 bytes in |
3901 | | // # length. |
3902 | 8 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
3903 | 8 | inbound->address_validation_identity, now); |
3904 | 8 | return finalize_endpoint_result(std::move(result), now); |
3905 | 8 | } |
3906 | | |
3907 | 348 | if (inbound_payload.size() < kMinimumClientInitialDatagramBytes) { |
3908 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14.1 |
3909 | | // # A server MUST discard an Initial packet that is carried in a |
3910 | | // # UDP datagram with a payload that is smaller than the smallest |
3911 | | // # allowed maximum datagram size of 1200 bytes. |
3912 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14 |
3913 | | // # Therefore, an endpoint MUST NOT close a connection when it |
3914 | | // # receives a datagram that does not meet size constraints; the |
3915 | | // # endpoint MAY discard such datagrams. |
3916 | 8 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
3917 | 8 | inbound->address_validation_identity, now); |
3918 | 8 | return finalize_endpoint_result(std::move(result), now); |
3919 | 8 | } |
3920 | 340 | if (!address_validation_identity_allowed_for_new_route( |
3921 | 340 | nullptr, inbound->address_validation_identity)) { |
3922 | 16 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
3923 | 16 | inbound->address_validation_identity, now); |
3924 | 16 | return finalize_endpoint_result(std::move(result), now); |
3925 | 16 | } |
3926 | | |
3927 | 324 | if (endpoint_config_.max_server_connections != 0 && |
3928 | 324 | connections_.size() >= endpoint_config_.max_server_connections) { |
3929 | 8 | auto close_bytes = make_connection_refused_close_packet_bytes(*parsed); |
3930 | 8 | if (!close_bytes.empty()) { |
3931 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-5.2.2 |
3932 | | // # If a server refuses to accept a new connection, it SHOULD |
3933 | | // # send an Initial packet containing a CONNECTION_CLOSE frame |
3934 | | // # with error code CONNECTION_REFUSED. |
3935 | 8 | emit_send_datagram(result, |
3936 | 8 | QuicCoreSendDatagram{ |
3937 | 8 | .connection = 0, |
3938 | 8 | .route_handle = inbound->route_handle, |
3939 | 8 | .bytes = DatagramBuffer(std::move(close_bytes)), |
3940 | 8 | }, |
3941 | 8 | send_sink); |
3942 | 8 | } |
3943 | 8 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
3944 | 8 | inbound->address_validation_identity, now); |
3945 | 8 | return finalize_endpoint_result(std::move(result), now); |
3946 | 8 | } |
3947 | | |
3948 | 316 | const auto token_classification = classify_address_validation_token(*parsed); |
3949 | 316 | std::optional<PendingRetryToken> retry_context; |
3950 | 316 | std::optional<StoredEndpointNewToken> new_token_context; |
3951 | 316 | if (!parsed->token.empty()) { |
3952 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
3953 | | // # When a server receives an Initial packet with an address |
3954 | | // # validation token, it MUST attempt to validate the token, |
3955 | | // # unless it has already completed address validation. |
3956 | 56 | new_token_context = take_new_token_context(*parsed, inbound->route_handle, now, |
3957 | 56 | inbound->address_validation_identity); |
3958 | 56 | } |
3959 | 316 | if (endpoint_config_.retry_enabled) { |
3960 | 104 | retry_context = take_retry_context(*parsed, inbound->route_handle, now, |
3961 | 104 | inbound->address_validation_identity); |
3962 | 104 | if (!retry_context.has_value()) { |
3963 | 88 | if (token_classification == AddressValidationTokenClassification::retry) { |
3964 | 8 | auto close_bytes = make_invalid_retry_token_close_packet_bytes(*parsed); |
3965 | 8 | if (!close_bytes.empty()) { |
3966 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.2 |
3967 | | // # Instead, the server SHOULD immediately close |
3968 | | // # (Section 10.2) the connection with an INVALID_TOKEN |
3969 | | // # error. |
3970 | 8 | emit_send_datagram(result, |
3971 | 8 | QuicCoreSendDatagram{ |
3972 | 8 | .connection = 0, |
3973 | 8 | .route_handle = inbound->route_handle, |
3974 | 8 | .bytes = DatagramBuffer(std::move(close_bytes)), |
3975 | 8 | }, |
3976 | 8 | send_sink); |
3977 | 8 | } |
3978 | 8 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
3979 | 8 | inbound->address_validation_identity, now); |
3980 | 8 | return finalize_endpoint_result(std::move(result), now); |
3981 | 8 | } |
3982 | | |
3983 | 80 | const auto sequence = next_server_connection_id_sequence_++; |
3984 | 80 | auto retry_source_connection_id = make_endpoint_connection_id( |
3985 | 80 | kServerConnectionIdPrefix, sequence, endpoint_random_); |
3986 | 80 | PendingRetryToken pending{ |
3987 | 80 | .original_destination_connection_id = parsed->destination_connection_id, |
3988 | 80 | .retry_source_connection_id = retry_source_connection_id, |
3989 | 80 | .original_version = parsed->version, |
3990 | 80 | .token = make_endpoint_retry_token( |
3991 | 80 | sequence, &*parsed, &retry_source_connection_id, inbound->route_handle, |
3992 | 80 | inbound->address_validation_identity, now), |
3993 | 80 | .route_handle = inbound->route_handle, |
3994 | 80 | .address_validation_identity = inbound->address_validation_identity, |
3995 | 80 | .expires_at = now + kRetryTokenLifetime, |
3996 | 80 | }; |
3997 | 80 | retry_tokens_.insert_or_assign(connection_id_key(pending.token), pending); |
3998 | | |
3999 | 80 | auto bytes = make_retry_packet_bytes(*parsed, pending); |
4000 | 80 | if (!bytes.empty()) { |
4001 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
4002 | | // # If the token is invalid, then the server SHOULD proceed |
4003 | | // # as if the client did not have a validated address, |
4004 | | // # including potentially sending a Retry packet. |
4005 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.1 |
4006 | | // # A server MAY send Retry packets in response to Initial |
4007 | | // # and 0-RTT packets. |
4008 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.1 |
4009 | | // # A server MUST NOT send more than one Retry |
4010 | | // # packet in response to a single UDP datagram. |
4011 | 72 | emit_send_datagram(result, |
4012 | 72 | QuicCoreSendDatagram{ |
4013 | 72 | .connection = 0, |
4014 | 72 | .route_handle = inbound->route_handle, |
4015 | 72 | .bytes = DatagramBuffer(std::move(bytes)), |
4016 | 72 | }, |
4017 | 72 | send_sink); |
4018 | 72 | } |
4019 | 80 | drop_matching_orphan_zero_rtt(*parsed, inbound->route_handle, |
4020 | 80 | inbound->address_validation_identity, now); |
4021 | 80 | return finalize_endpoint_result(std::move(result), now); |
4022 | 88 | } |
4023 | 104 | } |
4024 | | |
4025 | 228 | QuicCoreConfig config{ |
4026 | 228 | .role = EndpointRole::server, |
4027 | 228 | .source_connection_id = |
4028 | 228 | retry_context.has_value() |
4029 | 228 | ? retry_context->retry_source_connection_id |
4030 | 228 | : make_endpoint_connection_id(kServerConnectionIdPrefix, |
4031 | 212 | next_server_connection_id_sequence_++, |
4032 | 212 | endpoint_random_), |
4033 | 228 | .original_version = parsed->version, |
4034 | 228 | .initial_version = parsed->version, |
4035 | 228 | .supported_versions = endpoint_config_.supported_versions, |
4036 | 228 | .verify_peer = endpoint_config_.verify_peer, |
4037 | 228 | .application_protocol = endpoint_config_.application_protocol, |
4038 | 228 | .identity = endpoint_config_.identity, |
4039 | 228 | .transport = endpoint_config_.transport, |
4040 | 228 | .max_outbound_datagram_size = endpoint_config_.max_outbound_datagram_size, |
4041 | 228 | .allowed_tls_cipher_suites = endpoint_config_.allowed_tls_cipher_suites, |
4042 | 228 | .zero_rtt = endpoint_config_.zero_rtt, |
4043 | 228 | .qlog = endpoint_config_.qlog, |
4044 | 228 | .tls_keylog_path = endpoint_config_.tls_keylog_path, |
4045 | 228 | .stateless_reset_secret = endpoint_config_.stateless_reset_secret, |
4046 | 228 | .address_validation_token_secret = endpoint_config_.address_validation_token_secret, |
4047 | 228 | .previous_address_validation_token_secrets = |
4048 | 228 | endpoint_config_.previous_address_validation_token_secrets, |
4049 | 228 | .address_validation_replay_store_path = |
4050 | 228 | endpoint_config_.address_validation_replay_store_path, |
4051 | 228 | .request_forgery_policy = endpoint_config_.request_forgery_policy, |
4052 | 228 | .emit_shared_receive_stream_data = endpoint_config_.emit_shared_receive_stream_data, |
4053 | 228 | .enable_out_of_order_receive = endpoint_config_.enable_out_of_order_receive, |
4054 | 228 | .enable_packet_inspection = endpoint_config_.enable_packet_inspection, |
4055 | 228 | }; |
4056 | 228 | if (retry_context.has_value()) { |
4057 | 16 | config.initial_destination_connection_id = retry_context->retry_source_connection_id; |
4058 | 16 | config.original_destination_connection_id = |
4059 | 16 | retry_context->original_destination_connection_id; |
4060 | 16 | config.retry_source_connection_id = retry_context->retry_source_connection_id; |
4061 | 16 | config.original_version = retry_context->original_version; |
4062 | 16 | config.initial_version = retry_context->original_version; |
4063 | 16 | } |
4064 | | |
4065 | 228 | auto entry = ConnectionEntry{ |
4066 | 228 | .handle = next_connection_handle_++, |
4067 | 228 | .default_route_handle = inbound->route_handle, |
4068 | 228 | .connection = std::make_unique<QuicConnection>(std::move(config)), |
4069 | 228 | }; |
4070 | 228 | auto path_id = inbound->route_handle.has_value() |
4071 | 228 | ? remember_inbound_path(entry, *inbound->route_handle, |
4072 | 220 | inbound->address_validation_identity) |
4073 | 228 | : kDefaultPathId; |
4074 | 228 | if (!inbound->route_handle.has_value() && !inbound->address_validation_identity.empty()) { |
4075 | 0 | entry.address_validation_identity_by_path_id[path_id] = |
4076 | 0 | inbound->address_validation_identity; |
4077 | 0 | } |
4078 | 228 | if (inbound->shared_bytes != nullptr) { |
4079 | 0 | entry.connection->process_inbound_datagram_shared(std::move(inbound->shared_bytes), |
4080 | 0 | inbound->begin, inbound->end, now, |
4081 | 0 | path_id, inbound->ecn); |
4082 | 228 | } else { |
4083 | 228 | entry.connection->process_inbound_datagram_owned(std::move(inbound->bytes), now, |
4084 | 228 | path_id, inbound->ecn); |
4085 | 228 | } |
4086 | 228 | auto buffered_zero_rtt = take_matching_orphan_zero_rtt(*parsed, *inbound, now); |
4087 | 228 | for (auto &buffered : buffered_zero_rtt) { |
4088 | 16 | entry.connection->process_inbound_datagram_owned(std::move(buffered.bytes), now, |
4089 | 16 | path_id, buffered.ecn); |
4090 | 16 | } |
4091 | 228 | if (new_token_context.has_value()) { |
4092 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
4093 | | // # If the validation succeeds, the server SHOULD then allow the |
4094 | | // # handshake to proceed. |
4095 | 8 | entry.connection->mark_peer_address_validated(); |
4096 | 8 | } |
4097 | | |
4098 | 228 | auto drained = |
4099 | 228 | drain_connection_effects(entry.handle, entry.default_route_handle, |
4100 | 228 | entry.route_handle_by_path_id, *entry.connection, now, |
4101 | 228 | /*continue_paced_burst=*/false, send_sink); |
4102 | 228 | drain_queued_server_new_token(entry, drained, now, send_sink); |
4103 | 228 | bool keep_entry = should_keep_endpoint_connection_entry(*entry.connection, drained, now); |
4104 | 228 | append_result(result, std::move(drained)); |
4105 | 228 | result.effects.insert(result.effects.begin(), |
4106 | 228 | QuicCoreConnectionLifecycleEvent{ |
4107 | 228 | .connection = entry.handle, |
4108 | 228 | .event = QuicCoreConnectionLifecycle::accepted, |
4109 | 228 | }); |
4110 | | |
4111 | 228 | if (keep_entry) { |
4112 | 200 | const auto handle = entry.handle; |
4113 | 200 | store_send_continuation_wakeup(entry, result.send_continuation_pending, now); |
4114 | 200 | auto inserted_connection = connections_.emplace(handle, std::move(entry)); |
4115 | 200 | auto connection_iter = inserted_connection.first; |
4116 | 200 | refresh_entry_wakeup(connection_iter->second); |
4117 | 200 | refresh_server_connection_routes(connection_iter->second); |
4118 | 200 | } |
4119 | 228 | return finalize_endpoint_result(std::move(result), now); |
4120 | 316 | } |
4121 | | |
4122 | 4.13k | if (const auto *mtu = std::get_if<QuicCorePathMtuUpdate>(&input); mtu != nullptr) { |
4123 | 48 | QuicCoreResult result; |
4124 | 64 | for (auto &[handle, entry] : connections_) { |
4125 | 64 | static_cast<void>(handle); |
4126 | 64 | const auto path_id = path_id_for_route_handle(entry, mtu->route_handle); |
4127 | 64 | if (!path_id.has_value()) { |
4128 | 32 | continue; |
4129 | 32 | } |
4130 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14.2.1 |
4131 | | // # QUIC endpoints using PMTUD SHOULD validate ICMP messages to |
4132 | | // # protect from packet injection as specified in [RFC8201] and |
4133 | | // # Section 5.2 of [RFC8085]. |
4134 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-14.2.1 |
4135 | | // # This validation SHOULD use the quoted packet supplied in the |
4136 | | // # payload of an ICMP message to associate the message with a |
4137 | | // # corresponding transport connection (see Section 4.6.1 of |
4138 | | // # [DPLPMTUD]). |
4139 | 32 | if (!path_mtu_update_matches_connection(entry, *mtu)) { |
4140 | 8 | continue; |
4141 | 8 | } |
4142 | 24 | entry.connection->apply_path_mtu_update(*path_id, mtu->max_udp_payload_size); |
4143 | 24 | auto drained = drain_connection_effects( |
4144 | 24 | entry.handle, entry.default_route_handle, entry.route_handle_by_path_id, |
4145 | 24 | *entry.connection, now, take_send_continuation_drain(entry), send_sink); |
4146 | 24 | note_send_continuation(entry, drained, now); |
4147 | 24 | append_result(result, std::move(drained)); |
4148 | 24 | refresh_server_connection_routes(entry); |
4149 | 24 | break; |
4150 | 32 | } |
4151 | 48 | return finalize_endpoint_result(std::move(result), now); |
4152 | 48 | } |
4153 | | |
4154 | 4.08k | if (const auto *command = std::get_if<QuicCoreConnectionCommand>(&input)) { |
4155 | 1.50k | auto entry_it = connections_.find(command->connection); |
4156 | 1.50k | if (entry_it == connections_.end()) { |
4157 | 136 | return QuicCoreResult{ |
4158 | 136 | .next_wakeup = next_wakeup(), |
4159 | 136 | .local_error = |
4160 | 136 | QuicCoreLocalError{ |
4161 | 136 | .connection = command->connection, |
4162 | 136 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4163 | 136 | .stream_id = std::nullopt, |
4164 | 136 | }, |
4165 | 136 | }; |
4166 | 136 | } |
4167 | | |
4168 | 1.37k | auto &entry = entry_it->second; |
4169 | 1.37k | QuicCoreResult result; |
4170 | 1.37k | std::visit( |
4171 | 1.37k | overloaded{ |
4172 | 1.37k | [&](const QuicCoreSendStreamData &in) { |
4173 | 1.12k | const auto queued = entry.connection->queue_stream_send(in.stream_id, in.bytes, |
4174 | 1.12k | in.fin, in.priority); |
4175 | 1.12k | if (!queued.has_value()) { |
4176 | 48 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4177 | 48 | result.local_error->connection = entry.handle; |
4178 | 48 | } |
4179 | 1.12k | }, |
4180 | 1.37k | [&](const QuicCoreSendSharedStreamData &in) { |
4181 | 16 | const auto queued = entry.connection->queue_stream_send_shared( |
4182 | 16 | in.stream_id, in.bytes, in.fin, in.priority); |
4183 | 16 | if (!queued.has_value()) { |
4184 | 8 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4185 | 8 | result.local_error->connection = entry.handle; |
4186 | 8 | } |
4187 | 16 | }, |
4188 | 1.37k | [&](const QuicCoreSendDatagramData &in) { |
4189 | 32 | const auto queued = |
4190 | 32 | entry.connection->queue_datagram_send(in.bytes, in.priority); |
4191 | 32 | if (!queued.has_value()) { |
4192 | 24 | result.local_error = datagram_send_error_to_local_error(queued.error()); |
4193 | 24 | result.local_error->connection = entry.handle; |
4194 | 24 | } |
4195 | 32 | }, |
4196 | 1.37k | [&](const QuicCoreSendSharedDatagramData &in) { |
4197 | 16 | const auto queued = |
4198 | 16 | entry.connection->queue_datagram_send_shared(in.bytes, in.priority); |
4199 | 16 | if (!queued.has_value()) { |
4200 | 8 | result.local_error = datagram_send_error_to_local_error(queued.error()); |
4201 | 8 | result.local_error->connection = entry.handle; |
4202 | 8 | } |
4203 | 16 | }, |
4204 | 1.37k | [&](const QuicCoreResetStream &in) { |
4205 | 40 | const auto queued = entry.connection->queue_stream_reset(LocalResetCommand{ |
4206 | 40 | .stream_id = in.stream_id, |
4207 | 40 | .application_error_code = in.application_error_code, |
4208 | 40 | }); |
4209 | 40 | if (!queued.has_value()) { |
4210 | 32 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4211 | 32 | result.local_error->connection = entry.handle; |
4212 | 32 | } |
4213 | 40 | }, |
4214 | 1.37k | [&](const QuicCoreStopSending &in) { |
4215 | 40 | const auto queued = |
4216 | 40 | entry.connection->queue_stop_sending(LocalStopSendingCommand{ |
4217 | 40 | .stream_id = in.stream_id, |
4218 | 40 | .application_error_code = in.application_error_code, |
4219 | 40 | }); |
4220 | 40 | if (!queued.has_value()) { |
4221 | 32 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4222 | 32 | result.local_error->connection = entry.handle; |
4223 | 32 | } |
4224 | 40 | }, |
4225 | 1.37k | [&](const QuicCoreCloseConnection &in) { |
4226 | 24 | static_cast<void>( |
4227 | 24 | entry.connection->queue_application_close(LocalApplicationCloseCommand{ |
4228 | 24 | .application_error_code = in.application_error_code, |
4229 | 24 | .reason_phrase = in.reason_phrase, |
4230 | 24 | })); |
4231 | 24 | }, |
4232 | 1.37k | [&](const QuicCoreRequestKeyUpdate &) { entry.connection->request_key_update(); }, |
4233 | 1.37k | [&](const QuicCoreRequestConnectionMigration &in) { |
4234 | 56 | if (!endpoint_config_.allow_peer_address_change) { |
4235 | 8 | result.local_error = QuicCoreLocalError{ |
4236 | 8 | .connection = entry.handle, |
4237 | 8 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4238 | 8 | .stream_id = std::nullopt, |
4239 | 8 | }; |
4240 | 8 | return; |
4241 | 8 | } |
4242 | 48 | const auto effective_identity = effective_address_validation_identity_for_route( |
4243 | 48 | entry, in.route_handle, in.address_validation_identity); |
4244 | 48 | if (!address_validation_identity_allowed_for_new_route(&entry, |
4245 | 48 | effective_identity)) { |
4246 | 16 | result.local_error = QuicCoreLocalError{ |
4247 | 16 | .connection = entry.handle, |
4248 | 16 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4249 | 16 | .stream_id = std::nullopt, |
4250 | 16 | }; |
4251 | 16 | return; |
4252 | 16 | } |
4253 | 32 | if (in.reason == QuicMigrationRequestReason::preferred_address && |
4254 | 32 | !preferred_address_migration_route_family_allowed(entry, |
4255 | 16 | effective_identity)) { |
4256 | 8 | result.local_error = QuicCoreLocalError{ |
4257 | 8 | .connection = entry.handle, |
4258 | 8 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4259 | 8 | .stream_id = std::nullopt, |
4260 | 8 | }; |
4261 | 8 | return; |
4262 | 8 | } |
4263 | 24 | const auto path_id = |
4264 | 24 | remember_inbound_path(entry, in.route_handle, effective_identity); |
4265 | 24 | auto requested = |
4266 | 24 | entry.connection->request_connection_migration(path_id, in.reason, now); |
4267 | 24 | if (!requested.has_value()) { |
4268 | 8 | result.local_error = QuicCoreLocalError{ |
4269 | 8 | .connection = entry.handle, |
4270 | 8 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4271 | 8 | .stream_id = std::nullopt, |
4272 | 8 | }; |
4273 | 8 | } |
4274 | 24 | }, |
4275 | 1.37k | [&](const auto &) {}, |
4276 | 1.37k | }, |
4277 | 1.37k | command->input); |
4278 | | |
4279 | 1.37k | auto drained = drain_connection_effects( |
4280 | 1.37k | entry.handle, entry.default_route_handle, entry.route_handle_by_path_id, |
4281 | 1.37k | *entry.connection, now, take_send_continuation_drain(entry), send_sink); |
4282 | 1.37k | bool remove_entry = |
4283 | 1.37k | should_remove_endpoint_connection_entry(*entry.connection, drained, now); |
4284 | 1.37k | note_send_continuation(entry, drained, now); |
4285 | 1.37k | append_result(result, std::move(drained)); |
4286 | 1.37k | refresh_server_connection_routes(entry); |
4287 | 1.37k | if (remove_entry) { |
4288 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-10.2 |
4289 | | // # Once its closing or draining state ends, an endpoint SHOULD |
4290 | | // # discard all connection state. |
4291 | 8 | retire_endpoint_connection_routes(entry, now); |
4292 | 8 | ++entry.wakeup_generation; |
4293 | 8 | connections_.erase(entry_it); |
4294 | 8 | } |
4295 | 1.37k | return finalize_endpoint_result(std::move(result), now); |
4296 | 1.50k | } |
4297 | | |
4298 | 2.57k | purge_expired_orphan_zero_rtt(now); |
4299 | 2.57k | QuicCoreResult result; |
4300 | 2.57k | for (const auto handle : due_connection_handles(now)) { |
4301 | 2.39k | auto entry_it = connections_.find(handle); |
4302 | 2.39k | if (entry_it == connections_.end() || entry_it->second.connection == nullptr) { |
4303 | 0 | continue; |
4304 | 0 | } |
4305 | 2.39k | auto &entry = entry_it->second; |
4306 | | |
4307 | 2.39k | const bool continue_paced_burst = take_send_continuation_drain(entry); |
4308 | 2.39k | maybe_run_connection_timeout(entry, now); |
4309 | 2.39k | auto drained = drain_connection_effects(entry.handle, entry.default_route_handle, |
4310 | 2.39k | entry.route_handle_by_path_id, *entry.connection, |
4311 | 2.39k | now, continue_paced_burst, send_sink); |
4312 | 2.39k | const bool remove_entry = |
4313 | 2.39k | should_remove_endpoint_connection_entry(*entry.connection, drained, now); |
4314 | 2.39k | note_send_continuation(entry, drained, now); |
4315 | 2.39k | append_result(result, std::move(drained)); |
4316 | 2.39k | refresh_server_connection_routes(entry); |
4317 | 2.39k | if (remove_entry) { |
4318 | 40 | retire_endpoint_connection_routes(entry, now); |
4319 | 40 | ++entry.wakeup_generation; |
4320 | 40 | connections_.erase(entry_it); |
4321 | 40 | } |
4322 | 2.39k | } |
4323 | 2.57k | return finalize_endpoint_result(std::move(result), now); |
4324 | 4.08k | } |
4325 | | |
4326 | 55.9k | QuicCoreResult QuicCore::advance(QuicCoreInput input, QuicCoreTimePoint now) { |
4327 | 55.9k | QuicCoreResult result; |
4328 | 55.9k | if (!legacy_config_.has_value()) { |
4329 | 8 | result.local_error = QuicCoreLocalError{ |
4330 | 8 | .connection = std::nullopt, |
4331 | 8 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4332 | 8 | .stream_id = std::nullopt, |
4333 | 8 | }; |
4334 | 8 | result.next_wakeup = next_wakeup(); |
4335 | 8 | return result; |
4336 | 8 | } |
4337 | | |
4338 | | // advance() already returned above when legacy mode is unavailable, so the legacy entry |
4339 | | // is expected to exist here. |
4340 | 55.8k | auto &entry = *ensure_legacy_entry(); |
4341 | 55.8k | if (entry.connection == nullptr) { |
4342 | 8 | return finalize_legacy_result(std::move(result), now); |
4343 | 8 | } |
4344 | 55.8k | auto config = legacy_config_.value_or(QuicCoreConfig{}); |
4345 | 55.8k | auto *connection = entry.connection.get(); |
4346 | | |
4347 | 55.8k | std::visit( |
4348 | 55.8k | overloaded{ |
4349 | 55.8k | [&](const QuicCoreStart &) { connection->start(now); }, |
4350 | 55.8k | [&](const QuicCoreInboundDatagram &in) { |
4351 | 52.1k | const auto path_id = path_id_for_inbound_route(entry, in.route_handle, |
4352 | 52.1k | in.address_validation_identity); |
4353 | 52.1k | if (!path_id.has_value()) { |
4354 | 0 | return; |
4355 | 0 | } |
4356 | 52.1k | const auto inbound_payload = in.payload(); |
4357 | 52.1k | if (config.role == EndpointRole::client) { |
4358 | 38.2k | if (!connection->is_handshake_complete() && |
4359 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.2 |
4360 | | // # A client MUST discard any Version Negotiation packet if it has |
4361 | | // # received and successfully processed any other packet, including an |
4362 | | // # earlier Version Negotiation packet. |
4363 | 38.2k | !connection->has_processed_peer_packet() && |
4364 | 38.2k | !config.reacted_to_version_negotiation) { |
4365 | 1.06k | const auto version_negotiation = |
4366 | 1.06k | parse_version_negotiation_packet(inbound_payload); |
4367 | 1.06k | if (version_negotiation.has_value()) { |
4368 | 48 | const bool valid_destination_connection_id = |
4369 | 48 | version_negotiation->destination_connection_id == |
4370 | 48 | config.source_connection_id; |
4371 | 48 | const bool valid_source_connection_id = |
4372 | 48 | version_negotiation->source_connection_id == |
4373 | 48 | config.initial_destination_connection_id; |
4374 | 48 | const bool echoes_original_version = |
4375 | 48 | std::find(version_negotiation->supported_versions.begin(), |
4376 | 48 | version_negotiation->supported_versions.end(), |
4377 | 48 | config.original_version) != |
4378 | 48 | version_negotiation->supported_versions.end(); |
4379 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.2 |
4380 | | // # A client MUST discard a Version Negotiation packet that |
4381 | | // # lists the QUIC version selected by the client. |
4382 | 48 | if (valid_destination_connection_id && valid_source_connection_id && |
4383 | 48 | !echoes_original_version) { |
4384 | 40 | for (const auto supported_version : config.supported_versions) { |
4385 | 40 | if (std::find(version_negotiation->supported_versions.begin(), |
4386 | 40 | version_negotiation->supported_versions.end(), |
4387 | 40 | supported_version) == |
4388 | 40 | version_negotiation->supported_versions.end()) { |
4389 | 24 | continue; |
4390 | 24 | } |
4391 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-6.2 |
4392 | | // # A client that supports only this version of QUIC MUST |
4393 | | // # abandon the current connection attempt if it receives a |
4394 | | // # Version Negotiation packet, with the following two |
4395 | | // # exceptions. |
4396 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
4397 | | // # Otherwise, it SHALL select a mutually supported version and |
4398 | | // # send a new first flight with that version -- this version |
4399 | | // # is now the Negotiated Version. |
4400 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-6 |
4401 | | // # When incompatible version negotiation is in use, the second |
4402 | | // # connection that is created in response to the |
4403 | | // # received Version Negotiation packet MUST restart |
4404 | | // # its application-layer protocol negotiation |
4405 | | // # process without taking into account the |
4406 | | // # Original Version. |
4407 | 16 | config.initial_version = supported_version; |
4408 | 16 | config.reacted_to_version_negotiation = true; |
4409 | 16 | entry.connection = std::make_unique<QuicConnection>(config); |
4410 | 16 | connection = entry.connection.get(); |
4411 | 16 | if (const auto family = |
4412 | 16 | entry.address_family_by_path_id.find(*path_id); |
4413 | 16 | family != entry.address_family_by_path_id.end()) { |
4414 | 0 | remember_path_address_family(entry, *path_id, |
4415 | 0 | family->second); |
4416 | 0 | } |
4417 | 16 | connection->last_inbound_path_id_ = *path_id; |
4418 | 16 | connection->current_send_path_id_ = path_id; |
4419 | 16 | connection->ensure_path_state(*path_id).is_current_send_path = |
4420 | 16 | true; |
4421 | 16 | connection->start(now); |
4422 | 16 | return; |
4423 | 40 | } |
4424 | 24 | } |
4425 | | //= https://www.rfc-editor.org/rfc/rfc9368#section-2.1 |
4426 | | // # If it doesn't find one, it SHALL abort the connection attempt. |
4427 | 32 | return; |
4428 | 48 | } |
4429 | 1.06k | } |
4430 | | |
4431 | 38.1k | const auto retry = parse_retry_packet(inbound_payload); |
4432 | 38.1k | if (retry.has_value()) { |
4433 | 120 | const auto original_destination_connection_id = |
4434 | 120 | config.original_destination_connection_id.value_or( |
4435 | 120 | config.initial_destination_connection_id); |
4436 | 120 | const auto retry_integrity_valid = validate_retry_integrity_tag( |
4437 | 120 | *retry, original_destination_connection_id); |
4438 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2 |
4439 | | // # A client MUST accept and process at most one Retry packet for each |
4440 | | // # connection attempt. |
4441 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2 |
4442 | | // # After the client has received and processed an |
4443 | | // # Initial or Retry packet from the server, it MUST discard any |
4444 | | // # subsequent Retry packets that it receives. |
4445 | 120 | const bool can_process_retry = |
4446 | 120 | !connection->is_handshake_complete() && |
4447 | 120 | !connection->has_processed_peer_packet() && |
4448 | 120 | !config.retry_source_connection_id.has_value(); |
4449 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2 |
4450 | | // # Clients MUST discard Retry packets that have a Retry Integrity Tag |
4451 | | // # that cannot be validated; see Section 5.8 of [QUIC-TLS]. |
4452 | 120 | const bool valid_integrity = |
4453 | 120 | retry_integrity_valid.has_value() && retry_integrity_valid.value(); |
4454 | 120 | const bool valid_destination_connection_id = |
4455 | 120 | retry->destination_connection_id == config.source_connection_id; |
4456 | | //= https://www.rfc-editor.org/rfc/rfc9369#section-4.1 |
4457 | | // # The client |
4458 | | // # MUST NOT use a different version in the subsequent Initial packet |
4459 | | // # that contains the Retry token. |
4460 | 120 | const bool valid_version = retry->version == config.original_version; |
4461 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.1 |
4462 | | // # A client MUST discard a Retry packet that contains a Source |
4463 | | // # Connection ID field that is identical to the Destination |
4464 | | // # Connection ID field of its Initial packet. |
4465 | 120 | const bool valid_source_connection_id = |
4466 | 120 | retry->source_connection_id != original_destination_connection_id; |
4467 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2 |
4468 | | // # A client |
4469 | | // # MUST discard a Retry packet with a zero-length Retry Token field. |
4470 | 120 | const bool valid_retry_token = !retry->retry_token.empty(); |
4471 | 120 | if (can_process_retry && valid_integrity && |
4472 | 120 | valid_destination_connection_id && valid_version && |
4473 | 120 | valid_source_connection_id && valid_retry_token) { |
4474 | 40 | config.original_destination_connection_id = |
4475 | 40 | original_destination_connection_id; |
4476 | 40 | config.retry_source_connection_id = retry->source_connection_id; |
4477 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2 |
4478 | | // # The client MUST NOT change the Source Connection ID because the |
4479 | | // # server could include the connection ID as part of its token |
4480 | | // # validation logic; see Section 8.1.4. |
4481 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.2 |
4482 | | // # This token MUST be repeated by the client in all |
4483 | | // # Initial packets it sends for that connection after it receives the |
4484 | | // # Retry packet. |
4485 | | //= https://www.rfc-editor.org/rfc/rfc9000#section-8.1.3 |
4486 | | // # The client MUST include the token in all Initial packets it |
4487 | | // # sends, unless a Retry replaces the token with a newer one. |
4488 | 40 | config.retry_token = retry->retry_token; |
4489 | 40 | config.initial_destination_connection_id = retry->source_connection_id; |
4490 | 40 | connection->apply_client_retry(original_destination_connection_id, |
4491 | 40 | retry->source_connection_id, |
4492 | 40 | retry->retry_token); |
4493 | 40 | if (const auto family = entry.address_family_by_path_id.find(*path_id); |
4494 | 40 | family != entry.address_family_by_path_id.end()) { |
4495 | 0 | remember_path_address_family(entry, *path_id, family->second); |
4496 | 0 | } |
4497 | 40 | connection->last_inbound_path_id_ = *path_id; |
4498 | 40 | connection->current_send_path_id_ = path_id; |
4499 | 40 | connection->ensure_path_state(*path_id).is_current_send_path = true; |
4500 | 40 | } |
4501 | 120 | return; |
4502 | 120 | } |
4503 | 38.1k | } |
4504 | 51.9k | if (in.shared_bytes != nullptr) { |
4505 | 10.4k | connection->process_inbound_datagram_shared(in.shared_bytes, in.begin, in.end, |
4506 | 10.4k | now, *path_id, in.ecn); |
4507 | 41.5k | } else { |
4508 | 41.5k | connection->process_inbound_datagram(inbound_payload, now, *path_id, in.ecn); |
4509 | 41.5k | } |
4510 | 51.9k | }, |
4511 | 55.8k | [&](const QuicCorePathMtuUpdate &in) { |
4512 | 40 | if (!in.route_handle.has_value()) { |
4513 | 8 | return; |
4514 | 8 | } |
4515 | 32 | const auto path_it = entry.path_id_by_route_handle.find(*in.route_handle); |
4516 | 32 | if (path_it == entry.path_id_by_route_handle.end()) { |
4517 | 24 | return; |
4518 | 24 | } |
4519 | 8 | if (!path_mtu_update_matches_connection(entry, in)) { |
4520 | 0 | return; |
4521 | 0 | } |
4522 | 8 | connection->apply_path_mtu_update(path_it->second, in.max_udp_payload_size); |
4523 | 8 | }, |
4524 | 55.8k | [&](const QuicCoreSendStreamData &in) { |
4525 | 888 | const auto queued = |
4526 | 888 | connection->queue_stream_send(in.stream_id, in.bytes, in.fin, in.priority); |
4527 | 888 | if (!queued.has_value()) { |
4528 | 16 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4529 | 16 | } |
4530 | 888 | }, |
4531 | 55.8k | [&](const QuicCoreSendSharedStreamData &in) { |
4532 | 16 | const auto queued = connection->queue_stream_send_shared(in.stream_id, in.bytes, |
4533 | 16 | in.fin, in.priority); |
4534 | 16 | if (!queued.has_value()) { |
4535 | 8 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4536 | 8 | } |
4537 | 16 | }, |
4538 | 55.8k | [&](const QuicCoreSendDatagramData &in) { |
4539 | 32 | const auto queued = queue_legacy_local_command(*connection, in); |
4540 | 32 | if (!queued.has_value()) { |
4541 | 16 | result.local_error = datagram_send_error_to_local_error(queued.error()); |
4542 | 16 | } |
4543 | 32 | }, |
4544 | 55.8k | [&](const QuicCoreSendSharedDatagramData &in) { |
4545 | 16 | const auto queued = queue_legacy_local_command(*connection, in); |
4546 | 16 | if (!queued.has_value()) { |
4547 | 8 | result.local_error = datagram_send_error_to_local_error(queued.error()); |
4548 | 8 | } |
4549 | 16 | }, |
4550 | 55.8k | [&](const QuicCoreResetStream &in) { |
4551 | 16 | const auto queued = connection->queue_stream_reset(LocalResetCommand{ |
4552 | 16 | .stream_id = in.stream_id, |
4553 | 16 | .application_error_code = in.application_error_code, |
4554 | 16 | }); |
4555 | 16 | if (!queued.has_value()) { |
4556 | 8 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4557 | 8 | } |
4558 | 16 | }, |
4559 | 55.8k | [&](const QuicCoreStopSending &in) { |
4560 | 32 | const auto queued = connection->queue_stop_sending(LocalStopSendingCommand{ |
4561 | 32 | .stream_id = in.stream_id, |
4562 | 32 | .application_error_code = in.application_error_code, |
4563 | 32 | }); |
4564 | 32 | if (!queued.has_value()) { |
4565 | 16 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4566 | 16 | } |
4567 | 32 | }, |
4568 | 55.8k | [&](const QuicCoreCloseConnection &in) { |
4569 | 16 | static_cast<void>(connection->queue_application_close(LocalApplicationCloseCommand{ |
4570 | 16 | .application_error_code = in.application_error_code, |
4571 | 16 | .reason_phrase = in.reason_phrase, |
4572 | 16 | })); |
4573 | 16 | }, |
4574 | 55.8k | [&](const QuicCoreRequestKeyUpdate &) { connection->request_key_update(); }, |
4575 | 55.8k | [&](const QuicCoreRequestConnectionMigration &in) { |
4576 | 20 | if (!endpoint_config_.allow_peer_address_change) { |
4577 | 0 | result.local_error = QuicCoreLocalError{ |
4578 | 0 | .connection = std::nullopt, |
4579 | 0 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4580 | 0 | .stream_id = std::nullopt, |
4581 | 0 | }; |
4582 | 0 | return; |
4583 | 0 | } |
4584 | 20 | const auto effective_identity = effective_address_validation_identity_for_route( |
4585 | 20 | entry, in.route_handle, in.address_validation_identity); |
4586 | 20 | if (!address_validation_identity_allowed_for_new_route(&entry, |
4587 | 20 | effective_identity)) { |
4588 | 0 | result.local_error = QuicCoreLocalError{ |
4589 | 0 | .connection = std::nullopt, |
4590 | 0 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4591 | 0 | .stream_id = std::nullopt, |
4592 | 0 | }; |
4593 | 0 | return; |
4594 | 0 | } |
4595 | 20 | if (in.reason == QuicMigrationRequestReason::preferred_address && |
4596 | 20 | !preferred_address_migration_route_family_allowed(entry, effective_identity)) { |
4597 | 0 | result.local_error = QuicCoreLocalError{ |
4598 | 0 | .connection = std::nullopt, |
4599 | 0 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4600 | 0 | .stream_id = std::nullopt, |
4601 | 0 | }; |
4602 | 0 | return; |
4603 | 0 | } |
4604 | 20 | const auto path_id = |
4605 | 20 | remember_inbound_path(entry, in.route_handle, effective_identity); |
4606 | 20 | auto requested = connection->request_connection_migration(path_id, in.reason, now); |
4607 | 20 | if (!requested.has_value()) { |
4608 | 16 | result.local_error = QuicCoreLocalError{ |
4609 | 16 | .connection = std::nullopt, |
4610 | 16 | .code = QuicCoreLocalErrorCode::unsupported_operation, |
4611 | 16 | .stream_id = std::nullopt, |
4612 | 16 | }; |
4613 | 16 | } |
4614 | 20 | }, |
4615 | 55.8k | [&](const QuicCoreTimerExpired &) { maybe_run_connection_timeout(entry, now); }, |
4616 | 55.8k | }, |
4617 | 55.8k | input); |
4618 | | |
4619 | 55.8k | auto drained = drain_connection_effects(entry.handle, entry.default_route_handle, |
4620 | 55.8k | entry.route_handle_by_path_id, *connection, now, |
4621 | 55.8k | take_send_continuation_drain(entry)); |
4622 | 55.8k | append_result(result, std::move(drained)); |
4623 | 55.8k | legacy_config_ = std::move(config); |
4624 | 55.8k | return finalize_legacy_result(std::move(result), now); |
4625 | 55.8k | } |
4626 | | |
4627 | 540 | QuicCoreResult QuicCore::advance(std::span<const QuicCoreInput> inputs, QuicCoreTimePoint now) { |
4628 | 540 | QuicCoreResult combined; |
4629 | 988 | for (std::size_t index = 0; index < inputs.size();) { |
4630 | 468 | if (!legacy_stream_send_batchable(inputs[index])) { |
4631 | 84 | auto step = advance(inputs[index], now); |
4632 | 84 | append_sequential_result(combined, std::move(step)); |
4633 | 84 | if (combined.local_error.has_value()) { |
4634 | 16 | break; |
4635 | 16 | } |
4636 | 68 | ++index; |
4637 | 68 | continue; |
4638 | 84 | } |
4639 | | |
4640 | 384 | if (!legacy_config_.has_value()) { |
4641 | 0 | auto step = advance(inputs[index], now); |
4642 | 0 | append_sequential_result(combined, std::move(step)); |
4643 | 0 | break; |
4644 | 0 | } |
4645 | | |
4646 | 384 | QuicCoreResult result; |
4647 | 384 | auto *entry = ensure_legacy_entry(); |
4648 | 384 | if (entry->connection == nullptr) { |
4649 | 0 | result = finalize_legacy_result(std::move(result), now); |
4650 | 0 | append_sequential_result(combined, std::move(result)); |
4651 | 0 | ++index; |
4652 | 0 | continue; |
4653 | 0 | } |
4654 | | |
4655 | 384 | std::size_t run_end = index; |
4656 | 1.45k | for (; run_end < inputs.size() && legacy_stream_send_batchable(inputs[run_end]); |
4657 | 1.07k | ++run_end) { |
4658 | 1.07k | std::visit( |
4659 | 1.07k | overloaded{ |
4660 | 1.07k | [&](const QuicCoreSendStreamData &in) { |
4661 | 1.07k | const auto queued = queue_legacy_local_command(*entry->connection, in); |
4662 | 1.07k | if (!queued.has_value()) { |
4663 | 4 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4664 | 4 | } |
4665 | 1.07k | }, |
4666 | 1.07k | [&](const QuicCoreSendSharedStreamData &in) { |
4667 | 0 | const auto queued = queue_legacy_local_command(*entry->connection, in); |
4668 | 0 | if (!queued.has_value()) { |
4669 | 0 | result.local_error = stream_state_error_to_local_error(queued.error()); |
4670 | 0 | } |
4671 | 0 | }, |
4672 | 1.07k | [](const auto &) COQUIC_NO_PROFILE {}, |
4673 | 1.07k | }, |
4674 | 1.07k | inputs[run_end]); |
4675 | 1.07k | if (result.local_error.has_value()) { |
4676 | 4 | ++run_end; |
4677 | 4 | break; |
4678 | 4 | } |
4679 | 1.07k | } |
4680 | | |
4681 | 384 | auto drained = drain_connection_effects(entry->handle, entry->default_route_handle, |
4682 | 384 | entry->route_handle_by_path_id, *entry->connection, |
4683 | 384 | now, take_send_continuation_drain(*entry)); |
4684 | 384 | append_result(result, std::move(drained)); |
4685 | 384 | result = finalize_legacy_result(std::move(result), now); |
4686 | 384 | append_sequential_result(combined, std::move(result)); |
4687 | 384 | if (combined.local_error.has_value()) { |
4688 | 4 | break; |
4689 | 4 | } |
4690 | 380 | index = run_end; |
4691 | 380 | } |
4692 | 540 | return combined; |
4693 | 540 | } |
4694 | | |
4695 | 112 | std::vector<ConnectionId> QuicCore::active_local_connection_ids() const { |
4696 | 112 | if (const auto *entry = legacy_entry()) { |
4697 | 104 | return entry->connection->active_local_connection_ids(); |
4698 | 104 | } |
4699 | 8 | return {}; |
4700 | 112 | } |
4701 | | |
4702 | 1.59k | bool QuicCore::is_handshake_complete() const { |
4703 | 1.59k | if (const auto *entry = legacy_entry()) { |
4704 | 1.58k | return entry->connection->is_handshake_complete(); |
4705 | 1.58k | } |
4706 | 8 | return false; |
4707 | 1.59k | } |
4708 | | |
4709 | 544 | bool QuicCore::has_failed() const { |
4710 | 544 | if (const auto *entry = legacy_entry()) { |
4711 | 536 | return entry->connection->has_failed(); |
4712 | 536 | } |
4713 | 8 | return false; |
4714 | 544 | } |
4715 | | |
4716 | | } // namespace coquic::quic |