aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssl/ssl/quic/quic_rstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssl/ssl/quic/quic_rstream.c')
-rw-r--r--crypto/openssl/ssl/quic/quic_rstream.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/crypto/openssl/ssl/quic/quic_rstream.c b/crypto/openssl/ssl/quic/quic_rstream.c
new file mode 100644
index 000000000000..dd3dbf756b44
--- /dev/null
+++ b/crypto/openssl/ssl/quic/quic_rstream.c
@@ -0,0 +1,295 @@
+/*
+* Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+*
+* Licensed under the Apache License 2.0 (the "License"). You may not use
+* this file except in compliance with the License. You can obtain a copy
+* in the file LICENSE in the source distribution or at
+* https://www.openssl.org/source/license.html
+*/
+#include <openssl/err.h>
+#include "internal/common.h"
+#include "internal/time.h"
+#include "internal/quic_stream.h"
+#include "internal/quic_sf_list.h"
+#include "internal/ring_buf.h"
+
+struct quic_rstream_st {
+ SFRAME_LIST fl;
+ QUIC_RXFC *rxfc;
+ OSSL_STATM *statm;
+ UINT_RANGE head_range;
+ struct ring_buf rbuf;
+};
+
+QUIC_RSTREAM *ossl_quic_rstream_new(QUIC_RXFC *rxfc,
+ OSSL_STATM *statm, size_t rbuf_size)
+{
+ QUIC_RSTREAM *ret = OPENSSL_zalloc(sizeof(*ret));
+
+ if (ret == NULL)
+ return NULL;
+
+ ring_buf_init(&ret->rbuf);
+ if (!ring_buf_resize(&ret->rbuf, rbuf_size, 0)) {
+ OPENSSL_free(ret);
+ return NULL;
+ }
+
+ ossl_sframe_list_init(&ret->fl);
+ ret->rxfc = rxfc;
+ ret->statm = statm;
+ return ret;
+}
+
+void ossl_quic_rstream_free(QUIC_RSTREAM *qrs)
+{
+ int cleanse;
+
+ if (qrs == NULL)
+ return;
+
+ cleanse = qrs->fl.cleanse;
+ ossl_sframe_list_destroy(&qrs->fl);
+ ring_buf_destroy(&qrs->rbuf, cleanse);
+ OPENSSL_free(qrs);
+}
+
+int ossl_quic_rstream_queue_data(QUIC_RSTREAM *qrs, OSSL_QRX_PKT *pkt,
+ uint64_t offset,
+ const unsigned char *data, uint64_t data_len,
+ int fin)
+{
+ UINT_RANGE range;
+
+ if ((data == NULL && data_len != 0) || (data_len == 0 && fin == 0)) {
+ /* empty frame allowed only at the end of the stream */
+ ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ range.start = offset;
+ range.end = offset + data_len;
+
+ return ossl_sframe_list_insert(&qrs->fl, &range, pkt, data, fin);
+}
+
+static int read_internal(QUIC_RSTREAM *qrs, unsigned char *buf, size_t size,
+ size_t *readbytes, int *fin, int drop)
+{
+ void *iter = NULL;
+ UINT_RANGE range;
+ const unsigned char *data;
+ uint64_t offset = 0;
+ size_t readbytes_ = 0;
+ int fin_ = 0, ret = 1;
+
+ while (ossl_sframe_list_peek(&qrs->fl, &iter, &range, &data, &fin_)) {
+ size_t l = (size_t)(range.end - range.start);
+
+ if (l > size) {
+ l = size;
+ fin_ = 0;
+ }
+ offset = range.start + l;
+ if (l == 0)
+ break;
+
+ if (data == NULL) {
+ size_t max_len;
+
+ data = ring_buf_get_ptr(&qrs->rbuf, range.start, &max_len);
+ if (!ossl_assert(data != NULL))
+ return 0;
+ if (max_len < l) {
+ memcpy(buf, data, max_len);
+ size -= max_len;
+ buf += max_len;
+ readbytes_ += max_len;
+ l -= max_len;
+ data = ring_buf_get_ptr(&qrs->rbuf, range.start + max_len,
+ &max_len);
+ if (!ossl_assert(data != NULL) || !ossl_assert(max_len > l))
+ return 0;
+ }
+ }
+
+ memcpy(buf, data, l);
+ size -= l;
+ buf += l;
+ readbytes_ += l;
+ if (size == 0)
+ break;
+ }
+
+ if (drop && offset != 0) {
+ ret = ossl_sframe_list_drop_frames(&qrs->fl, offset);
+ ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1, qrs->fl.cleanse);
+ }
+
+ if (ret) {
+ *readbytes = readbytes_;
+ *fin = fin_;
+ }
+
+ return ret;
+}
+
+static OSSL_TIME get_rtt(QUIC_RSTREAM *qrs)
+{
+ OSSL_TIME rtt;
+
+ if (qrs->statm != NULL) {
+ OSSL_RTT_INFO rtt_info;
+
+ ossl_statm_get_rtt_info(qrs->statm, &rtt_info);
+ rtt = rtt_info.smoothed_rtt;
+ } else {
+ rtt = ossl_time_zero();
+ }
+ return rtt;
+}
+
+int ossl_quic_rstream_read(QUIC_RSTREAM *qrs, unsigned char *buf, size_t size,
+ size_t *readbytes, int *fin)
+{
+ OSSL_TIME rtt = get_rtt(qrs);
+
+ if (!read_internal(qrs, buf, size, readbytes, fin, 1))
+ return 0;
+
+ if (qrs->rxfc != NULL
+ && !ossl_quic_rxfc_on_retire(qrs->rxfc, *readbytes, rtt))
+ return 0;
+
+ return 1;
+}
+
+int ossl_quic_rstream_peek(QUIC_RSTREAM *qrs, unsigned char *buf, size_t size,
+ size_t *readbytes, int *fin)
+{
+ return read_internal(qrs, buf, size, readbytes, fin, 0);
+}
+
+int ossl_quic_rstream_available(QUIC_RSTREAM *qrs, size_t *avail, int *fin)
+{
+ void *iter = NULL;
+ UINT_RANGE range;
+ const unsigned char *data;
+ uint64_t avail_ = 0;
+
+ while (ossl_sframe_list_peek(&qrs->fl, &iter, &range, &data, fin))
+ avail_ += range.end - range.start;
+
+#if SIZE_MAX < UINT64_MAX
+ *avail = avail_ > SIZE_MAX ? SIZE_MAX : (size_t)avail_;
+#else
+ *avail = (size_t)avail_;
+#endif
+ return 1;
+}
+
+int ossl_quic_rstream_get_record(QUIC_RSTREAM *qrs,
+ const unsigned char **record, size_t *rec_len,
+ int *fin)
+{
+ const unsigned char *record_ = NULL;
+ size_t rec_len_, max_len;
+
+ if (!ossl_sframe_list_lock_head(&qrs->fl, &qrs->head_range, &record_, fin)) {
+ /* No head frame to lock and return */
+ *record = NULL;
+ *rec_len = 0;
+ return 1;
+ }
+
+ /* if final empty frame, we drop it immediately */
+ if (qrs->head_range.end == qrs->head_range.start) {
+ if (!ossl_assert(*fin))
+ return 0;
+ if (!ossl_sframe_list_drop_frames(&qrs->fl, qrs->head_range.end))
+ return 0;
+ }
+
+ rec_len_ = (size_t)(qrs->head_range.end - qrs->head_range.start);
+
+ if (record_ == NULL && rec_len_ != 0) {
+ record_ = ring_buf_get_ptr(&qrs->rbuf, qrs->head_range.start,
+ &max_len);
+ if (!ossl_assert(record_ != NULL))
+ return 0;
+ if (max_len < rec_len_) {
+ rec_len_ = max_len;
+ qrs->head_range.end = qrs->head_range.start + max_len;
+ }
+ }
+
+ *rec_len = rec_len_;
+ *record = record_;
+ return 1;
+}
+
+
+int ossl_quic_rstream_release_record(QUIC_RSTREAM *qrs, size_t read_len)
+{
+ uint64_t offset;
+
+ if (!ossl_sframe_list_is_head_locked(&qrs->fl))
+ return 0;
+
+ if (read_len > qrs->head_range.end - qrs->head_range.start) {
+ if (read_len != SIZE_MAX)
+ return 0;
+ offset = qrs->head_range.end;
+ } else {
+ offset = qrs->head_range.start + read_len;
+ }
+
+ if (!ossl_sframe_list_drop_frames(&qrs->fl, offset))
+ return 0;
+
+ if (offset > 0)
+ ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1, qrs->fl.cleanse);
+
+ if (qrs->rxfc != NULL) {
+ OSSL_TIME rtt = get_rtt(qrs);
+
+ if (!ossl_quic_rxfc_on_retire(qrs->rxfc, offset, rtt))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int write_at_ring_buf_cb(uint64_t logical_offset,
+ const unsigned char *buf,
+ size_t buf_len,
+ void *cb_arg)
+{
+ struct ring_buf *rbuf = cb_arg;
+
+ return ring_buf_write_at(rbuf, logical_offset, buf, buf_len);
+}
+
+int ossl_quic_rstream_move_to_rbuf(QUIC_RSTREAM *qrs)
+{
+ if (ring_buf_avail(&qrs->rbuf) == 0)
+ return 0;
+ return ossl_sframe_list_move_data(&qrs->fl,
+ write_at_ring_buf_cb, &qrs->rbuf);
+}
+
+int ossl_quic_rstream_resize_rbuf(QUIC_RSTREAM *qrs, size_t rbuf_size)
+{
+ if (ossl_sframe_list_is_head_locked(&qrs->fl))
+ return 0;
+
+ if (!ring_buf_resize(&qrs->rbuf, rbuf_size, qrs->fl.cleanse))
+ return 0;
+
+ return 1;
+}
+
+void ossl_quic_rstream_set_cleanse(QUIC_RSTREAM *qrs, int cleanse)
+{
+ qrs->fl.cleanse = cleanse;
+}