mirror of
https://github.com/openssl/openssl.git
synced 2024-12-21 06:09:35 +08:00
2113ea584c
Add API calls to avoid copying data when reading These are ossl_quic_rstream_get_record() and ossl_quic_rstream_release_record(). Add side storage for the stream frame data. When there are too many packets referenced by the receiving stream the function ossl_quic_rstream_move_to_rbuf() can be called to move the data to a ring buffer. Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19794)
289 lines
7.5 KiB
C
289 lines
7.5 KiB
C
/*
|
|
* Copyright 2022 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)) {
|
|
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)
|
|
{
|
|
if (qrs == NULL)
|
|
return;
|
|
|
|
ossl_sframe_list_destroy(&qrs->fl);
|
|
ring_buf_destroy(&qrs->rbuf);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
/* TODO(QUIC): Do we need to distinguish different error conditions ? */
|
|
if (ossl_sframe_list_is_head_locked(&qrs->fl))
|
|
return 0;
|
|
|
|
if (!ring_buf_resize(&qrs->rbuf, rbuf_size))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|