Coverage Report

Created: 2026-06-17 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 &note) {
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 &quoted,
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