Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/third_party/ngtcp2/lib/ngtcp2_dcidtr.c
Line
Count
Source
1
/*
2
 * ngtcp2
3
 *
4
 * Copyright (c) 2025 ngtcp2 contributors
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining
7
 * a copy of this software and associated documentation files (the
8
 * "Software"), to deal in the Software without restriction, including
9
 * without limitation the rights to use, copy, modify, merge, publish,
10
 * distribute, sublicense, and/or sell copies of the Software, and to
11
 * permit persons to whom the Software is furnished to do so, subject to
12
 * the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be
15
 * included in all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
 */
25
#include "ngtcp2_dcidtr.h"
26
27
#include <assert.h>
28
29
#include "ngtcp2_tstamp.h"
30
#include "ngtcp2_macro.h"
31
32
0
void ngtcp2_dcidtr_init(ngtcp2_dcidtr *dtr) {
33
0
  ngtcp2_static_ringbuf_dcid_unused_init(&dtr->unused);
34
0
  ngtcp2_static_ringbuf_dcid_bound_init(&dtr->bound);
35
0
  ngtcp2_static_ringbuf_dcid_retired_init(&dtr->retired);
36
37
0
  dtr->retire_unacked.len = 0;
38
0
}
39
40
0
int ngtcp2_dcidtr_track_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq) {
41
0
  if (dtr->retire_unacked.len >= ngtcp2_arraylen(dtr->retire_unacked.seqs)) {
42
0
    return NGTCP2_ERR_CONNECTION_ID_LIMIT;
43
0
  }
44
45
0
  dtr->retire_unacked.seqs[dtr->retire_unacked.len++] = seq;
46
47
0
  return 0;
48
0
}
49
50
0
void ngtcp2_dcidtr_untrack_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq) {
51
0
  size_t i;
52
53
0
  for (i = 0; i < dtr->retire_unacked.len; ++i) {
54
0
    if (dtr->retire_unacked.seqs[i] != seq) {
55
0
      continue;
56
0
    }
57
58
0
    if (i != dtr->retire_unacked.len - 1) {
59
0
      dtr->retire_unacked.seqs[i] =
60
0
        dtr->retire_unacked.seqs[dtr->retire_unacked.len - 1];
61
0
    }
62
63
0
    --dtr->retire_unacked.len;
64
65
0
    return;
66
0
  }
67
0
}
68
69
int ngtcp2_dcidtr_check_retired_seq_tracked(const ngtcp2_dcidtr *dtr,
70
0
                                            uint64_t seq) {
71
0
  size_t i;
72
73
0
  for (i = 0; i < dtr->retire_unacked.len; ++i) {
74
0
    if (dtr->retire_unacked.seqs[i] == seq) {
75
0
      return 1;
76
0
    }
77
0
  }
78
79
0
  return 0;
80
0
}
81
82
static int dcidtr_on_retire(ngtcp2_dcidtr *dtr, const ngtcp2_dcid *dcid,
83
0
                            ngtcp2_dcidtr_cb on_retire, void *user_data) {
84
0
  int rv;
85
86
0
  if (ngtcp2_dcidtr_check_retired_seq_tracked(dtr, dcid->seq)) {
87
0
    return 0;
88
0
  }
89
90
0
  rv = ngtcp2_dcidtr_track_retired_seq(dtr, dcid->seq);
91
0
  if (rv != 0) {
92
0
    return rv;
93
0
  }
94
95
0
  if (!on_retire) {
96
0
    return 0;
97
0
  }
98
99
0
  return on_retire(dcid, user_data);
100
0
}
101
102
ngtcp2_dcid *ngtcp2_dcidtr_find_bound_dcid(const ngtcp2_dcidtr *dtr,
103
0
                                           const ngtcp2_path *path) {
104
0
  ngtcp2_dcid *dcid;
105
0
  const ngtcp2_ringbuf *rb = &dtr->bound.rb;
106
0
  size_t i, len = ngtcp2_ringbuf_len(rb);
107
108
0
  for (i = 0; i < len; ++i) {
109
0
    dcid = ngtcp2_ringbuf_get(rb, i);
110
111
0
    if (ngtcp2_path_eq(&dcid->ps.path, path)) {
112
0
      return dcid;
113
0
    }
114
0
  }
115
116
0
  return NULL;
117
0
}
118
119
ngtcp2_dcid *ngtcp2_dcidtr_bind_zerolen_dcid(ngtcp2_dcidtr *dtr,
120
0
                                             const ngtcp2_path *path) {
121
0
  ngtcp2_dcid *dcid = ngtcp2_ringbuf_push_back(&dtr->bound.rb);
122
0
  ngtcp2_cid cid;
123
124
0
  ngtcp2_cid_zero(&cid);
125
0
  ngtcp2_dcid_init(dcid, 0, &cid, NULL);
126
0
  ngtcp2_dcid_set_path(dcid, path);
127
128
0
  return dcid;
129
0
}
130
131
int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest,
132
                            const ngtcp2_path *path, ngtcp2_tstamp ts,
