/src/openssl/ssl/quic/quic_sstream.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | #include "internal/quic_stream.h" |
11 | | #include "internal/uint_set.h" |
12 | | #include "internal/common.h" |
13 | | #include "internal/ring_buf.h" |
14 | | |
15 | | /* |
16 | | * ================================================================== |
17 | | * QUIC Send Stream |
18 | | */ |
19 | | struct quic_sstream_st { |
20 | | struct ring_buf ring_buf; |
21 | | |
22 | | /* |
23 | | * Any logical byte in the stream is in one of these states: |
24 | | * |
25 | | * - NEW: The byte has not yet been transmitted, or has been lost and is |
26 | | * in need of retransmission. |
27 | | * |
28 | | * - IN_FLIGHT: The byte has been transmitted but is awaiting |
29 | | * acknowledgement. We continue to store the data in case we return |
30 | | * to the NEW state. |
31 | | * |
32 | | * - ACKED: The byte has been acknowledged and we can cease storing it. |
33 | | * We do not necessarily cull it immediately, so there may be a delay |
34 | | * between reaching the ACKED state and the buffer space actually being |
35 | | * recycled. |
36 | | * |
37 | | * A logical byte in the stream is |
38 | | * |
39 | | * - in the NEW state if it is in new_set; |
40 | | * - is in the ACKED state if it is in acked_set |
41 | | * (and may or may not have been culled); |
42 | | * - is in the IN_FLIGHT state otherwise. |
43 | | * |
44 | | * Invariant: No logical byte is ever in both new_set and acked_set. |
45 | | */ |
46 | | UINT_SET new_set, acked_set; |
47 | | |
48 | | /* |
49 | | * The current size of the stream is ring_buf.head_offset. If |
50 | | * have_final_size is true, this is also the final size of the stream. |
51 | | */ |
52 | | unsigned int have_final_size : 1; |
53 | | unsigned int sent_final_size : 1; |
54 | | unsigned int acked_final_size : 1; |
55 | | unsigned int cleanse : 1; |
56 | | }; |
57 | | |
58 | | static void qss_cull(QUIC_SSTREAM *qss); |
59 | | |
60 | | QUIC_SSTREAM *ossl_quic_sstream_new(size_t init_buf_size) |
61 | 0 | { |
62 | 0 | QUIC_SSTREAM *qss; |
63 | |
|
64 | 0 | qss = OPENSSL_zalloc(sizeof(QUIC_SSTREAM)); |
65 | 0 | if (qss == NULL) |
66 | 0 | return NULL; |
67 | | |
68 | 0 | ring_buf_init(&qss->ring_buf); |
69 | 0 | if (!ring_buf_resize(&qss->ring_buf, init_buf_size, 0)) { |
70 | 0 | ring_buf_destroy(&qss->ring_buf, 0); |
71 | 0 | OPENSSL_free(qss); |
72 | 0 | return NULL; |
73 | 0 | } |
74 | | |
75 | 0 | ossl_uint_set_init(&qss->new_set); |
76 | 0 | ossl_uint_set_init(&qss->acked_set); |
77 | 0 | return qss; |
78 | 0 | } |
79 | | |
80 | | void ossl_quic_sstream_free(QUIC_SSTREAM *qss) |
81 | 0 | { |
82 | 0 | if (qss == NULL) |
83 | 0 | return; |
84 | | |
85 | 0 | ossl_uint_set_destroy(&qss->new_set); |
86 | 0 | ossl_uint_set_destroy(&qss->acked_set); |
87 | 0 | ring_buf_destroy(&qss->ring_buf, qss->cleanse); |
88 | 0 | OPENSSL_free(qss); |
89 | 0 | } |
90 | | |
91 | | int ossl_quic_sstream_get_stream_frame(QUIC_SSTREAM *qss, |
92 | | size_t skip, |
93 | | OSSL_QUIC_FRAME_STREAM *hdr, |
94 | | OSSL_QTX_IOVEC *iov, |
95 | | size_t *num_iov) |
96 | 0 | { |
97 | 0 | size_t num_iov_ = 0, src_len = 0, total_len = 0, i; |
98 | 0 | uint64_t max_len; |
99 | 0 | const unsigned char *src = NULL; |
100 | 0 | UINT_SET_ITEM *range = ossl_list_uint_set_head(&qss->new_set); |
101 | |
|
102 | 0 | if (*num_iov < 2) |
103 | 0 | return 0; |
104 | | |
105 | 0 | for (i = 0; i < skip && range != NULL; ++i) |
106 | 0 | range = ossl_list_uint_set_next(range); |
107 | |
|
108 | 0 | if (range == NULL) { |
109 | 0 | if (i < skip) |
110 | | /* Don't return FIN for infinitely increasing skip */ |
111 | 0 | return 0; |
112 | | |
113 | | /* No new bytes to send, but we might have a FIN */ |
114 | 0 | if (!qss->have_final_size || qss->sent_final_size) |
115 | 0 | return 0; |
116 | | |
117 | 0 | hdr->offset = qss->ring_buf.head_offset; |
118 | 0 | hdr->len = 0; |
119 | 0 | hdr->is_fin = 1; |
120 | 0 | *num_iov = 0; |
121 | 0 | return 1; |
122 | 0 | } |
123 | | |
124 | | /* |
125 | | * We can only send a contiguous range of logical bytes in a single |
126 | | * stream frame, so limit ourselves to the range of the first set entry. |
127 | | * |
128 | | * Set entries never have 'adjacent' entries so we don't have to worry |
129 | | * about them here. |
130 | | */ |
131 | 0 | max_len = range->range.end - range->range.start + 1; |
132 | |
|
133 | 0 | for (i = 0;; ++i) { |
134 | 0 | if (total_len >= max_len) |
135 | 0 | break; |
136 | | |
137 | 0 | if (!ring_buf_get_buf_at(&qss->ring_buf, |
138 | 0 | range->range.start + total_len, |
139 | 0 | &src, &src_len)) |
140 | 0 | return 0; |
141 | | |
142 | 0 | if (src_len == 0) |
143 | 0 | break; |
144 | | |
145 | 0 | assert(i < 2); |
146 | | |
147 | 0 | if (total_len + src_len > max_len) |
148 | 0 | src_len = (size_t)(max_len - total_len); |
149 | |
|
150 | 0 | iov[num_iov_].buf = src; |
151 | 0 | iov[num_iov_].buf_len = src_len; |
152 | |
|
153 | 0 | total_len += src_len; |
154 | 0 | ++num_iov_; |
155 | 0 | } |
156 | | |
157 | 0 | hdr->offset = range->range.start; |
158 | 0 | hdr->len = total_len; |
159 | 0 | hdr->is_fin = qss->have_final_size |
160 | 0 | && hdr->offset + hdr->len == qss->ring_buf.head_offset; |
161 | |
|
162 | 0 | *num_iov = num_iov_; |
163 | 0 | return 1; |
164 | 0 | } |
165 | | |
166 | | int ossl_quic_sstream_has_pending(QUIC_SSTREAM *qss) |
167 | 0 | { |
168 | 0 | OSSL_QUIC_FRAME_STREAM shdr; |
169 | 0 | OSSL_QTX_IOVEC iov[2]; |
170 | 0 | size_t num_iov = OSSL_NELEM(iov); |
171 | |
|
172 | 0 | return ossl_quic_sstream_get_stream_frame(qss, 0, &shdr, iov, &num_iov); |
173 | 0 | } |
174 | | |
175 | | uint64_t ossl_quic_sstream_get_cur_size(QUIC_SSTREAM *qss) |
176 | 0 | { |
177 | 0 | return qss->ring_buf.head_offset; |
178 | 0 | } |
179 | | |
180 | | int ossl_quic_sstream_mark_transmitted(QUIC_SSTREAM *qss, |
181 | | uint64_t start, |
182 | | uint64_t end) |
183 | 0 | { |
184 | 0 | UINT_RANGE r; |
185 | |
|
186 | 0 | r.start = start; |
187 | 0 | r.end = end; |
188 | |
|
189 | 0 | if (!ossl_uint_set_remove(&qss->new_set, &r)) |
190 | 0 | return 0; |
191 | | |
192 | 0 | return 1; |
193 | 0 | } |
194 | | |
195 | | int ossl_quic_sstream_mark_transmitted_fin(QUIC_SSTREAM *qss, |
196 | | uint64_t final_size) |
197 | 0 | { |
198 | | /* |
199 | | * We do not really need final_size since we already know the size of the |
200 | | * stream, but this serves as a sanity check. |
201 | | */ |
202 | 0 | if (!qss->have_final_size || final_size != qss->ring_buf.head_offset) |
203 | 0 | return 0; |
204 | | |
205 | 0 | qss->sent_final_size = 1; |
206 | 0 | return 1; |
207 | 0 | } |
208 | | |
209 | | int ossl_quic_sstream_mark_lost(QUIC_SSTREAM *qss, |
210 | | uint64_t start, |
211 | | uint64_t end) |
212 | 0 | { |
213 | 0 | UINT_RANGE r; |
214 | 0 | r.start = start; |
215 | 0 | r.end = end; |
216 | | |
217 | | /* |
218 | | * We lost a range of stream data bytes, so reinsert them into the new set, |
219 | | * so that they are returned once more by ossl_quic_sstream_get_stream_frame. |
220 | | */ |
221 | 0 | if (!ossl_uint_set_insert(&qss->new_set, &r)) |
222 | 0 | return 0; |
223 | | |
224 | 0 | return 1; |
225 | 0 | } |
226 | | |
227 | | int ossl_quic_sstream_mark_lost_fin(QUIC_SSTREAM *qss) |
228 | 0 | { |
229 | 0 | if (qss->acked_final_size) |
230 | | /* Does not make sense to lose a FIN after it has been ACKed */ |
231 | 0 | return 0; |
232 | | |
233 | | /* FIN was lost, so we need to transmit it again. */ |
234 | 0 | qss->sent_final_size = 0; |
235 | 0 | return 1; |
236 | 0 | } |
237 | | |
238 | | int ossl_quic_sstream_mark_acked(QUIC_SSTREAM *qss, |
239 | | uint64_t start, |
240 | | uint64_t end) |
241 | 0 | { |
242 | 0 | UINT_RANGE r; |
243 | 0 | r.start = start; |
244 | 0 | r.end = end; |
245 | |
|
246 | 0 | if (!ossl_uint_set_insert(&qss->acked_set, &r)) |
247 | 0 | return 0; |
248 | | |
249 | 0 | qss_cull(qss); |
250 | 0 | return 1; |
251 | 0 | } |
252 | | |
253 | | int ossl_quic_sstream_mark_acked_fin(QUIC_SSTREAM *qss) |
254 | 0 | { |
255 | 0 | if (!qss->have_final_size) |
256 | | /* Cannot ack final size before we have a final size */ |
257 | 0 | return 0; |
258 | | |
259 | 0 | qss->acked_final_size = 1; |
260 | 0 | return 1; |
261 | 0 | } |
262 | | |
263 | | void ossl_quic_sstream_fin(QUIC_SSTREAM *qss) |
264 | 0 | { |
265 | 0 | if (qss->have_final_size) |
266 | 0 | return; |
267 | | |
268 | 0 | qss->have_final_size = 1; |
269 | 0 | } |
270 | | |
271 | | int ossl_quic_sstream_get_final_size(QUIC_SSTREAM *qss, uint64_t *final_size) |
272 | 0 | { |
273 | 0 | if (!qss->have_final_size) |
274 | 0 | return 0; |
275 | | |
276 | 0 | if (final_size != NULL) |
277 | 0 | *final_size = qss->ring_buf.head_offset; |
278 | |
|
279 | 0 | return 1; |
280 | 0 | } |
281 | | |
282 | | int ossl_quic_sstream_append(QUIC_SSTREAM *qss, |
283 | | const unsigned char *buf, |
284 | | size_t buf_len, |
285 | | size_t *consumed) |
286 | 0 | { |
287 | 0 | size_t l, consumed_ = 0; |
288 | 0 | UINT_RANGE r; |
289 | 0 | struct ring_buf old_ring_buf = qss->ring_buf; |
290 | |
|
291 | 0 | if (qss->have_final_size) { |
292 | 0 | *consumed = 0; |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | | /* |
297 | | * Note: It is assumed that ossl_quic_sstream_append will be called during a |
298 | | * call to e.g. SSL_write and this function is therefore designed to support |
299 | | * such semantics. In particular, the buffer pointed to by buf is only |
300 | | * assumed to be valid for the duration of this call, therefore we must copy |
301 | | * the data here. We will later copy-and-encrypt the data during packet |
302 | | * encryption, so this is a two-copy design. Supporting a one-copy design in |
303 | | * the future will require applications to use a different kind of API. |
304 | | * Supporting such changes in future will require corresponding enhancements |
305 | | * to this code. |
306 | | */ |
307 | 0 | while (buf_len > 0) { |
308 | 0 | l = ring_buf_push(&qss->ring_buf, buf, buf_len); |
309 | 0 | if (l == 0) |
310 | 0 | break; |
311 | | |
312 | 0 | buf += l; |
313 | 0 | buf_len -= l; |
314 | 0 | consumed_ += l; |
315 | 0 | } |
316 | |
|
317 | 0 | if (consumed_ > 0) { |
318 | 0 | r.start = old_ring_buf.head_offset; |
319 | 0 | r.end = r.start + consumed_ - 1; |
320 | 0 | assert(r.end + 1 == qss->ring_buf.head_offset); |
321 | 0 | if (!ossl_uint_set_insert(&qss->new_set, &r)) { |
322 | 0 | qss->ring_buf = old_ring_buf; |
323 | 0 | *consumed = 0; |
324 | 0 | return 0; |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | 0 | *consumed = consumed_; |
329 | 0 | return 1; |
330 | 0 | } |
331 | | |
332 | | static void qss_cull(QUIC_SSTREAM *qss) |
333 | 0 | { |
334 | 0 | UINT_SET_ITEM *h = ossl_list_uint_set_head(&qss->acked_set); |
335 | | |
336 | | /* |
337 | | * Potentially cull data from our ring buffer. This can happen once data has |
338 | | * been ACKed and we know we are never going to have to transmit it again. |
339 | | * |
340 | | * Since we use a ring buffer design for simplicity, we cannot cull byte n + |
341 | | * k (for k > 0) from the ring buffer until byte n has also been culled. |
342 | | * This means if parts of the stream get acknowledged out of order we might |
343 | | * keep around some data we technically don't need to for a while. The |
344 | | * impact of this is likely to be small and limited to quite a short |
345 | | * duration, and doesn't justify the use of a more complex design. |
346 | | */ |
347 | | |
348 | | /* |
349 | | * We only need to check the first range entry in the integer set because we |
350 | | * can only cull contiguous areas at the start of the ring buffer anyway. |
351 | | */ |
352 | 0 | if (h != NULL) |
353 | 0 | ring_buf_cpop_range(&qss->ring_buf, h->range.start, h->range.end, |
354 | 0 | qss->cleanse); |
355 | 0 | } |
356 | | |
357 | | int ossl_quic_sstream_set_buffer_size(QUIC_SSTREAM *qss, size_t num_bytes) |
358 | 0 | { |
359 | 0 | return ring_buf_resize(&qss->ring_buf, num_bytes, qss->cleanse); |
360 | 0 | } |
361 | | |
362 | | size_t ossl_quic_sstream_get_buffer_size(QUIC_SSTREAM *qss) |
363 | 0 | { |
364 | 0 | return qss->ring_buf.alloc; |
365 | 0 | } |
366 | | |
367 | | size_t ossl_quic_sstream_get_buffer_used(QUIC_SSTREAM *qss) |
368 | 0 | { |
369 | 0 | return ring_buf_used(&qss->ring_buf); |
370 | 0 | } |
371 | | |
372 | | size_t ossl_quic_sstream_get_buffer_avail(QUIC_SSTREAM *qss) |
373 | 0 | { |
374 | 0 | return ring_buf_avail(&qss->ring_buf); |
375 | 0 | } |
376 | | |
377 | | int ossl_quic_sstream_is_totally_acked(QUIC_SSTREAM *qss) |
378 | 0 | { |
379 | 0 | UINT_RANGE r; |
380 | 0 | uint64_t cur_size; |
381 | |
|
382 | 0 | if (qss->have_final_size && !qss->acked_final_size) |
383 | 0 | return 0; |
384 | | |
385 | 0 | if (ossl_quic_sstream_get_cur_size(qss) == 0) |
386 | 0 | return 1; |
387 | | |
388 | 0 | if (ossl_list_uint_set_num(&qss->acked_set) != 1) |
389 | 0 | return 0; |
390 | | |
391 | 0 | r = ossl_list_uint_set_head(&qss->acked_set)->range; |
392 | 0 | cur_size = qss->ring_buf.head_offset; |
393 | | |
394 | | /* |
395 | | * The invariants of UINT_SET guarantee a single list element if we have a |
396 | | * single contiguous range, which is what we should have if everything has |
397 | | * been acked. |
398 | | */ |
399 | 0 | assert(r.end + 1 <= cur_size); |
400 | 0 | return r.start == 0 && r.end + 1 == cur_size; |
401 | 0 | } |
402 | | |
403 | | void ossl_quic_sstream_adjust_iov(size_t len, |
404 | | OSSL_QTX_IOVEC *iov, |
405 | | size_t num_iov) |
406 | 0 | { |
407 | 0 | size_t running = 0, i, iovlen; |
408 | |
|
409 | 0 | for (i = 0, running = 0; i < num_iov; ++i) { |
410 | 0 | iovlen = iov[i].buf_len; |
411 | |
|
412 | 0 | if (running >= len) |
413 | 0 | iov[i].buf_len = 0; |
414 | 0 | else if (running + iovlen > len) |
415 | 0 | iov[i].buf_len = len - running; |
416 | |
|
417 | 0 | running += iovlen; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | void ossl_quic_sstream_set_cleanse(QUIC_SSTREAM *qss, int cleanse) |
422 | 0 | { |
423 | 0 | qss->cleanse = cleanse; |
424 | 0 | } |