mirror of
https://github.com/openssl/openssl.git
synced 2024-11-23 18:13:39 +08:00
Implement BIO_s_dgram_mem() reusing the BIO_s_dgram_pair() code
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20012)
This commit is contained in:
parent
6e193d4d03
commit
3a857b9532
@ -11,15 +11,18 @@
|
||||
#include <errno.h>
|
||||
#include "bio_local.h"
|
||||
#include "internal/cryptlib.h"
|
||||
#include "internal/safe_math.h"
|
||||
|
||||
#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK)
|
||||
|
||||
OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
|
||||
|
||||
/* ===========================================================================
|
||||
* Byte-wise ring buffer which supports pushing and popping blocks of multiple
|
||||
* bytes at a time.
|
||||
*/
|
||||
struct ring_buf {
|
||||
unsigned char *start; /* start of buffer, never changes */
|
||||
unsigned char *start; /* start of buffer */
|
||||
size_t len; /* size of buffer allocation in bytes */
|
||||
size_t count; /* number of bytes currently pushed */
|
||||
/*
|
||||
@ -114,6 +117,44 @@ static void ring_buf_clear(struct ring_buf *r)
|
||||
r->idx[0] = r->idx[1] = r->count = 0;
|
||||
}
|
||||
|
||||
static int ring_buf_resize(struct ring_buf *r, size_t nbytes)
|
||||
{
|
||||
unsigned char *new_start;
|
||||
|
||||
if (r->start == NULL)
|
||||
return ring_buf_init(r, nbytes);
|
||||
|
||||
if (nbytes == r->len)
|
||||
return 1;
|
||||
|
||||
if (r->count > 0 && nbytes < r->len)
|
||||
/* fail shrinking the ring buffer when there is any data in it */
|
||||
return 0;
|
||||
|
||||
new_start = OPENSSL_realloc(r->start, nbytes);
|
||||
if (new_start == NULL)
|
||||
return 0;
|
||||
|
||||
/* Moving tail if it is after (or equal to) head */
|
||||
if (r->count > 0) {
|
||||
if (r->idx[0] <= r->idx[1]) {
|
||||
size_t offset = nbytes - r->len;
|
||||
|
||||
memmove(new_start + r->idx[1] + offset, new_start + r->idx[1],
|
||||
r->len - r->idx[1]);
|
||||
r->idx[1] += offset;
|
||||
}
|
||||
} else {
|
||||
/* just reset the head/tail because it might be pointing outside */
|
||||
r->idx[0] = r->idx[1] = 0;
|
||||
}
|
||||
|
||||
r->start = new_start;
|
||||
r->len = nbytes;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ===========================================================================
|
||||
* BIO_s_dgram_pair is documented in BIO_s_dgram_pair(3).
|
||||
*
|
||||
@ -136,8 +177,11 @@ static void ring_buf_clear(struct ring_buf *r)
|
||||
struct bio_dgram_pair_st;
|
||||
static int dgram_pair_write(BIO *bio, const char *buf, int sz_);
|
||||
static int dgram_pair_read(BIO *bio, char *buf, int sz_);
|
||||
static int dgram_mem_read(BIO *bio, char *buf, int sz_);
|
||||
static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr);
|
||||
static long dgram_mem_ctrl(BIO *bio, int cmd, long num, void *ptr);
|
||||
static int dgram_pair_init(BIO *bio);
|
||||
static int dgram_mem_init(BIO *bio);
|
||||
static int dgram_pair_free(BIO *bio);
|
||||
static int dgram_pair_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
|
||||
size_t num_msg, uint64_t flags,
|
||||
@ -169,18 +213,40 @@ static const BIO_METHOD dgram_pair_method = {
|
||||
dgram_pair_recvmmsg,
|
||||
};
|
||||
|
||||
static const BIO_METHOD dgram_mem_method = {
|
||||
BIO_TYPE_DGRAM_MEM,
|
||||
"BIO dgram mem",
|
||||
bwrite_conv,
|
||||
dgram_pair_write,
|
||||
bread_conv,
|
||||
dgram_mem_read,
|
||||
NULL, /* dgram_pair_puts */
|
||||
NULL, /* dgram_pair_gets */
|
||||
dgram_mem_ctrl,
|
||||
dgram_mem_init,
|
||||
dgram_pair_free,
|
||||
NULL, /* dgram_pair_callback_ctrl */
|
||||
dgram_pair_sendmmsg,
|
||||
dgram_pair_recvmmsg,
|
||||
};
|
||||
|
||||
const BIO_METHOD *BIO_s_dgram_pair(void)
|
||||
{
|
||||
return &dgram_pair_method;
|
||||
}
|
||||
|
||||
const BIO_METHOD *BIO_s_dgram_mem(void)
|
||||
{
|
||||
return &dgram_mem_method;
|
||||
}
|
||||
|
||||
struct dgram_hdr {
|
||||
size_t len; /* payload length in bytes, not including this struct */
|
||||
BIO_ADDR src_addr, dst_addr; /* family == 0: not present */
|
||||
};
|
||||
|
||||
struct bio_dgram_pair_st {
|
||||
/* The other half of the BIO pair. */
|
||||
/* The other half of the BIO pair. NULL for dgram_mem. */
|
||||
BIO *peer;
|
||||
/* Writes are directed to our own ringbuf and reads to our peer. */
|
||||
struct ring_buf rbuf;
|
||||
@ -199,10 +265,13 @@ struct bio_dgram_pair_st {
|
||||
unsigned int no_trunc : 1; /* Reads fail if they would truncate */
|
||||
unsigned int local_addr_enable : 1; /* Can use BIO_MSG->local? */
|
||||
unsigned int role : 1; /* Determines lock order */
|
||||
unsigned int fixed_size : 1; /* Affects BIO_s_dgram_mem only */
|
||||
};
|
||||
|
||||
#define MIN_BUF_LEN (1024)
|
||||
|
||||
#define is_dgram_pair(b) (b->peer != NULL)
|
||||
|
||||
static int dgram_pair_init(BIO *bio)
|
||||
{
|
||||
struct bio_dgram_pair_st *b = OPENSSL_zalloc(sizeof(*b));
|
||||
@ -223,6 +292,24 @@ static int dgram_pair_init(BIO *bio)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dgram_mem_init(BIO *bio)
|
||||
{
|
||||
struct bio_dgram_pair_st *b;
|
||||
|
||||
if (!dgram_pair_init(bio))
|
||||
return 0;
|
||||
|
||||
b = bio->ptr;
|
||||
|
||||
if (ring_buf_init(&b->rbuf, b->req_buf_len) == 0) {
|
||||
ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bio->init = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dgram_pair_free(BIO *bio)
|
||||
{
|
||||
struct bio_dgram_pair_st *b;
|
||||
@ -312,22 +399,23 @@ static int dgram_pair_ctrl_destroy_bio_pair(BIO *bio1)
|
||||
BIO *bio2;
|
||||
struct bio_dgram_pair_st *b1 = bio1->ptr, *b2;
|
||||
|
||||
/* If we already don't have a peer, treat this as a no-op. */
|
||||
ring_buf_destroy(&b1->rbuf);
|
||||
bio1->init = 0;
|
||||
|
||||
/* Early return if we don't have a peer. */
|
||||
if (b1->peer == NULL)
|
||||
return 1;
|
||||
|
||||
bio2 = b1->peer;
|
||||
b2 = bio2->ptr;
|
||||
|
||||
/* Invariants. */
|
||||
if (!ossl_assert(b1->peer == bio2 && b2->peer == bio1))
|
||||
/* Invariant. */
|
||||
if (!ossl_assert(b2->peer == bio1))
|
||||
return 0;
|
||||
|
||||
/* Free buffers. */
|
||||
ring_buf_destroy(&b1->rbuf);
|
||||
ring_buf_destroy(&b2->rbuf);
|
||||
|
||||
bio1->init = 0;
|
||||
bio2->init = 0;
|
||||
b1->peer = NULL;
|
||||
b2->peer = NULL;
|
||||
@ -342,9 +430,12 @@ static int dgram_pair_ctrl_eof(BIO *bio)
|
||||
if (!ossl_assert(b != NULL))
|
||||
return -1;
|
||||
|
||||
/* If we have no peer, we can never read anything */
|
||||
if (b->peer == NULL)
|
||||
/* If we aren't initialized, we can never read anything */
|
||||
if (!bio->init)
|
||||
return 1;
|
||||
if (!is_dgram_pair(b))
|
||||
return 0;
|
||||
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
if (!ossl_assert(peerb != NULL))
|
||||
@ -372,14 +463,13 @@ static int dgram_pair_ctrl_set_write_buf_size(BIO *bio, size_t len)
|
||||
if (len < MIN_BUF_LEN)
|
||||
len = MIN_BUF_LEN;
|
||||
|
||||
/*
|
||||
* We have no peer yet, therefore the ring buffer should not have been
|
||||
* allocated yet.
|
||||
*/
|
||||
if (!ossl_assert(b->rbuf.start == NULL))
|
||||
return 0;
|
||||
if (b->rbuf.start != NULL) {
|
||||
if (!ring_buf_resize(&b->rbuf, len))
|
||||
return 0;
|
||||
}
|
||||
|
||||
b->req_buf_len = len;
|
||||
b->fixed_size = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -396,28 +486,30 @@ static int dgram_pair_ctrl_reset(BIO *bio)
|
||||
static size_t dgram_pair_ctrl_pending(BIO *bio)
|
||||
{
|
||||
size_t saved_idx, saved_count;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *peerb;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *readb;
|
||||
struct dgram_hdr hdr;
|
||||
size_t l;
|
||||
|
||||
/* Safe to check; peer may not change during this call */
|
||||
if (b->peer == NULL)
|
||||
/* Safe to check; init may not change during this call */
|
||||
if (!bio->init)
|
||||
return 0;
|
||||
if (is_dgram_pair(b))
|
||||
readb = b->peer->ptr;
|
||||
else
|
||||
readb = b;
|
||||
|
||||
if (CRYPTO_THREAD_write_lock(readb->lock) == 0)
|
||||
return 0;
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
saved_idx = readb->rbuf.idx[1];
|
||||
saved_count = readb->rbuf.count;
|
||||
|
||||
if (CRYPTO_THREAD_write_lock(peerb->lock) == 0)
|
||||
return 0;
|
||||
l = dgram_pair_read_inner(readb, (uint8_t *)&hdr, sizeof(hdr));
|
||||
|
||||
saved_idx = peerb->rbuf.idx[1];
|
||||
saved_count = peerb->rbuf.count;
|
||||
readb->rbuf.idx[1] = saved_idx;
|
||||
readb->rbuf.count = saved_count;
|
||||
|
||||
l = dgram_pair_read_inner(peerb, (uint8_t *)&hdr, sizeof(hdr));
|
||||
|
||||
peerb->rbuf.idx[1] = saved_idx;
|
||||
peerb->rbuf.count = saved_count;
|
||||
|
||||
CRYPTO_THREAD_unlock(peerb->lock);
|
||||
CRYPTO_THREAD_unlock(readb->lock);
|
||||
|
||||
if (!ossl_assert(l == 0 || l == sizeof(hdr)))
|
||||
return 0;
|
||||
@ -452,15 +544,18 @@ static size_t dgram_pair_ctrl_get_write_guarantee(BIO *bio)
|
||||
/* BIO_dgram_get_local_addr_cap (BIO_CTRL_DGRAM_GET_LOCAL_ADDR_CAP) */
|
||||
static int dgram_pair_ctrl_get_local_addr_cap(BIO *bio)
|
||||
{
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *peerb;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *readb;
|
||||
|
||||
if (b->peer == NULL)
|
||||
if (!bio->init)
|
||||
return 0;
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
if (is_dgram_pair(b))
|
||||
readb = b->peer->ptr;
|
||||
else
|
||||
readb = b;
|
||||
|
||||
return (~peerb->cap & (BIO_DGRAM_CAP_HANDLES_SRC_ADDR
|
||||
| BIO_DGRAM_CAP_PROVIDES_DST_ADDR)) == 0;
|
||||
return (~readb->cap & (BIO_DGRAM_CAP_HANDLES_SRC_ADDR
|
||||
| BIO_DGRAM_CAP_PROVIDES_DST_ADDR)) == 0;
|
||||
}
|
||||
|
||||
/* BIO_dgram_get_effective_caps (BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS) */
|
||||
@ -537,7 +632,7 @@ static int dgram_pair_ctrl_set_mtu(BIO *bio, size_t mtu)
|
||||
}
|
||||
|
||||
/* Partially threadsafe (some commands) */
|
||||
static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
static long dgram_mem_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
{
|
||||
long ret = 1;
|
||||
struct bio_dgram_pair_st *b = bio->ptr;
|
||||
@ -562,23 +657,6 @@ static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
ret = (long)b->req_buf_len;
|
||||
break;
|
||||
|
||||
/*
|
||||
* BIO_make_bio_pair: this is usually used by BIO_new_dgram_pair, though it
|
||||
* may be used manually after manually creating each half of a BIO pair
|
||||
* using BIO_new. This only needs to be called on one of the BIOs.
|
||||
*/
|
||||
case BIO_C_MAKE_BIO_PAIR: /* Non-threadsafe */
|
||||
ret = (long)dgram_pair_ctrl_make_bio_pair(bio, (BIO *)ptr);
|
||||
break;
|
||||
|
||||
/*
|
||||
* BIO_destroy_bio_pair: Manually disconnect two halves of a BIO pair so
|
||||
* that they are no longer peers.
|
||||
*/
|
||||
case BIO_C_DESTROY_BIO_PAIR: /* Non-threadsafe */
|
||||
dgram_pair_ctrl_destroy_bio_pair(bio);
|
||||
break;
|
||||
|
||||
/*
|
||||
* BIO_reset: Clear all data which was written to this side of the pair.
|
||||
*/
|
||||
@ -630,9 +708,6 @@ static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
|
||||
/* BIO_dgram_get_effective_caps */
|
||||
case BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS: /* Non-threadsafe */
|
||||
ret = (long)dgram_pair_ctrl_get_effective_caps(bio);
|
||||
break;
|
||||
|
||||
/* BIO_dgram_get_caps */
|
||||
case BIO_CTRL_DGRAM_GET_CAPS: /* Non-threadsafe */
|
||||
ret = (long)dgram_pair_ctrl_get_caps(bio);
|
||||
@ -669,6 +744,41 @@ static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
|
||||
{
|
||||
long ret = 1;
|
||||
|
||||
switch (cmd) {
|
||||
/*
|
||||
* BIO_make_bio_pair: this is usually used by BIO_new_dgram_pair, though it
|
||||
* may be used manually after manually creating each half of a BIO pair
|
||||
* using BIO_new. This only needs to be called on one of the BIOs.
|
||||
*/
|
||||
case BIO_C_MAKE_BIO_PAIR: /* Non-threadsafe */
|
||||
ret = (long)dgram_pair_ctrl_make_bio_pair(bio, (BIO *)ptr);
|
||||
break;
|
||||
|
||||
/*
|
||||
* BIO_destroy_bio_pair: Manually disconnect two halves of a BIO pair so
|
||||
* that they are no longer peers.
|
||||
*/
|
||||
case BIO_C_DESTROY_BIO_PAIR: /* Non-threadsafe */
|
||||
dgram_pair_ctrl_destroy_bio_pair(bio);
|
||||
break;
|
||||
|
||||
/* BIO_dgram_get_effective_caps */
|
||||
case BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS: /* Non-threadsafe */
|
||||
ret = (long)dgram_pair_ctrl_get_effective_caps(bio);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = dgram_mem_ctrl(bio, cmd, num, ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BIO_new_bio_dgram_pair(BIO **pbio1, size_t writebuf1,
|
||||
BIO **pbio2, size_t writebuf2)
|
||||
{
|
||||
@ -763,7 +873,7 @@ static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
|
||||
int is_multi)
|
||||
{
|
||||
size_t l, trunc = 0, saved_idx, saved_count;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *peerb;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *readb;
|
||||
struct dgram_hdr hdr;
|
||||
|
||||
if (!is_multi)
|
||||
@ -772,11 +882,14 @@ static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
|
||||
if (!bio->init)
|
||||
return -BIO_R_UNINITIALIZED;
|
||||
|
||||
if (!ossl_assert(b != NULL && b->peer != NULL))
|
||||
if (!ossl_assert(b != NULL))
|
||||
return -BIO_R_TRANSFER_ERROR;
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
if (!ossl_assert(peerb != NULL && peerb->rbuf.start != NULL))
|
||||
if (is_dgram_pair(b))
|
||||
readb = b->peer->ptr;
|
||||
else
|
||||
readb = b;
|
||||
if (!ossl_assert(readb != NULL && readb->rbuf.start != NULL))
|
||||
return -BIO_R_TRANSFER_ERROR;
|
||||
|
||||
if (sz > 0 && buf == NULL)
|
||||
@ -787,9 +900,9 @@ static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
|
||||
return -BIO_R_LOCAL_ADDR_NOT_AVAILABLE;
|
||||
|
||||
/* Read the header. */
|
||||
saved_idx = peerb->rbuf.idx[1];
|
||||
saved_count = peerb->rbuf.count;
|
||||
l = dgram_pair_read_inner(peerb, (uint8_t *)&hdr, sizeof(hdr));
|
||||
saved_idx = readb->rbuf.idx[1];
|
||||
saved_count = readb->rbuf.count;
|
||||
l = dgram_pair_read_inner(readb, (uint8_t *)&hdr, sizeof(hdr));
|
||||
if (l == 0) {
|
||||
/* Buffer was empty. */
|
||||
if (!is_multi)
|
||||
@ -811,13 +924,13 @@ static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
|
||||
trunc = hdr.len - sz;
|
||||
if (b->no_trunc) {
|
||||
/* Restore original state. */
|
||||
peerb->rbuf.idx[1] = saved_idx;
|
||||
peerb->rbuf.count = saved_count;
|
||||
readb->rbuf.idx[1] = saved_idx;
|
||||
readb->rbuf.count = saved_count;
|
||||
return -BIO_R_NON_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
l = dgram_pair_read_inner(peerb, (uint8_t *)buf, sz);
|
||||
l = dgram_pair_read_inner(readb, (uint8_t *)buf, sz);
|
||||
if (!ossl_assert(l == sz))
|
||||
/* We were somehow not able to read the entire datagram. */
|
||||
return -BIO_R_TRANSFER_ERROR;
|
||||
@ -826,7 +939,7 @@ static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
|
||||
* If the datagram was truncated due to an inadequate buffer, discard the
|
||||
* remainder.
|
||||
*/
|
||||
if (trunc > 0 && !ossl_assert(dgram_pair_read_inner(peerb, NULL, trunc) == trunc))
|
||||
if (trunc > 0 && !ossl_assert(dgram_pair_read_inner(readb, NULL, trunc) == trunc))
|
||||
/* We were somehow not able to read/skip the entire datagram. */
|
||||
return -BIO_R_TRANSFER_ERROR;
|
||||
|
||||
@ -902,7 +1015,8 @@ static int dgram_pair_read(BIO *bio, char *buf, int sz_)
|
||||
|
||||
l = dgram_pair_read_actual(bio, buf, (size_t)sz_, NULL, NULL, 0);
|
||||
if (l < 0) {
|
||||
ERR_raise(ERR_LIB_BIO, -l);
|
||||
if (l != -BIO_R_NON_FATAL)
|
||||
ERR_raise(ERR_LIB_BIO, -l);
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = (int)l;
|
||||
@ -922,22 +1036,25 @@ static int dgram_pair_recvmmsg(BIO *bio, BIO_MSG *msg,
|
||||
ossl_ssize_t l;
|
||||
BIO_MSG *m;
|
||||
size_t i;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *peerb;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *readb;
|
||||
|
||||
if (num_msg == 0) {
|
||||
*num_processed = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (b->peer == NULL) {
|
||||
if (!bio->init) {
|
||||
ERR_raise(ERR_LIB_BIO, BIO_R_BROKEN_PIPE);
|
||||
*num_processed = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
if (is_dgram_pair(b))
|
||||
readb = b->peer->ptr;
|
||||
else
|
||||
readb = b;
|
||||
|
||||
if (CRYPTO_THREAD_write_lock(peerb->lock) == 0) {
|
||||
if (CRYPTO_THREAD_write_lock(readb->lock) == 0) {
|
||||
ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
|
||||
*num_processed = 0;
|
||||
return 0;
|
||||
@ -965,10 +1082,68 @@ static int dgram_pair_recvmmsg(BIO *bio, BIO_MSG *msg,
|
||||
*num_processed = i;
|
||||
ret = 1;
|
||||
out:
|
||||
CRYPTO_THREAD_unlock(peerb->lock);
|
||||
CRYPTO_THREAD_unlock(readb->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Threadsafe */
|
||||
static int dgram_mem_read(BIO *bio, char *buf, int sz_)
|
||||
{
|
||||
int ret;
|
||||
ossl_ssize_t l;
|
||||
struct bio_dgram_pair_st *b = bio->ptr;
|
||||
|
||||
if (sz_ < 0) {
|
||||
ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (CRYPTO_THREAD_write_lock(b->lock) == 0) {
|
||||
ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l = dgram_pair_read_actual(bio, buf, (size_t)sz_, NULL, NULL, 0);
|
||||
if (l < 0) {
|
||||
if (l != -BIO_R_NON_FATAL)
|
||||
ERR_raise(ERR_LIB_BIO, -l);
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = (int)l;
|
||||
}
|
||||
|
||||
CRYPTO_THREAD_unlock(b->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the array growth based on the target size.
|
||||
*
|
||||
* The growth factor is a rational number and is defined by a numerator
|
||||
* and a denominator. According to Andrew Koenig in his paper "Why Are
|
||||
* Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less
|
||||
* than the golden ratio (1.618...).
|
||||
*
|
||||
* We use an expansion factor of 8 / 5 = 1.6
|
||||
*/
|
||||
static const size_t max_rbuf_size = SIZE_MAX / 2; /* unlimited in practice */
|
||||
static ossl_inline size_t compute_rbuf_growth(size_t target, size_t current)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
while (current < target) {
|
||||
if (current >= max_rbuf_size)
|
||||
return 0;
|
||||
|
||||
current = safe_muldiv_size_t(current, 8, 5, &err);
|
||||
if (err)
|
||||
return 0;
|
||||
if (current >= max_rbuf_size)
|
||||
current = max_rbuf_size;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/* Must hold local write lock */
|
||||
static size_t dgram_pair_write_inner(struct bio_dgram_pair_st *b, const uint8_t *buf, size_t sz)
|
||||
{
|
||||
@ -988,8 +1163,17 @@ static size_t dgram_pair_write_inner(struct bio_dgram_pair_st *b, const uint8_t
|
||||
* ringbuf and read from the peer ringbuf.
|
||||
*/
|
||||
ring_buf_head(&b->rbuf, &dst_buf, &dst_len);
|
||||
if (dst_len == 0)
|
||||
break;
|
||||
if (dst_len == 0) {
|
||||
size_t new_len;
|
||||
|
||||
if (!b->fixed_size) /* resizeable only unless size not set explicitly */
|
||||
break;
|
||||
/* increase the size */
|
||||
new_len = compute_rbuf_growth(b->req_buf_len + sz, b->req_buf_len);
|
||||
if (new_len == 0 || !ring_buf_resize(&b->rbuf, new_len))
|
||||
break;
|
||||
b->req_buf_len = new_len;
|
||||
}
|
||||
|
||||
if (dst_len > sz)
|
||||
dst_len = sz;
|
||||
@ -1015,7 +1199,7 @@ static ossl_ssize_t dgram_pair_write_actual(BIO *bio, const char *buf, size_t sz
|
||||
{
|
||||
static const BIO_ADDR zero_addr;
|
||||
size_t saved_idx, saved_count;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *peerb;
|
||||
struct bio_dgram_pair_st *b = bio->ptr, *readb;
|
||||
struct dgram_hdr hdr = {0};
|
||||
|
||||
if (!is_multi)
|
||||
@ -1024,7 +1208,7 @@ static ossl_ssize_t dgram_pair_write_actual(BIO *bio, const char *buf, size_t sz
|
||||
if (!bio->init)
|
||||
return -BIO_R_UNINITIALIZED;
|
||||
|
||||
if (!ossl_assert(b != NULL && b->peer != NULL && b->rbuf.start != NULL))
|
||||
if (!ossl_assert(b != NULL && b->rbuf.start != NULL))
|
||||
return -BIO_R_TRANSFER_ERROR;
|
||||
|
||||
if (sz > 0 && buf == NULL)
|
||||
@ -1033,8 +1217,11 @@ static ossl_ssize_t dgram_pair_write_actual(BIO *bio, const char *buf, size_t sz
|
||||
if (local != NULL && b->local_addr_enable == 0)
|
||||
return -BIO_R_LOCAL_ADDR_NOT_AVAILABLE;
|
||||
|
||||
peerb = b->peer->ptr;
|
||||
if (peer != NULL && (peerb->cap & BIO_DGRAM_CAP_HANDLES_DST_ADDR) == 0)
|
||||
if (is_dgram_pair(b))
|
||||
readb = b->peer->ptr;
|
||||
else
|
||||
readb = b;
|
||||
if (peer != NULL && (readb->cap & BIO_DGRAM_CAP_HANDLES_DST_ADDR) == 0)
|
||||
return -BIO_R_PEER_ADDR_NOT_AVAILABLE;
|
||||
|
||||
hdr.len = sz;
|
||||
|
@ -52,7 +52,11 @@ Any data written to a memory BIO can be recalled by reading from it.
|
||||
Unless the memory BIO is read only any data read from it is deleted from
|
||||
the BIO.
|
||||
|
||||
Memory BIOs support BIO_gets() and BIO_puts().
|
||||
Memory BIOs except BIO_s_dgram_mem() support BIO_gets() and BIO_puts().
|
||||
|
||||
BIO_s_dgram_mem() supports L<BIO_sendmmsg(3)> and L<BIO_recvmmsg(3)> calls
|
||||
and calls related to B<BIO_ADDR> and MTU handling similarly to the
|
||||
L<BIO_s_dgram_pair(3)>.
|
||||
|
||||
If the BIO_CLOSE flag is set when a memory BIO is freed then the underlying
|
||||
BUF_MEM structure is also freed.
|
||||
@ -93,10 +97,16 @@ made available from a static area of memory in the form of a BIO. The
|
||||
supplied data is read directly from the supplied buffer: it is B<not> copied
|
||||
first, so the supplied area of memory must be unchanged until the BIO is freed.
|
||||
|
||||
All of the five functions described above return an error with
|
||||
BIO_s_dgram_mem().
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
Writes to memory BIOs will always succeed if memory is available: that is
|
||||
their size can grow indefinitely.
|
||||
their size can grow indefinitely. An exception is BIO_s_dgram_mem() when
|
||||
L<BIO_set_write_buf_size(3)> is called on it. In such case the write buffer
|
||||
size will be fixed and any writes that would overflow the buffer will return
|
||||
an error.
|
||||
|
||||
Every write after partial read (not all data in the memory buffer was read)
|
||||
to a read write memory BIO will have to move the unread data with an internal
|
||||
@ -169,7 +179,7 @@ Extract the BUF_MEM structure from a memory BIO and then free up the BIO:
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2000-2020 The OpenSSL Project Authors. All Rights Reserved.
|
||||
Copyright 2000-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
|
||||
|
@ -69,6 +69,7 @@ extern "C" {
|
||||
# endif
|
||||
# define BIO_TYPE_CORE_TO_PROV (25|BIO_TYPE_SOURCE_SINK)
|
||||
# define BIO_TYPE_DGRAM_PAIR (26|BIO_TYPE_SOURCE_SINK)
|
||||
# define BIO_TYPE_DGRAM_MEM (27|BIO_TYPE_SOURCE_SINK)
|
||||
|
||||
#define BIO_TYPE_START 128
|
||||
|
||||
@ -730,7 +731,9 @@ int BIO_nwrite0(BIO *bio, char **buf);
|
||||
int BIO_nwrite(BIO *bio, char **buf, int num);
|
||||
|
||||
const BIO_METHOD *BIO_s_mem(void);
|
||||
# ifndef OPENSSL_NO_DGRAM
|
||||
const BIO_METHOD *BIO_s_dgram_mem(void);
|
||||
# endif
|
||||
const BIO_METHOD *BIO_s_secmem(void);
|
||||
BIO *BIO_new_mem_buf(const void *buf, int len);
|
||||
# ifndef OPENSSL_NO_SOCK
|
||||
|
@ -1292,9 +1292,11 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
|
||||
&& level != OSSL_RECORD_PROTECTION_LEVEL_NONE)
|
||||
epoch = DTLS_RECORD_LAYER_get_r_epoch(&s->rlayer) + 1; /* new epoch */
|
||||
|
||||
#ifndef OPENSSL_NO_DGRAM
|
||||
if (SSL_CONNECTION_IS_DTLS(s))
|
||||
next = BIO_new(BIO_s_dgram_mem());
|
||||
else
|
||||
#endif
|
||||
next = BIO_new(BIO_s_mem());
|
||||
|
||||
if (next == NULL) {
|
||||
|
@ -489,7 +489,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_bio_dgram_pair(void)
|
||||
static int test_bio_dgram_pair(int idx)
|
||||
{
|
||||
int testresult = 0, blen, mtu1, mtu2, r;
|
||||
BIO *bio1 = NULL, *bio2 = NULL;
|
||||
@ -513,8 +513,13 @@ static int test_bio_dgram_pair(void)
|
||||
for (i = 0; i < OSSL_NELEM(key); ++i)
|
||||
key[i] = test_random();
|
||||
|
||||
if (!TEST_int_eq(BIO_new_bio_dgram_pair(&bio1, 0, &bio2, 0), 1))
|
||||
goto err;
|
||||
if (idx == 0) {
|
||||
if (!TEST_int_eq(BIO_new_bio_dgram_pair(&bio1, 0, &bio2, 0), 1))
|
||||
goto err;
|
||||
} else {
|
||||
if (!TEST_ptr(bio1 = bio2 = BIO_new(BIO_s_dgram_mem())))
|
||||
goto err;
|
||||
}
|
||||
|
||||
mtu1 = BIO_dgram_get_mtu(bio1);
|
||||
if (!TEST_int_ge(mtu1, 1280))
|
||||
@ -530,7 +535,7 @@ static int test_bio_dgram_pair(void)
|
||||
if (!TEST_int_le(mtu1, sizeof(scratch) - 4))
|
||||
goto err;
|
||||
|
||||
for (i = 0;; ++i) {
|
||||
for (i = 0; idx == 0 || i < 9; ++i) {
|
||||
if (!TEST_int_eq(random_data(key, scratch, sizeof(scratch), i), 1))
|
||||
goto err;
|
||||
|
||||
@ -630,8 +635,8 @@ static int test_bio_dgram_pair(void)
|
||||
msgs[0].peer = addr1;
|
||||
|
||||
/* fails due to lack of caps on peer */
|
||||
if (!TEST_false(BIO_sendmmsg(bio1, msgs, sizeof(BIO_MSG), OSSL_NELEM(msgs),
|
||||
0, &num_processed))
|
||||
if (!TEST_false(BIO_sendmmsg(bio1, msgs, sizeof(BIO_MSG),
|
||||
OSSL_NELEM(msgs), 0, &num_processed))
|
||||
|| !TEST_size_t_eq(num_processed, 0))
|
||||
goto err;
|
||||
|
||||
@ -644,7 +649,7 @@ static int test_bio_dgram_pair(void)
|
||||
if (!TEST_int_eq(BIO_dgram_get_effective_caps(bio1), ref_caps))
|
||||
goto err;
|
||||
|
||||
if (!TEST_int_eq(BIO_dgram_get_effective_caps(bio2), 0))
|
||||
if (idx == 0 && !TEST_int_eq(BIO_dgram_get_effective_caps(bio2), 0))
|
||||
goto err;
|
||||
|
||||
if (!TEST_int_eq(BIO_dgram_set_caps(bio1, ref_caps), 1))
|
||||
@ -739,7 +744,8 @@ static int test_bio_dgram_pair(void)
|
||||
|
||||
testresult = 1;
|
||||
err:
|
||||
BIO_free(bio1);
|
||||
if (idx == 0)
|
||||
BIO_free(bio1);
|
||||
BIO_free(bio2);
|
||||
BIO_ADDR_free(addr1);
|
||||
BIO_ADDR_free(addr2);
|
||||
@ -760,7 +766,7 @@ int setup_tests(void)
|
||||
#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK)
|
||||
ADD_ALL_TESTS(test_bio_dgram, OSSL_NELEM(bio_dgram_cases));
|
||||
# if !defined(OPENSSL_NO_CHACHA)
|
||||
ADD_TEST(test_bio_dgram_pair);
|
||||
ADD_ALL_TESTS(test_bio_dgram_pair, 2);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <openssl/bio.h>
|
||||
#include "testutil.h"
|
||||
|
||||
#ifndef OPENSSL_NO_DGRAM
|
||||
static int test_dgram(void)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_dgram_mem()), *rbio = NULL;
|
||||
@ -98,13 +99,17 @@ static int test_dgram(void)
|
||||
|| !TEST_true(BIO_should_retry(bio)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_int_eq(BIO_dgram_set_mtu(bio, 123456), 1)
|
||||
|| !TEST_int_eq(BIO_dgram_get_mtu(bio), 123456))
|
||||
goto err;
|
||||
|
||||
testresult = 1;
|
||||
err:
|
||||
BIO_free(rbio);
|
||||
BIO_free(bio);
|
||||
return testresult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int setup_tests(void)
|
||||
{
|
||||
@ -113,7 +118,9 @@ int setup_tests(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_DGRAM
|
||||
ADD_TEST(test_dgram);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -5456,7 +5456,7 @@ CMS_EnvelopedData_decrypt ? 3_2_0 EXIST::FUNCTION:CMS
|
||||
CMS_SignedData_free ? 3_2_0 EXIST::FUNCTION:CMS
|
||||
CMS_SignedData_new ? 3_2_0 EXIST::FUNCTION:CMS
|
||||
CMS_SignedData_verify ? 3_2_0 EXIST::FUNCTION:CMS
|
||||
BIO_s_dgram_mem ? 3_2_0 EXIST::FUNCTION:
|
||||
BIO_s_dgram_mem ? 3_2_0 EXIST::FUNCTION:DGRAM
|
||||
BIO_recvmmsg ? 3_2_0 EXIST::FUNCTION:
|
||||
BIO_sendmmsg ? 3_2_0 EXIST::FUNCTION:
|
||||
BIO_meth_set_sendmmsg ? 3_2_0 EXIST::FUNCTION:
|
||||
|
Loading…
Reference in New Issue
Block a user