133
0
                            ngtcp2_dcidtr_cb on_retire, void *user_data) {
134
0
  const ngtcp2_dcid *src;
135
0
  ngtcp2_dcid *dest;
136
0
  int rv;
137
138
0
  if (ngtcp2_ringbuf_full(&dtr->bound.rb)) {
139
0
    rv = dcidtr_on_retire(dtr, ngtcp2_ringbuf_get(&dtr->bound.rb, 0), on_retire,
140
0
                          user_data);
141
0
    if (rv != 0) {
142
0
      return rv;
143
0
    }
144
0
  }
145
146
0
  src = ngtcp2_ringbuf_get(&dtr->unused.rb, 0);
147
0
  dest = ngtcp2_ringbuf_push_back(&dtr->bound.rb);
148
149
0
  ngtcp2_dcid_copy(dest, src);
150
0
  dest->bound_ts = ts;
151
0
  ngtcp2_dcid_set_path(dest, path);
152
153
0
  ngtcp2_ringbuf_pop_front(&dtr->unused.rb);
154
155
0
  *pdest = dest;
156
157
0
  return 0;
158
0
}
159
160
int ngtcp2_dcidtr_verify_stateless_reset(
161
  const ngtcp2_dcidtr *dtr, const ngtcp2_path *path,
162
0
  const ngtcp2_stateless_reset_token *token) {
163
0
  const ngtcp2_dcid *dcid;
164
0
  const ngtcp2_ringbuf *rb = &dtr->bound.rb;
165
0
  size_t i, len = ngtcp2_ringbuf_len(rb);
166
167
0
  for (i = 0; i < len; ++i) {
168
0
    dcid = ngtcp2_ringbuf_get(rb, i);
169
0
    if (ngtcp2_dcid_verify_stateless_reset_token(dcid, path, token) == 0) {
170
0
      return 0;
171
0
    }
172
0
  }
173
174
0
  return NGTCP2_ERR_INVALID_ARGUMENT;
175
0
}
176
177
static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound,
178
                                   uint64_t seq, const ngtcp2_cid *cid,
