Coverage Report

Created: 2025-07-18 06:41

/src/h2o/deps/quicly/lib/remote_cid.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020 Fastly, Inc.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include <assert.h>
23
#include "quicly/constants.h"
24
#include "quicly/remote_cid.h"
25
26
void quicly_remote_cid_init_set(quicly_remote_cid_set_t *set, ptls_iovec_t *initial_cid, void (*random_bytes)(void *, size_t))
27
0
{
28
0
    set->cids[0] = (quicly_remote_cid_t){
29
0
        .state = QUICLY_REMOTE_CID_IN_USE,
30
0
        .sequence = 0,
31
0
    };
32
0
    if (initial_cid != NULL) {
33
0
        quicly_set_cid(&set->cids[0].cid, *initial_cid);
34
0
    } else {
35
0
        random_bytes(set->cids[0].cid.cid, QUICLY_MIN_INITIAL_DCID_LEN);
36
0
        set->cids[0].cid.len = QUICLY_MIN_INITIAL_DCID_LEN;
37
0
    }
38
0
    random_bytes(set->cids[0].stateless_reset_token, sizeof(set->cids[0].stateless_reset_token));
39
40
0
    for (size_t i = 1; i < PTLS_ELEMENTSOF(set->cids); i++)
41
0
        set->cids[i] = (quicly_remote_cid_t){
42
0
            .state = QUICLY_REMOTE_CID_UNAVAILABLE,
43
0
            .sequence = i,
44
0
        };
45
46
0
    set->_largest_sequence_expected = PTLS_ELEMENTSOF(set->cids) - 1;
47
0
}
48
49
static quicly_error_t do_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len,
50
                                  const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN])
51
0
{
52
0
    int was_stored = 0;
53
54
0
    if (set->_largest_sequence_expected < sequence)
55
0
        return QUICLY_TRANSPORT_ERROR_CONNECTION_ID_LIMIT;
56
57
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
58
0
        if (set->cids[i].state != QUICLY_REMOTE_CID_UNAVAILABLE) {
59
            /* compare newly received CID against what we already have, to see if there is duplication/conflicts */
60
61
            /* If an endpoint receives a NEW_CONNECTION_ID frame that repeats a previously issued connection ID with
62
             * a different Stateless Reset Token or a different sequence number, or if a sequence number is used for
63
             * different connection IDs, the endpoint MAY treat that receipt as a connection error of type PROTOCOL_VIOLATION.
64
             * (19.15)
65
             */
66
0
            if (quicly_cid_is_equal(&set->cids[i].cid, ptls_iovec_init(cid, cid_len))) {
67
0
                if (set->cids[i].sequence == sequence &&
68
0
                    memcmp(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN) == 0) {
69
                    /* likely a duplicate due to retransmission */
70
0
                    return 0;
71
0
                } else {
72
                    /* received a frame that carries conflicting information */
73
0
                    return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION;
74
0
                }
75
0
            }
76
            /* here we know CID is not equal */
77
0
            if (set->cids[i].sequence == sequence)
78
0
                return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION;
79
0
        } else if (set->cids[i].sequence == sequence) {
80
0
            assert(!was_stored);
81
0
            set->cids[i].sequence = sequence;
82
0
            quicly_set_cid(&set->cids[i].cid, ptls_iovec_init(cid, cid_len));
83
0
            memcpy(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN);
84
0
            set->cids[i].state = QUICLY_REMOTE_CID_AVAILABLE;
85
0
            was_stored = 1;
86
0
        }
87
0
    }
88
89
    /* execution reaches here in two cases, 1) normal path, i.e. new CID was successfully registered, and 2) new CID was already
90
     * retired (was_stored == 0). */
91
0
    return 0;
92
0
}
93
94
static void do_unregister(quicly_remote_cid_set_t *set, size_t idx_to_unreg)
95
0
{
96
0
    set->cids[idx_to_unreg].state = QUICLY_REMOTE_CID_UNAVAILABLE;
97
0
    set->cids[idx_to_unreg].sequence = ++set->_largest_sequence_expected;
98
0
}
99
100
void quicly_remote_cid_unregister(quicly_remote_cid_set_t *set, uint64_t sequence)
101
0
{
102
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
103
0
        if (sequence == set->cids[i].sequence) {
104
0
            do_unregister(set, i);
105
0
            return;
106
0
        }
107
0
    }
108
0
    assert(!"invalid CID sequence number");
109
0
}
110
111
static size_t unregister_prior_to(quicly_remote_cid_set_t *set, uint64_t seq_unreg_prior_to,
112
                                  uint64_t unregistered_seqs[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT])
113
0
{
114
0
    size_t num_unregistered = 0;
115
116
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
117
0
        if (set->cids[i].sequence < seq_unreg_prior_to) {
118
0
            unregistered_seqs[num_unregistered++] = set->cids[i].sequence;
119
0
            do_unregister(set, i);
120
0
        }
121
0
    }
122
123
0
    return num_unregistered;
124
0
}
125
126
quicly_error_t quicly_remote_cid_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len,
127
                                          const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN], uint64_t retire_prior_to,
128
                                          uint64_t unregistered_seqs[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT],
129
                                          size_t *num_unregistered_seqs)
130
0
{
131
0
    quicly_remote_cid_set_t backup = *set; /* preserve current state so that it can be restored to notify protocol violation */
132
0
    quicly_error_t ret;
133
134
0
    assert(sequence >= retire_prior_to);
135
136
    /* First, handle retire_prior_to. This order is important as it is possible to receive a NEW_CONNECTION_ID frame such that it
137
     * retires active_connection_id_limit CIDs and then installs one new CID. */
138
0
    *num_unregistered_seqs = unregister_prior_to(set, retire_prior_to, unregistered_seqs);
139
140
    /* Then, register given value. If an error occurs, restore the backup and send the error. */
141
0
    if ((ret = do_register(set, sequence, cid, cid_len, srt)) != 0) {
142
0
        *set = backup;
143
0
        return ret;
144
0
    }
145
146
0
    return ret;
147
0
}