Coverage Report

Created: 2025-10-10 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2o/deps/quicly/lib/remote_cid.c
Line
Count
Source
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 <string.h>
24
#include "quicly/constants.h"
25
#include "quicly/remote_cid.h"
26
27
void quicly_remote_cid_init_set(quicly_remote_cid_set_t *set, ptls_iovec_t *initial_cid, void (*random_bytes)(void *, size_t))
28
0
{
29
0
    set->cids[0] = (quicly_remote_cid_t){
30
0
        .state = QUICLY_REMOTE_CID_IN_USE,
31
0
        .sequence = 0,
32
0
    };
33
0
    if (initial_cid != NULL) {
34
0
        quicly_set_cid(&set->cids[0].cid, *initial_cid);
35
0
    } else {
36
0
        random_bytes(set->cids[0].cid.cid, QUICLY_MIN_INITIAL_DCID_LEN);
37
0
        set->cids[0].cid.len = QUICLY_MIN_INITIAL_DCID_LEN;
38
0
    }
39
0
    random_bytes(set->cids[0].stateless_reset_token, sizeof(set->cids[0].stateless_reset_token));
40
41
0
    for (size_t i = 1; i < PTLS_ELEMENTSOF(set->cids); i++)
42
0
        set->cids[i] = (quicly_remote_cid_t){
43
0
            .state = QUICLY_REMOTE_CID_UNAVAILABLE,
44
0
            .sequence = i,
45
0
        };
46
47
0
    set->_largest_sequence_expected = PTLS_ELEMENTSOF(set->cids) - 1;
48
0
    memset(&set->retired, 0, sizeof(set->retired));
49
0
}
50
51
static quicly_error_t do_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len,
52
                                  const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN])
53
0
{
54
0
    int was_stored = 0;
55
56
0
    if (set->_largest_sequence_expected < sequence)
57
0
        return QUICLY_TRANSPORT_ERROR_CONNECTION_ID_LIMIT;
58
59
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
60
0
        if (set->cids[i].state != QUICLY_REMOTE_CID_UNAVAILABLE) {
61
            /* compare newly received CID against what we already have, to see if there is duplication/conflicts */
62
63
            /* If an endpoint receives a NEW_CONNECTION_ID frame that repeats a previously issued connection ID with
64
             * a different Stateless Reset Token or a different sequence number, or if a sequence number is used for
65
             * different connection IDs, the endpoint MAY treat that receipt as a connection error of type PROTOCOL_VIOLATION.
66
             * (19.15)
67
             */
68
0
            if (quicly_cid_is_equal(&set->cids[i].cid, ptls_iovec_init(cid, cid_len))) {
69
0
                if (set->cids[i].sequence == sequence &&
70
0
                    memcmp(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN) == 0) {
71
                    /* likely a duplicate due to retransmission */
72
0
                    return 0;
73
0
                } else {
74
                    /* received a frame that carries conflicting information */
75
0
                    return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION;
76
0
                }
77
0
            }
78
            /* here we know CID is not equal */
79
0
            if (set->cids[i].sequence == sequence)
80
0
                return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION;
81
0
        } else if (set->cids[i].sequence == sequence) {
82
0
            assert(!was_stored);
83
0
            set->cids[i].sequence = sequence;
84
0
            quicly_set_cid(&set->cids[i].cid, ptls_iovec_init(cid, cid_len));
85
0
            memcpy(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN);
86
0
            set->cids[i].state = QUICLY_REMOTE_CID_AVAILABLE;
87
0
            was_stored = 1;
88
0
        }
89
0
    }
90
91
    /* execution reaches here in two cases, 1) normal path, i.e. new CID was successfully registered, and 2) new CID was already
92
     * retired (was_stored == 0). */
93
0
    return 0;
94
0
}
95
96
static int do_unregister(quicly_remote_cid_set_t *set, size_t idx_to_unreg)
97
0
{
98
0
    uint64_t seq_to_unreg = set->cids[idx_to_unreg].sequence;
99
100
0
    set->cids[idx_to_unreg].state = QUICLY_REMOTE_CID_UNAVAILABLE;
101
0
    set->cids[idx_to_unreg].sequence = ++set->_largest_sequence_expected;
102
103
0
    return quicly_remote_cid_push_retired(set, seq_to_unreg);
104
0
}
105
106
int quicly_remote_cid_unregister(quicly_remote_cid_set_t *set, uint64_t sequence)
107
0
{
108
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
109
0
        if (sequence == set->cids[i].sequence)
110
0
            return do_unregister(set, i);
111
0
    }
112
0
    assert(!"invalid CID sequence number");
113
0
}
114
115
static int unregister_prior_to(quicly_remote_cid_set_t *set, uint64_t seq_unreg_prior_to)
116
0
{
117
0
    int ret;
118
119
0
    for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) {
120
0
        while (set->cids[i].sequence < seq_unreg_prior_to) {
121
0
            if ((ret = do_unregister(set, i)) != 0)
122
0
                return ret;
123
0
        }
124
0
    }
125
126
0
    return 0;
127
0
}
128
129
quicly_error_t quicly_remote_cid_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len,
130
                                          const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN], uint64_t retire_prior_to)
131
0
{
132
0
    quicly_remote_cid_set_t backup = *set; /* preserve current state so that it can be restored to notify protocol violation */
133
0
    quicly_error_t ret;
134
135
0
    assert(sequence >= retire_prior_to);
136
137
    /* First, handle retire_prior_to. This order is important as it is possible to receive a NEW_CONNECTION_ID frame such that it
138
     * retires active_connection_id_limit CIDs and then installs one new CID. Next, the new CID is registered. If either of the two
139
     * fails, the state is restored to the original so that we can send an error to the peer. */
140
0
    if ((ret = unregister_prior_to(set, retire_prior_to)) != 0 || (ret = do_register(set, sequence, cid, cid_len, srt)) != 0)
141
0
        *set = backup;
142
143
0
    return ret;
144
0
}
145
146
int quicly_remote_cid_push_retired(quicly_remote_cid_set_t *set, uint64_t sequence)
147
0
{
148
    /* do nothing if given sequence is already registered */
149
0
    for (size_t i = 0; i < set->retired.count; ++i) {
150
0
        if (set->retired.cids[i] == sequence)
151
0
            return 0;
152
0
    }
153
154
0
    if (set->retired.count >= PTLS_ELEMENTSOF(set->retired.cids))
155
0
        return QUICLY_ERROR_STATE_EXHAUSTION;
156
0
    set->retired.cids[set->retired.count++] = sequence;
157
0
    return 0;
158
0
}
159
160
void quicly_remote_cid_shift_retired(quicly_remote_cid_set_t *set, size_t count)
161
0
{
162
0
    assert(count <= set->retired.count);
163
164
0
    set->retired.count -= count;
165
0
    for (size_t i = 0; i < count; ++i)
166
0
        set->retired.cids[i] = set->retired.cids[i + count];
167
0
}