179
0
                                   const ngtcp2_stateless_reset_token *token) {
180
0
  const ngtcp2_dcid *dcid;
181
0
  size_t i, len = ngtcp2_ringbuf_len(rb);
182
0
  int rv;
183
184
0
  for (i = 0; i < len; ++i) {
185
0
    dcid = ngtcp2_ringbuf_get(rb, i);
186
0
    rv = ngtcp2_dcid_verify_uniqueness(dcid, seq, cid, token);
187
0
    if (rv != 0) {
188
0
      return NGTCP2_ERR_PROTO;
189
0
    }
190
191
0
    if (ngtcp2_cid_eq(&dcid->cid, cid)) {
192
0
      *pfound = 1;
193
0
    }
194
0
  }
195
196
0
  return 0;
197
0
}
198
199
int ngtcp2_dcidtr_verify_token_uniqueness(
200
  const ngtcp2_dcidtr *dtr, int *pfound, uint64_t seq, const ngtcp2_cid *cid,
201
0
  const ngtcp2_stateless_reset_token *token) {
202
0
  int rv;
203
204
0
  rv = verify_token_uniqueness(&dtr->bound.rb, pfound, seq, cid, token);
205
0
  if (rv != 0) {
206
0
    return rv;
207
0
  }
208
209
0
  return verify_token_uniqueness(&dtr->unused.rb, pfound, seq, cid, token);
210
0
}
211
212
0
static void remove_dcid_at(ngtcp2_ringbuf *rb, size_t at) {
213
0
  const ngtcp2_dcid *src;
214
0
  ngtcp2_dcid *dest;
215
216
0
  if (at == 0) {
217
0
    ngtcp2_ringbuf_pop_front(rb);
218
0
    return;
219
0
  }
220
221
0
  if (at == ngtcp2_ringbuf_len(rb) - 1) {
222
0
    ngtcp2_ringbuf_pop_back(rb);
223
0
    return;
224
0
  }
225
226
0
  src = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
227
0
  dest = ngtcp2_ringbuf_get(rb, at);
228
229
0
  ngtcp2_dcid_copy(dest, src);
230
0
  ngtcp2_ringbuf_pop_back(rb);
231
0
}
232
233
static int dcidtr_retire_dcid_prior_to(ngtcp2_dcidtr *dtr, ngtcp2_ringbuf *rb,
234
                                       uint64_t seq, ngtcp2_dcidtr_cb on_retire,
235
0
                                       void *user_data) {
236
0
  size_t i;
237
0
  const ngtcp2_dcid *dcid;
238
0
  int rv;
239
240
0
  for (i = 0; i < ngtcp2_ringbuf_len(rb);) {
241
0
    dcid = ngtcp2_ringbuf_get(rb, i);
242
0
    if (dcid->seq >= seq) {
243
0
      ++i;
244
0
      continue;
245
0
    }
246
247
0
    rv = dcidtr_on_retire(dtr, dcid, on_retire, user_data);
248
0
    if (rv != 0) {
249
0
      return rv;
250
0
    }
251
252
0
    remove_dcid_at(rb, i);
253
0
  }
254
255
0
  return 0;
256
0
}
257
258
int ngtcp2_dcidtr_retire_inactive_dcid_prior_to(ngtcp2_dcidtr *dtr,
259
                                                uint64_t seq,
260
                                                ngtcp2_dcidtr_cb on_retire,
261
0
                                                void *user_data) {
262
0
  int rv;
263
264
0
  rv =
265
0
    dcidtr_retire_dcid_prior_to(dtr, &dtr->bound.rb, seq, on_retire, user_data);
266
0
  if (rv != 0) {
267
0
    return rv;
268
0
  }
269
270
0
  return dcidtr_retire_dcid_prior_to(dtr, &dtr->unused.rb, seq, on_retire,
271
0
                                     user_data);
272
0
}
273
274
int ngtcp2_dcidtr_retire_active_dcid(ngtcp2_dcidtr *dtr,
275
                                     const ngtcp2_dcid *dcid, ngtcp2_tstamp ts,
276
                                     ngtcp2_dcidtr_cb on_deactivate,
277
0
                                     void *user_data) {
278
0
  ngtcp2_ringbuf *rb = &dtr->retired.rb;
279
0
  const ngtcp2_dcid *stale_dcid;
280
0
  ngtcp2_dcid *dest;
281
0
  int rv;
282
283
0
  assert(dcid->cid.datalen);
284
285
0
  if (ngtcp2_ringbuf_full(rb)) {
286
0
    stale_dcid = ngtcp2_ringbuf_get(rb, 0);
287
0
    rv = on_deactivate(stale_dcid, user_data);
288
0
    if (rv != 0) {
289
0
      return rv;
290
0
    }
291
0
  }
292
293
0
  dest = ngtcp2_ringbuf_push_back(rb);
294
0
  ngtcp2_dcid_copy(dest, dcid);
295
0
  dest->retired_ts = ts;
296
297
0
  return dcidtr_on_retire(dtr, dest, NULL, NULL);
298
0
}
299
300
int ngtcp2_dcidtr_remove_stale_retired_dcid(ngtcp2_dcidtr *dtr,
301
                                            ngtcp2_duration timeout,
302
                                            ngtcp2_tstamp ts,
303
                                            ngtcp2_dcidtr_cb on_deactivate,
304
0
                                            void *user_data) {
305
0
  ngtcp2_ringbuf *rb = &dtr->retired.rb;
306
0
  const ngtcp2_dcid *dcid;
307
0
  int rv;
308
309
0
  for (; ngtcp2_ringbuf_len(rb);) {
310
0
    dcid = ngtcp2_ringbuf_get(rb, 0);
311
0
    if (ngtcp2_tstamp_not_elapsed(dcid->retired_ts, timeout, ts)) {
312
0
      break;
313
0
    }
314
315
0
    rv = on_deactivate(dcid, user_data);
316
0
    if (rv != 0) {
317
0
      return rv;
318
0
    }
319
320
0
    ngtcp2_ringbuf_pop_front(rb);
321
0
  }
322
323
0
  return 0;
324
0
}
325
326
int ngtcp2_dcidtr_pop_bound_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest,
327
0
                                 const ngtcp2_path *path) {
328
0
  const ngtcp2_dcid *src;
329
0
  ngtcp2_ringbuf *rb = &dtr->bound.rb;
330
0
  size_t len = ngtcp2_ringbuf_len(rb);
331
0
  size_t i;
332
333
0
  for (i = 0; i < len; ++i) {
334
0
    src = ngtcp2_ringbuf_get(rb, i);
335
0
    if (ngtcp2_path_eq(&src->ps.path, path)) {
336
0
      ngtcp2_dcid_copy(dest, src);
337
0
      remove_dcid_at(rb, i);
338
339
0
      return 0;
340
0
    }
341
0
  }
342
343
0
  return NGTCP2_ERR_INVALID_ARGUMENT;
344
0
}
345
346
int ngtcp2_dcidtr_retire_stale_bound_dcid(ngtcp2_dcidtr *dtr,
347
                                          ngtcp2_duration timeout,
348
                                          ngtcp2_tstamp ts,
349
                                          ngtcp2_dcidtr_cb on_retire,
350
0
                                          void *user_data) {
351
0
  ngtcp2_ringbuf *rb = &dtr->bound.rb;
352
0
  size_t i;
353
0
  const ngtcp2_dcid *dcid;
354
0
  int rv;
355
356
0
  for (i = 0; i < ngtcp2_ringbuf_len(rb);) {
357
0
    dcid = ngtcp2_ringbuf_get(rb, i);
358
359
0
    assert(dcid->cid.datalen);
360
361
0
    if (ngtcp2_tstamp_not_elapsed(dcid->bound_ts, timeout, ts)) {
362
0
      ++i;
363
0
      continue;
364
0
    }
365
366
0
    rv = dcidtr_on_retire(dtr, dcid, on_retire, user_data);
367
0
    if (rv != 0) {
368
0
      return rv;
369
0
    }
370
371
0
    remove_dcid_at(rb, i);
372
0
  }
373
374
0
  return 0;
375
0
}
376
377
0
ngtcp2_tstamp ngtcp2_dcidtr_earliest_bound_ts(const ngtcp2_dcidtr *dtr) {
378
0
  const ngtcp2_ringbuf *rb = &dtr->bound.rb;
379
0
  size_t i, len = ngtcp2_ringbuf_len(rb);
380
0
  ngtcp2_tstamp res = UINT64_MAX;
381
0
  const ngtcp2_dcid *dcid;
382
383
0
  for (i = 0; i < len; ++i) {
384
0
    dcid = ngtcp2_ringbuf_get(rb, i);
385
386
0
    assert(dcid->cid.datalen);
387
0
    assert(dcid->bound_ts != UINT64_MAX);
388
389
0
    res = ngtcp2_min_uint64(res, dcid->bound_ts);
390
0
  }
391
392
0
  return res;
393
0
}
394
395
0
ngtcp2_tstamp ngtcp2_dcidtr_earliest_retired_ts(const ngtcp2_dcidtr *dtr) {
396
0
  const ngtcp2_ringbuf *rb = &dtr->retired.rb;
397
0
  const ngtcp2_dcid *dcid;
398
399
0
  if (ngtcp2_ringbuf_len(rb) == 0) {
400
0
    return UINT64_MAX;
401
0
  }
402
403
0
  dcid = ngtcp2_ringbuf_get(rb, 0);
404
405
0
  return dcid->retired_ts;
406
0
}
407
408
void ngtcp2_dcidtr_push_unused(ngtcp2_dcidtr *dtr, uint64_t seq,
409
                               const ngtcp2_cid *cid,
410
0
                               const ngtcp2_stateless_reset_token *token) {
411
0
  ngtcp2_dcid *dcid = ngtcp2_ringbuf_push_back(&dtr->unused.rb);
412
413
0
  ngtcp2_dcid_init(dcid, seq, cid, token);
414
0
}
415
416
0
void ngtcp2_dcidtr_pop_unused_cid_token(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest) {
417
0
  ngtcp2_ringbuf *rb = &dtr->unused.rb;
418
0
  const ngtcp2_dcid *src;
419
420
0
  assert(ngtcp2_ringbuf_len(rb));
421
422
0
  src = ngtcp2_ringbuf_get(rb, 0);
423
424
0
  dest->flags = NGTCP2_DCID_FLAG_NONE;
425
0
  ngtcp2_dcid_copy_cid_token(dest, src);
426
427
0
  ngtcp2_ringbuf_pop_front(rb);
428
0
}
429
430
0
void ngtcp2_dcidtr_pop_unused(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest) {
431
0
  ngtcp2_ringbuf *rb = &dtr->unused.rb;
432
0
  const ngtcp2_dcid *src;
433
434
0
  assert(ngtcp2_ringbuf_len(rb));
435
436
0
  src = ngtcp2_ringbuf_get(rb, 0);
437
438
0
  ngtcp2_dcid_copy(dest, src);
439
440
0
  ngtcp2_ringbuf_pop_front(rb);
441
0
}
442
443
int ngtcp2_dcidtr_check_path_retired(const ngtcp2_dcidtr *dtr,
444
0
                                     const ngtcp2_path *path) {
445
0
  const ngtcp2_ringbuf *rb = &dtr->retired.rb;
446
0
  size_t i, len = ngtcp2_ringbuf_len(rb);
447
0
  const ngtcp2_dcid *dcid;
448
449
0
  for (i = 0; i < len; ++i) {
450
0
    dcid = ngtcp2_ringbuf_get(rb, i);
451
0
    if (ngtcp2_path_eq(&dcid->ps.path, path)) {
452
0
      return 1;
453
0
    }
454
0
  }
455
456
0
  return 0;
457
0
}
458
459
0
size_t ngtcp2_dcidtr_unused_len(const ngtcp2_dcidtr *dtr) {
460
0
  return ngtcp2_ringbuf_len(&dtr->unused.rb);
461
0
}
462
463
0
size_t ngtcp2_dcidtr_bound_len(const ngtcp2_dcidtr *dtr) {
464
0
  return ngtcp2_ringbuf_len(&dtr->bound.rb);
465
0
}
466
467
0
size_t ngtcp2_dcidtr_retired_len(const ngtcp2_dcidtr *dtr) {
468
0
  return ngtcp2_ringbuf_len(&dtr->retired.rb);
469
0
}
470
471
0
size_t ngtcp2_dcidtr_inactive_len(const ngtcp2_dcidtr *dtr) {
472
0
  return ngtcp2_ringbuf_len(&dtr->unused.rb) +
473
0
         ngtcp2_ringbuf_len(&dtr->bound.rb);
474
0
}
475
476
0
int ngtcp2_dcidtr_unused_full(const ngtcp2_dcidtr *dtr) {
477
0
  return ngtcp2_ringbuf_full(&dtr->unused.rb);
478
0
}
479
480
0
int ngtcp2_dcidtr_unused_empty(const ngtcp2_dcidtr *dtr) {
481
0
  return ngtcp2_ringbuf_len(&dtr->unused.rb) == 0;
482
0
}
483
484
0
int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr) {
485
0
  return ngtcp2_ringbuf_full(&dtr->bound.rb);
486
0
}