From: Hugo Landau Date: Tue, 31 May 2022 19:22:40 +0000 (+0100) Subject: BIO_s_dgram_pair X-Git-Tag: openssl-3.2.0-alpha1~2050 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b88ce46ee88c4128f72694e42160622844971d04;p=thirdparty%2Fopenssl.git BIO_s_dgram_pair Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/18442) --- diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c index 7f8ae17245..6fe295ee52 100644 --- a/crypto/bio/bio_err.c +++ b/crypto/bio/bio_err.c @@ -78,6 +78,8 @@ static const ERR_STRING_DATA BIO_str_reasons[] = { {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_WSASTARTUP), "WSAStartup"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_LOCAL_ADDR_NOT_AVAILABLE), "local address not available"}, + {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_PEER_ADDR_NOT_AVAILABLE), + "peer address not available"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NON_FATAL), "non-fatal or transient error"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_PORT_MISMATCH), diff --git a/crypto/bio/bss_dgram_pair.c b/crypto/bio/bss_dgram_pair.c new file mode 100644 index 0000000000..f2d6ecbb56 --- /dev/null +++ b/crypto/bio/bss_dgram_pair.c @@ -0,0 +1,1136 @@ +/* + * 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 +#include +#include "bio_local.h" +#include "internal/cryptlib.h" + +#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK) + +/* =========================================================================== + * 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 */ + size_t len; /* size of buffer allocation in bytes */ + size_t count; /* number of bytes currently pushed */ + /* + * These index into start. Where idx[0] == idx[1], the buffer is full + * (if count is nonzero) and empty otherwise. + */ + size_t idx[2]; /* 0: head, 1: tail */ +}; + +static int ring_buf_init(struct ring_buf *r, size_t nbytes) +{ + r->start = OPENSSL_malloc(nbytes); + if (r->start == NULL) + return 0; + + r->len = nbytes; + r->idx[0] = r->idx[1] = r->count = 0; + return 1; +} + +static void ring_buf_destroy(struct ring_buf *r) +{ + OPENSSL_free(r->start); + r->start = NULL; + r->len = 0; + r->count = 0; +} + +/* + * Get a pointer to the next place to write data to be pushed to the ring buffer + * (idx=0), or the next data to be popped from the ring buffer (idx=1). The + * pointer is written to *buf and the maximum number of bytes which can be + * read/written are written to *len. After writing data to the buffer, call + * ring_buf_push/pop() with the number of bytes actually read/written, which + * must not exceed the returned length. + */ +static void ring_buf_head_tail(struct ring_buf *r, int idx, uint8_t **buf, size_t *len) +{ + size_t max_len = r->len - r->idx[idx]; + + if (idx == 0 && max_len > r->len - r->count) + max_len = r->len - r->count; + if (idx == 1 && max_len > r->count) + max_len = r->count; + + *buf = (uint8_t *)r->start + r->idx[idx]; + *len = max_len; +} + +#define ring_buf_head(r, buf, len) ring_buf_head_tail((r), 0, (buf), (len)) +#define ring_buf_tail(r, buf, len) ring_buf_head_tail((r), 1, (buf), (len)) + +/* + * Commit bytes to the ring buffer previously filled after a call to + * ring_buf_head(). + */ +static void ring_buf_push_pop(struct ring_buf *r, int idx, size_t num_bytes) +{ + size_t new_idx; + + /* A single push/pop op cannot wrap around, though it can reach the end. + * If the caller adheres to the convention of using the length returned + * by ring_buf_head/tail(), this cannot happen. + */ + if (!ossl_assert(num_bytes <= r->len - r->idx[idx])) + return; + + /* + * Must not overfill the buffer, or pop more than is in the buffer either. + */ + if (!ossl_assert(idx != 0 ? num_bytes <= r->count + : num_bytes + r->count <= r->len)) + return; + + /* Update the index. */ + new_idx = r->idx[idx] + num_bytes; + if (new_idx == r->len) + new_idx = 0; + + r->idx[idx] = new_idx; + if (idx != 0) + r->count -= num_bytes; + else + r->count += num_bytes; +} + +#define ring_buf_push(r, num_bytes) ring_buf_push_pop((r), 0, (num_bytes)) +#define ring_buf_pop(r, num_bytes) ring_buf_push_pop((r), 1, (num_bytes)) + +static void ring_buf_clear(struct ring_buf *r) +{ + r->idx[0] = r->idx[1] = r->count = 0; +} + +/* =========================================================================== + * BIO_s_dgram_pair is documented in BIO_s_dgram_pair(3). + * + * INTERNAL DATA STRUCTURE + * + * This is managed internally by using a bytewise ring buffer which supports + * pushing and popping spans of multiple bytes at once. The ring buffer stores + * internal packets which look like this: + * + * struct dgram_hdr hdr; + * uint8_t data[]; + * + * The header contains the length of the data and metadata such as + * source/destination addresses. + * + * The datagram pair BIO is designed to support both traditional + * BIO_read/BIO_write (likely to be used by applications) as well as + * BIO_recvmmsg/BIO_sendmmsg. + */ +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 long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr); +static int dgram_pair_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, + size_t *num_processed); +static int dgram_pair_recvmmsg(BIO *b, BIO_MSG *msg, size_t stride, + size_t num_msg, uint64_t flags, + size_t *num_processed); + +static int dgram_pair_ctrl_destroy_bio_pair(BIO *bio1); +static size_t dgram_pair_read_inner(struct bio_dgram_pair_st *b, uint8_t *buf, + size_t sz); + +#define BIO_MSG_N(array, n) (*(BIO_MSG *)((char *)(array) + (n)*stride)) + +static const BIO_METHOD dgram_pair_method = { + BIO_TYPE_DGRAM_PAIR, + "BIO dgram pair", + bwrite_conv, + dgram_pair_write, + bread_conv, + dgram_pair_read, + NULL, /* dgram_pair_puts */ + NULL, /* dgram_pair_gets */ + dgram_pair_ctrl, + dgram_pair_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; +} + +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. */ + BIO *peer; + /* Writes are directed to our own ringbuf and reads to our peer. */ + struct ring_buf rbuf; + /* Requested size of rbuf buffer in bytes once we initialize. */ + size_t req_buf_len; + /* Largest possible datagram size */ + size_t mtu; + /* Capability flags. */ + uint32_t cap; + /* + * This lock protects updates to our rbuf. Since writes are directed to our + * own rbuf, this means we use this lock for writes and our peer's lock for + * reads. + */ + CRYPTO_RWLOCK *lock; + 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 */ +}; + +#define MIN_BUF_LEN (1024) + +static int dgram_pair_init(BIO *bio) +{ + struct bio_dgram_pair_st *b = OPENSSL_zalloc(sizeof(*b)); + + if (b == NULL) + return 0; + + b->req_buf_len = 17*1024; /* default buffer size */ + b->mtu = 1472; /* conservative default MTU */ + + b->lock = CRYPTO_THREAD_lock_new(); + if (b->lock == NULL) { + OPENSSL_free(b); + return 0; + } + + bio->ptr = b; + return 1; +} + +static int dgram_pair_free(BIO *bio) +{ + struct bio_dgram_pair_st *b; + + if (bio == NULL) + return 0; + + b = bio->ptr; + if (!ossl_assert(b != NULL)) + return 0; + + /* We are being freed. Disconnect any peer and destroy buffers. */ + dgram_pair_ctrl_destroy_bio_pair(bio); + + CRYPTO_THREAD_lock_free(b->lock); + OPENSSL_free(b); + return 1; +} + +/* BIO_make_bio_pair (BIO_C_MAKE_BIO_PAIR) */ +static int dgram_pair_ctrl_make_bio_pair(BIO *bio1, BIO *bio2) +{ + struct bio_dgram_pair_st *b1, *b2; + + /* peer must be non-NULL. */ + if (bio1 == NULL || bio2 == NULL) { + ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT); + return 0; + } + + /* Ensure the BIO we have been passed is actually a dgram pair BIO. */ + if (bio1->method != &dgram_pair_method || bio2->method != &dgram_pair_method) { + ERR_raise_data(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT, + "both BIOs must be BIO_dgram_pair"); + return 0; + } + + b1 = bio1->ptr; + b2 = bio2->ptr; + + if (!ossl_assert(b1 != NULL && b2 != NULL)) { + ERR_raise(ERR_LIB_BIO, BIO_R_UNINITIALIZED); + return 0; + } + + /* + * This ctrl cannot be used to associate a BIO pair half which is already + * associated. + */ + if (b1->peer != NULL || b2->peer != NULL) { + ERR_raise_data(ERR_LIB_BIO, BIO_R_IN_USE, + "cannot associate a BIO_dgram_pair which is already in use"); + return 0; + } + + if (!ossl_assert(b1->req_buf_len >= MIN_BUF_LEN + && b2->req_buf_len >= MIN_BUF_LEN)) { + ERR_raise(ERR_LIB_BIO, BIO_R_UNINITIALIZED); + return 0; + } + + if (b1->rbuf.len != b1->req_buf_len) + if (ring_buf_init(&b1->rbuf, b1->req_buf_len) == 0) { + ERR_raise(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (b2->rbuf.len != b2->req_buf_len) + if (ring_buf_init(&b2->rbuf, b2->req_buf_len) == 0) { + ERR_raise(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE); + ring_buf_destroy(&b1->rbuf); + return 0; + } + + b1->peer = bio2; + b2->peer = bio1; + b1->role = 0; + b2->role = 1; + bio1->init = 1; + bio2->init = 1; + return 1; +} + +/* BIO_destroy_bio_pair (BIO_C_DESTROY_BIO_PAIR) */ +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. */ + if (b1->peer == NULL) + return 1; + + bio2 = b1->peer; + b2 = bio2->ptr; + + /* Invariants. */ + if (!ossl_assert(b1->peer == bio2 && 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; + return 1; +} + +/* BIO_eof (BIO_CTRL_EOF) */ +static int dgram_pair_ctrl_eof(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + + if (!ossl_assert(b != NULL)) + return -1; + + /* If we have no peer, we can never read anything */ + if (b->peer == NULL) + return 1; + + peerb = b->peer->ptr; + if (!ossl_assert(peerb != NULL)) + return -1; + + /* + * Since we are emulating datagram semantics, never indicate EOF so long as + * we have a peer. + */ + return 0; +} + +/* BIO_set_write_buf_size (BIO_C_SET_WRITE_BUF_SIZE) */ +static int dgram_pair_ctrl_set_write_buf_size(BIO *bio, size_t len) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + /* Changing buffer sizes is not permitted while a peer is connected. */ + if (b->peer != NULL) { + ERR_raise(ERR_LIB_BIO, BIO_R_IN_USE); + return 0; + } + + /* Enforce minimum size. */ + 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; + + b->req_buf_len = len; + return 1; +} + +/* BIO_reset (BIO_CTRL_RESET) */ +static int dgram_pair_ctrl_reset(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + ring_buf_clear(&b->rbuf); + return 1; +} + +/* BIO_pending (BIO_CTRL_PENDING) (Threadsafe) */ +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 dgram_hdr hdr; + size_t l; + + /* Safe to check; peer may not change during this call */ + if (b->peer == NULL) + return 0; + + peerb = b->peer->ptr; + + if (CRYPTO_THREAD_write_lock(peerb->lock) == 0) + return 0; + + saved_idx = peerb->rbuf.idx[1]; + saved_count = peerb->rbuf.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); + + if (!ossl_assert(l == 0 || l == sizeof(hdr))) + return 0; + + return l > 0 ? hdr.len : 0; +} + +/* BIO_get_write_guarantee (BIO_C_GET_WRITE_GUARANTEE) (Threadsafe) */ +static size_t dgram_pair_ctrl_get_write_guarantee(BIO *bio) +{ + size_t l; + struct bio_dgram_pair_st *b = bio->ptr; + + if (CRYPTO_THREAD_read_lock(b->lock) == 0) + return 0; + + l = b->rbuf.len - b->rbuf.count; + if (l >= sizeof(struct dgram_hdr)) + l -= sizeof(struct dgram_hdr); + + /* + * If the amount of buffer space would not be enough to accommodate the + * worst-case size of a datagram, report no space available. + */ + if (l < b->mtu) + l = 0; + + CRYPTO_THREAD_unlock(b->lock); + return l; +} + +/* 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; + + if (b->peer == NULL) + return 0; + + peerb = b->peer->ptr; + + return (~peerb->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) */ +static int dgram_pair_ctrl_get_effective_caps(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + + if (b->peer == NULL) + return 0; + + peerb = b->peer->ptr; + + return peerb->cap; +} + +/* BIO_dgram_get_caps (BIO_CTRL_DGRAM_GET_CAPS) */ +static uint32_t dgram_pair_ctrl_get_caps(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + return b->cap; +} + +/* BIO_dgram_set_caps (BIO_CTRL_DGRAM_SET_CAPS) */ +static int dgram_pair_ctrl_set_caps(BIO *bio, uint32_t caps) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + b->cap = caps; + return 1; +} + +/* BIO_dgram_get_local_addr_enable (BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE) */ +static int dgram_pair_ctrl_get_local_addr_enable(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + return b->local_addr_enable; +} + +/* BIO_dgram_set_local_addr_enable (BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE) */ +static int dgram_pair_ctrl_set_local_addr_enable(BIO *bio, int enable) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + if (dgram_pair_ctrl_get_local_addr_cap(bio) == 0) + return 0; + + b->local_addr_enable = (enable != 0 ? 1 : 0); + return 1; +} + +/* BIO_dgram_get_mtu (BIO_CTRL_DGRAM_GET_MTU) */ +static int dgram_pair_ctrl_get_mtu(BIO *bio) +{ + struct bio_dgram_pair_st *b = bio->ptr; + + return b->mtu; +} + +/* BIO_dgram_set_mtu (BIO_CTRL_DGRAM_SET_MTU) */ +static int dgram_pair_ctrl_set_mtu(BIO *bio, size_t mtu) +{ + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + + b->mtu = mtu; + + if (b->peer != NULL) { + peerb = b->peer->ptr; + peerb->mtu = mtu; + } + + return 1; +} + +/* Partially threadsafe (some commands) */ +static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + long ret = 1; + struct bio_dgram_pair_st *b = bio->ptr; + + if (!ossl_assert(b != NULL)) + return 0; + + switch (cmd) { + /* + * BIO_set_write_buf_size: Set the size of the ring buffer used for storing + * datagrams. No more writes can be performed once the buffer is filled up, + * until reads are performed. This cannot be used after a peer is connected. + */ + case BIO_C_SET_WRITE_BUF_SIZE: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_set_write_buf_size(bio, (size_t)num); + break; + + /* + * BIO_get_write_buf_size: Get ring buffer size. + */ + case BIO_C_GET_WRITE_BUF_SIZE: /* Non-threadsafe */ + 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. + */ + case BIO_CTRL_RESET: /* Non-threadsafe */ + dgram_pair_ctrl_reset(bio); + break; + + /* + * BIO_get_write_guarantee: Any BIO_write providing a buffer less than or + * equal to this value is guaranteed to succeed. + */ + case BIO_C_GET_WRITE_GUARANTEE: /* Threadsafe */ + ret = (long)dgram_pair_ctrl_get_write_guarantee(bio); + break; + + /* BIO_pending: Bytes available to read. */ + case BIO_CTRL_PENDING: /* Threadsafe */ + ret = (long)dgram_pair_ctrl_pending(bio); + break; + + /* BIO_flush: No-op. */ + case BIO_CTRL_FLUSH: /* Threadsafe */ + break; + + /* BIO_dgram_get_no_trunc */ + case BIO_CTRL_DGRAM_GET_NO_TRUNC: /* Non-threadsafe */ + ret = (long)b->no_trunc; + break; + + /* BIO_dgram_set_no_trunc */ + case BIO_CTRL_DGRAM_SET_NO_TRUNC: /* Non-threadsafe */ + b->no_trunc = (num > 0); + break; + + /* BIO_dgram_get_local_addr_enable */ + case BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_get_local_addr_enable(bio); + break; + + /* BIO_dgram_set_local_addr_enable */ + case BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_set_local_addr_enable(bio, num); + break; + + /* BIO_dgram_get_local_addr_cap: Can local addresses be supported? */ + case BIO_CTRL_DGRAM_GET_LOCAL_ADDR_CAP: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_get_local_addr_cap(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; + + /* BIO_dgram_get_caps */ + case BIO_CTRL_DGRAM_GET_CAPS: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_get_caps(bio); + break; + + /* BIO_dgram_set_caps */ + case BIO_CTRL_DGRAM_SET_CAPS: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_set_caps(bio, (uint32_t)num); + break; + + /* BIO_dgram_get_mtu */ + case BIO_CTRL_DGRAM_GET_MTU: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_get_mtu(bio); + break; + + /* BIO_dgram_set_mtu */ + case BIO_CTRL_DGRAM_SET_MTU: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_set_mtu(bio, (uint32_t)num); + break; + + /* + * BIO_eof: Returns whether this half of the BIO pair is empty of data to + * read. + */ + case BIO_CTRL_EOF: /* Non-threadsafe */ + ret = (long)dgram_pair_ctrl_eof(bio); + break; + + default: + ret = 0; + break; + } + + return ret; +} + +int BIO_new_bio_dgram_pair(BIO **pbio1, size_t writebuf1, + BIO **pbio2, size_t writebuf2) +{ + int ret = 0; + long r; + BIO *bio1 = NULL, *bio2 = NULL; + + bio1 = BIO_new(BIO_s_dgram_pair()); + if (bio1 == NULL) + goto err; + + bio2 = BIO_new(BIO_s_dgram_pair()); + if (bio2 == NULL) + goto err; + + if (writebuf1 > 0) { + r = BIO_set_write_buf_size(bio1, writebuf1); + if (r == 0) + goto err; + } + + if (writebuf2 > 0) { + r = BIO_set_write_buf_size(bio2, writebuf2); + if (r == 0) + goto err; + } + + r = BIO_make_bio_pair(bio1, bio2); + if (r == 0) + goto err; + + ret = 1; +err: + if (ret == 0) { + BIO_free(bio1); + bio1 = NULL; + BIO_free(bio2); + bio2 = NULL; + } + + *pbio1 = bio1; + *pbio2 = bio2; + return ret; +} + +/* Must hold peer write lock */ +static size_t dgram_pair_read_inner(struct bio_dgram_pair_st *b, uint8_t *buf, size_t sz) +{ + size_t total_read = 0; + + /* + * We repeat pops from the ring buffer for as long as we have more + * application *buffer to fill until we fail. We may not be able to pop + * enough data to fill the buffer in one operation if the ring buffer wraps + * around, but there may still be more data available. + */ + while (sz > 0) { + uint8_t *src_buf = NULL; + size_t src_len = 0; + + /* + * There are two BIO instances, each with a ringbuf. We read from the + * peer ringbuf and write to our own ringbuf. + */ + ring_buf_tail(&b->rbuf, &src_buf, &src_len); + if (src_len == 0) + break; + + if (src_len > sz) + src_len = sz; + + if (buf != NULL) + memcpy(buf, src_buf, src_len); + + ring_buf_pop(&b->rbuf, src_len); + + buf += src_len; + total_read += src_len; + sz -= src_len; + } + + return total_read; +} + +/* + * Must hold peer write lock. Returns number of bytes processed or negated BIO + * response code. + */ +static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz, + BIO_ADDR *local, BIO_ADDR *peer, + int is_multi) +{ + size_t l, trunc = 0, saved_idx, saved_count; + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + struct dgram_hdr hdr; + + if (!is_multi) + BIO_clear_retry_flags(bio); + + if (!bio->init) + return -BIO_R_UNINITIALIZED; + + if (!ossl_assert(b != NULL && b->peer != NULL)) + return -BIO_R_TRANSFER_ERROR; + + peerb = b->peer->ptr; + if (!ossl_assert(peerb != NULL && peerb->rbuf.start != NULL)) + return -BIO_R_TRANSFER_ERROR; + + if (sz > 0 && buf == NULL) + return -BIO_R_INVALID_ARGUMENT; + + /* If the caller wants to know the local address, it must be enabled */ + if (local != NULL && b->local_addr_enable == 0) + 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)); + if (l == 0) { + /* Buffer was empty. */ + if (!is_multi) + BIO_set_retry_read(bio); + return -BIO_R_NON_FATAL; + } + + if (!ossl_assert(l == sizeof(hdr))) + /* + * This should not be possible as headers (and their following payloads) + * should always be written atomically. + */ + return -BIO_R_BROKEN_PIPE; + + if (sz > hdr.len) { + sz = hdr.len; + } else if (sz < hdr.len) { + /* Truncation is occurring. */ + trunc = hdr.len - sz; + if (b->no_trunc) { + /* Restore original state. */ + peerb->rbuf.idx[1] = saved_idx; + peerb->rbuf.count = saved_count; + return -BIO_R_NON_FATAL; + } + } + + l = dgram_pair_read_inner(peerb, (uint8_t *)buf, sz); + if (!ossl_assert(l == sz)) + /* We were somehow not able to read the entire datagram. */ + return -BIO_R_TRANSFER_ERROR; + + /* + * 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)) + /* We were somehow not able to read/skip the entire datagram. */ + return -BIO_R_TRANSFER_ERROR; + + if (local != NULL) + *local = hdr.dst_addr; + if (peer != NULL) + *peer = hdr.src_addr; + + return (ossl_ssize_t)l; +} + +/* Threadsafe */ +static int dgram_pair_lock_both_write(struct bio_dgram_pair_st *a, + struct bio_dgram_pair_st *b) +{ + struct bio_dgram_pair_st *x, *y; + + x = (a->role == 1) ? a : b; + y = (a->role == 1) ? b : a; + + if (!ossl_assert(a->role != b->role)) + return 0; + + if (!ossl_assert(a != b && x != y)) + return 0; + + if (CRYPTO_THREAD_write_lock(x->lock) == 0) + return 0; + + if (CRYPTO_THREAD_write_lock(y->lock) == 0) { + CRYPTO_THREAD_unlock(x->lock); + return 0; + } + + return 1; +} + +static void dgram_pair_unlock_both(struct bio_dgram_pair_st *a, + struct bio_dgram_pair_st *b) +{ + CRYPTO_THREAD_unlock(a->lock); + CRYPTO_THREAD_unlock(b->lock); +} + +/* Threadsafe */ +static int dgram_pair_read(BIO *bio, char *buf, int sz_) +{ + int ret; + ossl_ssize_t l; + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + + if (sz_ < 0) { + ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT); + return -1; + } + + if (b->peer == NULL) { + ERR_raise(ERR_LIB_BIO, BIO_R_BROKEN_PIPE); + return -1; + } + + peerb = b->peer->ptr; + + /* + * For BIO_read we have to acquire both locks because we touch the retry + * flags on the local bio. (This is avoided in the recvmmsg case as it does + * not touch the retry flags.) + */ + if (dgram_pair_lock_both_write(peerb, b) == 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) { + ERR_raise(ERR_LIB_BIO, -l); + ret = -1; + } else { + ret = (int)l; + } + + dgram_pair_unlock_both(peerb, b); + return ret; +} + +/* Threadsafe */ +static int dgram_pair_recvmmsg(BIO *bio, BIO_MSG *msg, + size_t stride, size_t num_msg, + uint64_t flags, + size_t *num_processed) +{ + int ret; + ossl_ssize_t l; + BIO_MSG *m; + size_t i; + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + + if (num_msg == 0) { + *num_processed = 0; + return 1; + } + + if (b->peer == NULL) { + ERR_raise(ERR_LIB_BIO, BIO_R_BROKEN_PIPE); + *num_processed = 0; + return 0; + } + + peerb = b->peer->ptr; + + if (CRYPTO_THREAD_write_lock(peerb->lock) == 0) { + ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK); + *num_processed = 0; + return 0; + } + + for (i = 0; i < num_msg; ++i) { + m = &BIO_MSG_N(msg, i); + l = dgram_pair_read_actual(bio, m->data, m->data_len, + m->local, m->peer, 1); + if (l < 0) { + *num_processed = i; + if (i > 0) { + ret = 1; + } else { + ERR_raise(ERR_LIB_BIO, -l); + ret = 0; + } + goto out; + } + + m->data_len = l; + m->flags = 0; + } + + *num_processed = i; + ret = 1; +out: + CRYPTO_THREAD_unlock(peerb->lock); + return ret; +} + +/* 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) +{ + size_t total_written = 0; + + /* + * We repeat pushes to the ring buffer for as long as we have data until we + * fail. We may not be able to push in one operation if the ring buffer + * wraps around, but there may still be more room for data. + */ + while (sz > 0) { + size_t dst_len; + uint8_t *dst_buf; + + /* + * There are two BIO instances, each with a ringbuf. We write to our own + * ringbuf and read from the peer ringbuf. + */ + ring_buf_head(&b->rbuf, &dst_buf, &dst_len); + if (dst_len == 0) + break; + + if (dst_len > sz) + dst_len = sz; + + memcpy(dst_buf, buf, dst_len); + ring_buf_push(&b->rbuf, dst_len); + + buf += dst_len; + sz -= dst_len; + total_written += dst_len; + } + + return total_written; +} + +/* + * Must hold local write lock. Returns number of bytes processed or negated BIO + * response code. + */ +static ossl_ssize_t dgram_pair_write_actual(BIO *bio, const char *buf, size_t sz, + const BIO_ADDR *local, const BIO_ADDR *peer, + int is_multi) +{ + static const BIO_ADDR zero_addr = {0}; + size_t saved_idx, saved_count; + struct bio_dgram_pair_st *b = bio->ptr, *peerb; + struct dgram_hdr hdr = {0}; + + if (!is_multi) + BIO_clear_retry_flags(bio); + + if (!bio->init) + return -BIO_R_UNINITIALIZED; + + if (!ossl_assert(b != NULL && b->peer != NULL && b->rbuf.start != NULL)) + return -BIO_R_TRANSFER_ERROR; + + if (sz > 0 && buf == NULL) + return -BIO_R_INVALID_ARGUMENT; + + 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) + return -BIO_R_PEER_ADDR_NOT_AVAILABLE; + + hdr.len = sz; + hdr.dst_addr = (peer != NULL ? *peer : zero_addr); + hdr.src_addr = (local != NULL ? *local : zero_addr); + + saved_idx = b->rbuf.idx[0]; + saved_count = b->rbuf.count; + if (dgram_pair_write_inner(b, (const uint8_t *)&hdr, sizeof(hdr)) != sizeof(hdr) + || dgram_pair_write_inner(b, (const uint8_t *)buf, sz) != sz) { + /* + * We were not able to push the header and the entirety of the payload + * onto the ring buffer, so abort and roll back the ring buffer state. + */ + b->rbuf.idx[0] = saved_idx; + b->rbuf.count = saved_count; + if (!is_multi) + BIO_set_retry_write(bio); + return -BIO_R_NON_FATAL; + } + + return sz; +} + +/* Threadsafe */ +static int dgram_pair_write(BIO *bio, const 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_write_actual(bio, buf, (size_t)sz_, NULL, NULL, 0); + if (l < 0) { + ERR_raise(ERR_LIB_BIO, -l); + ret = -1; + } else { + ret = (int)l; + } + + CRYPTO_THREAD_unlock(b->lock); + return ret; +} + +/* Threadsafe */ +static int dgram_pair_sendmmsg(BIO *bio, BIO_MSG *msg, + size_t stride, size_t num_msg, + uint64_t flags, size_t *num_processed) +{ + ossl_ssize_t ret, l; + BIO_MSG *m; + size_t i; + struct bio_dgram_pair_st *b = bio->ptr; + + if (num_msg == 0) { + *num_processed = 0; + return 1; + } + + if (CRYPTO_THREAD_write_lock(b->lock) == 0) { + ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK); + *num_processed = 0; + return 0; + } + + for (i = 0; i < num_msg; ++i) { + m = &BIO_MSG_N(msg, i); + l = dgram_pair_write_actual(bio, m->data, m->data_len, + m->local, m->peer, 1); + if (l < 0) { + *num_processed = i; + if (i > 0) { + ret = 1; + } else { + ERR_raise(ERR_LIB_BIO, -l); + ret = 0; + } + goto out; + } + + m->flags = 0; + } + + *num_processed = i; + ret = 1; +out: + CRYPTO_THREAD_unlock(b->lock); + return ret; +} + +#endif diff --git a/crypto/bio/build.info b/crypto/bio/build.info index b203ed5e63..2102038c74 100644 --- a/crypto/bio/build.info +++ b/crypto/bio/build.info @@ -11,7 +11,7 @@ SOURCE[../../libcrypto]=\ SOURCE[../../libcrypto]=\ bss_null.c bss_mem.c bss_bio.c bss_fd.c bss_file.c \ bss_sock.c bss_conn.c bss_acpt.c bss_dgram.c \ - bss_log.c bss_core.c + bss_log.c bss_core.c bss_dgram_pair.c # Filters SOURCE[../../libcrypto]=\ diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 693b7bc960..dbb2daa350 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -146,12 +146,14 @@ BIO_R_LOCAL_ADDR_NOT_AVAILABLE:111:local addr not available BIO_R_LOOKUP_RETURNED_NOTHING:142:lookup returned nothing BIO_R_MALFORMED_HOST_OR_SERVICE:130:malformed host or service BIO_R_NBIO_CONNECT_ERROR:110:nbio connect error +BIO_R_NON_FATAL:112:non fatal BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED:143:\ no accept addr or service specified BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED:144:no hostname or service specified BIO_R_NO_PORT_DEFINED:113:no port defined BIO_R_NO_SUCH_FILE:128:no such file BIO_R_PORT_MISMATCH:150:port mismatch +BIO_R_PEER_ADDR_NOT_AVAILABLE:114:peer addr not available BIO_R_TFO_DISABLED:106:tfo disabled BIO_R_TFO_NO_KERNEL_SUPPORT:108:tfo no kernel support BIO_R_TRANSFER_ERROR:104:transfer error diff --git a/doc/build.info b/doc/build.info index 6b6e837838..b90ad11eae 100644 --- a/doc/build.info +++ b/doc/build.info @@ -651,6 +651,10 @@ DEPEND[html/man3/BIO_s_core.html]=man3/BIO_s_core.pod GENERATE[html/man3/BIO_s_core.html]=man3/BIO_s_core.pod DEPEND[man/man3/BIO_s_core.3]=man3/BIO_s_core.pod GENERATE[man/man3/BIO_s_core.3]=man3/BIO_s_core.pod +DEPEND[html/man3/BIO_s_dgram_pair.html]=man3/BIO_s_dgram_pair.pod +GENERATE[html/man3/BIO_s_dgram_pair.html]=man3/BIO_s_dgram_pair.pod +DEPEND[man/man3/BIO_s_dgram_pair.3]=man3/BIO_s_dgram_pair.pod +GENERATE[man/man3/BIO_s_dgram_pair.3]=man3/BIO_s_dgram_pair.pod DEPEND[html/man3/BIO_s_fd.html]=man3/BIO_s_fd.pod GENERATE[html/man3/BIO_s_fd.html]=man3/BIO_s_fd.pod DEPEND[man/man3/BIO_s_fd.3]=man3/BIO_s_fd.pod @@ -2914,6 +2918,7 @@ html/man3/BIO_s_accept.html \ html/man3/BIO_s_bio.html \ html/man3/BIO_s_connect.html \ html/man3/BIO_s_core.html \ +html/man3/BIO_s_dgram_pair.html \ html/man3/BIO_s_fd.html \ html/man3/BIO_s_file.html \ html/man3/BIO_s_mem.html \ @@ -3514,6 +3519,7 @@ man/man3/BIO_s_accept.3 \ man/man3/BIO_s_bio.3 \ man/man3/BIO_s_connect.3 \ man/man3/BIO_s_core.3 \ +man/man3/BIO_s_dgram_pair.3 \ man/man3/BIO_s_fd.3 \ man/man3/BIO_s_file.3 \ man/man3/BIO_s_mem.3 \ diff --git a/doc/man3/BIO_s_dgram_pair.pod b/doc/man3/BIO_s_dgram_pair.pod new file mode 100644 index 0000000000..b0f52af05d --- /dev/null +++ b/doc/man3/BIO_s_dgram_pair.pod @@ -0,0 +1,222 @@ +=pod + +=head1 NAME + +BIO_s_dgram_pair, BIO_new_bio_dgram_pair, BIO_dgram_set_no_trunc, +BIO_dgram_get_no_trunc, BIO_dgram_get_effective_caps, BIO_dgram_get_caps, +BIO_dgram_set_caps, BIO_dgram_set_mtu, BIO_dgram_get_mtu - datagram pair BIO + +=head1 SYNOPSIS + + #include + + const BIO_METHOD *BIO_s_dgram_pair(void); + + int BIO_new_bio_dgram_pair(BIO **bio1, size_t writebuf1, + BIO **bio2, size_t writebuf2); + int BIO_dgram_set_no_trunc(BIO *bio, int enable); + int BIO_dgram_get_no_trunc(BIO *bio); + uint32_t BIO_dgram_get_effective_caps(BIO *bio); + uint32_t BIO_dgram_get_caps(BIO *bio); + int BIO_dgram_set_caps(BIO *bio, uint32_t caps); + int BIO_dgram_set_mtu(BIO *bio, unsigned int mtu); + unsigned int BIO_dgram_get_mtu(BIO *bio); + +=head1 DESCRIPTION + +BIO_s_dgram_pair() returns the method for a BIO datagram pair. A BIO datagram +pair is similar to a BIO pair (see L) but has datagram semantics. +Broadly, this means that the length of the buffer passed to a write call will +match that retrieved by a read call. If the buffer passed to a read call is too +short, the datagram is truncated or the read fails, depending on how the BIO is +configured. + +The BIO datagram pair attaches certain metadata to each write, such as source +and destination addresses. This information may be retrieved on read. + +A typical application of a BIO datagram pair is to allow an application to keep +all datagram network I/O requested by libssl under application control. + +The BIO datagram pair is designed to support multithreaded use where certain +restrictions are observed; see THREADING. + +The BIO datagram pair allows each half of a pair to signal to the other half +whether they support certain capabilities; see CAPABILITY INDICATION. + +BIO_new_bio_dgram_pair() combines the calls to L, +L and L to create a connected +pair of BIOs B, B with write buffer sizes B and +B. If either size is zero then the default size is used. + +L may be used to join two datagram pair BIOs into a pair. +The two BIOs must both use the method returned by BIO_s_dgram_pair() and neither +of the BIOs may currently be associated in a pair. + +L destroys the association between two connected BIOs. +Freeing either half of the pair will automatically destroy the association. + +L clears any data in the write buffer of the given BIO. This means +that the opposite BIO in the pair will no longer have any data waiting to be +read. + +The BIO maintains a fixed size internal write buffer. When the buffer is full, +further writes will fail until the buffer is drained via calls to +L. The size of the buffer can be changed using +L and queried using L. + +Note that the write buffer is partially consumed by metadata stored internally +which is attached to each datagram, such as source and destination addresses. +The size of this overhead is undefined and may change between releases. + +The standard L call has modified behaviour and returns the +size of the next datagram waiting to be read in bytes. An application can use +this function to ensure it provides an adequate buffer to a subsequent read +call. If no datagram is waiting to be read, zero is returned. + +This BIO does not support sending or receiving zero-length datagrams. Passing a +zero-length buffer to BIO_write is treated as a no-op. + +L returns 1 only if the given BIO datagram pair BIO is not currently +connected to a peer BIO. + +L and L return how +large a datagram the next call to L can accept. If there is not +enough space in the write buffer to accept another datagram equal in size to the +configured MTU, zero is returned (see below). This is intended to avoid a +situation where an application attempts to read a datagram from a network +intending to write it to a BIO datagram pair, but where the received datagram +ends up being too large to write to the BIO datagram pair. + +BIO_dgram_set_no_trunc() and BIO_ctrl_get_no_trunc() set and retrieve the +truncation mode for the given half of a BIO datagram pair. When no-truncate mode +is enabled, BIO_read() will fail if the buffer provided is inadequate to hold +the next datagram to be read. If no-truncate mode is disabled (the default), the +datagram will be silently truncated. This default behaviour maintains +compatibility with the semantics of the Berkeley sockets API. + +BIO_dgram_set_mtu() and BIO_dgram_get_mtu() may be used to set an informational +MTU value on the BIO datagram pair. If BIO_dgram_set_mtu() is used on a BIO +which is currently part of a BIO datagram pair, the MTU value is set on both +halves of the pair. The value does not affect the operation of the BIO datagram +pair (except for BIO_get_write_guarantee(); see above) but may be used by other +code to determine a requested MTU. When a BIO datagram pair BIO is created, the +MTU is set to an unspecified but valid value. + +L is a no-op. + +=head1 NOTES + +The halves of a BIO datagram pair have independent lifetimes and must be +separately freed. + +=head1 THREADING + +L, L, L, L, +L, L and L may be used +by multiple threads simultaneously on the same BIO datagram pair. Specific +L operations (namely BIO_CTRL_PENDING, BIO_CTRL_FLUSH and +BIO_C_GET_WRITE_GUARANTEE) may also be used. Invoking any other BIO call, or any +other L operation, on either half of a BIO datagram pair while any +other BIO call is also in progress to either half of the same BIO datagram pair +results in undefined behaviour. + +=head1 CAPABILITY INDICATION + +The BIO datagram pair can be used to enqueue datagrams which have source and +destination addresses attached. It is important that the component consuming one +side of a BIO datagram pair understand whether the other side of the pair will +honour any source and destination addresses it attaches to each datagram. For +example, if datagrams are queued with destination addresses set but simply read +by simple calls to L, the destination addresses will be discarded. + +Each half of a BIO datagram pair can have capability flags set on it which +indicate whether source and destination addresses will be honoured by the reader +and whether they will be provided by the writer. These capability flags should +be set via a call to BIO_dgram_set_caps(), and these capabilities will be +reflected in the value returned by BIO_dgram_get_effective_caps() on the +opposite BIO. If necessary, the capability value previously set can be retrieved +using BIO_dgram_get_caps(). Note that BIO_dgram_set_caps() on a given BIO +controls the capabilities advertised to the peer, and +BIO_dgram_get_effective_caps() on a given BIO determines the capabilities +advertised by the peer of that BIO. + +The following capabilities are available: + +=over 4 + +=item B + +The user of the datagram pair BIO promises to honour source addresses provided +with datagrams written to the BIO pair. + +=item B + +The user of the datagram pair BIO promises to honour destination addresses provided +with datagrams written to the BIO pair. + +=item B + +The user of the datagram pair BIO advertises the fact that it will provide source +addressing information with future writes to the BIO pair, where available. + +=item B + +The user of the datagram pair BIO advertises the fact that it will provide +destination addressing information with future writes to the BIO pair, where +available. + +=back + +If a caller attempts to specify a destination address (for example, using +L) and the peer has not advertised the +B capability, the operation fails. Thus, +capability negotiation is mandatory. + +If a caller attempts to specify a source address when writing, or requests a +destination address when receiving, and local address support has not been +enabled, the operation fails; see L. + +If a caller attempts to enable local address support using +L and L +does not return 1 (meaning that the peer has not advertised both the +B and the B +capability), the operation fails. + +B and B +indicate that the application using that half of a BIO datagram pair promises to +provide source and destination addresses respectively when writing datagrams to +that half of the BIO datagram pair. However, these capability flags do not +affect the behaviour of the BIO datagram pair. + +=head1 RETURN VALUES + +BIO_new_bio_dgram_pair() returns 1 on success, with the new BIOs available in +B and B, or 0 on failure, with NULL pointers stored into the +locations for B and B. Check the error stack for more information. + +BIO_dgram_set_no_trunc(), BIO_dgram_set_caps() and BIO_dgram_set_mtu() return 1 +on success and 0 on failure. + +BIO_dgram_get_no_trunc() returns 1 if no-truncate mode is enabled on a BIO, or 0 +if no-truncate mode is not enabled or not supported on a given BIO. + +BIO_dgram_get_effective_caps() and BIO_dgram_get_caps() return zero if no +capabilities are supported. + +BIO_dgram_get_mtu() returns the MTU value configured on the BIO, or zero if the +operation is not supported. + +=head1 SEE ALSO + +L, L + +=head1 COPYRIGHT + +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 +L. + +=cut diff --git a/doc/man3/BIO_sendmmsg.pod b/doc/man3/BIO_sendmmsg.pod index 6940847e22..c8789ba0ad 100644 --- a/doc/man3/BIO_sendmmsg.pod +++ b/doc/man3/BIO_sendmmsg.pod @@ -158,6 +158,11 @@ particular may be noted: The I field was set to a non-NULL value, but local address support is not available or not enabled on the BIO. +=item B + +The I field was set to a non-NULL value, but peer address support is not +available on the BIO. + =item B The BIO_sendmmsg() or BIO_recvmmsg() method is not supported on the BIO. diff --git a/include/openssl/bio.h.in b/include/openssl/bio.h.in index 2bbfc75714..e413bb41cb 100644 --- a/include/openssl/bio.h.in +++ b/include/openssl/bio.h.in @@ -68,6 +68,7 @@ extern "C" { # define BIO_TYPE_DGRAM_SCTP (24|BIO_TYPE_SOURCE_SINK|BIO_TYPE_DESCRIPTOR) # 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_START 128 @@ -175,6 +176,17 @@ extern "C" { # define BIO_CTRL_DGRAM_GET_LOCAL_ADDR_CAP 82 # define BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE 83 # define BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE 84 +# define BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS 85 +# define BIO_CTRL_DGRAM_GET_CAPS 86 +# define BIO_CTRL_DGRAM_SET_CAPS 87 +# define BIO_CTRL_DGRAM_GET_NO_TRUNC 88 +# define BIO_CTRL_DGRAM_SET_NO_TRUNC 89 + +# define BIO_DGRAM_CAP_NONE 0U +# define BIO_DGRAM_CAP_HANDLES_SRC_ADDR (1U << 0) +# define BIO_DGRAM_CAP_HANDLES_DST_ADDR (1U << 1) +# define BIO_DGRAM_CAP_PROVIDES_SRC_ADDR (1U << 2) +# define BIO_DGRAM_CAP_PROVIDES_DST_ADDR (1U << 3) # ifndef OPENSSL_NO_KTLS # define BIO_get_ktls_send(b) \ @@ -607,6 +619,20 @@ int BIO_ctrl_reset_read_request(BIO *b); (int)BIO_ctrl((b), BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE, 0, (char *)(penable)) # define BIO_dgram_set_local_addr_enable(b, enable) \ (int)BIO_ctrl((b), BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE, (enable), NULL) +# define BIO_dgram_get_effective_caps(b) \ + (uint32_t)BIO_ctrl((b), BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS, 0, NULL) +# define BIO_dgram_get_caps(b) \ + (uint32_t)BIO_ctrl((b), BIO_CTRL_DGRAM_GET_CAPS, 0, NULL) +# define BIO_dgram_set_caps(b, caps) \ + (int)BIO_ctrl((b), BIO_CTRL_DGRAM_SET_CAPS, (long)(caps), NULL) +# define BIO_dgram_get_no_trunc(b) \ + (unsigned int)BIO_ctrl((b), BIO_CTRL_DGRAM_GET_NO_TRUNC, 0, NULL) +# define BIO_dgram_set_no_trunc(b, enable) \ + (int)BIO_ctrl((b), BIO_CTRL_DGRAM_SET_NO_TRUNC, (enable), NULL) +# define BIO_dgram_get_mtu(b) \ + (unsigned int)BIO_ctrl((b), BIO_CTRL_DGRAM_GET_MTU, 0, NULL) +# define BIO_dgram_set_mtu(b, mtu) \ + (int)BIO_ctrl((b), BIO_CTRL_DGRAM_SET_MTU, (mtu), NULL) /* ctrl macros for BIO_f_prefix */ # define BIO_set_prefix(b,p) BIO_ctrl((b), BIO_CTRL_SET_PREFIX, 0, (void *)(p)) @@ -702,6 +728,7 @@ const BIO_METHOD *BIO_f_nbio_test(void); const BIO_METHOD *BIO_f_prefix(void); const BIO_METHOD *BIO_s_core(void); # ifndef OPENSSL_NO_DGRAM +const BIO_METHOD *BIO_s_dgram_pair(void); const BIO_METHOD *BIO_s_datagram(void); int BIO_dgram_non_fatal_error(int error); BIO *BIO_new_dgram(int fd, int close_flag); @@ -824,6 +851,11 @@ BIO *BIO_new_fd(int fd, int close_flag); int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2); +# ifndef OPENSSL_NO_DGRAM +int BIO_new_bio_dgram_pair(BIO **bio1, size_t writebuf1, + BIO **bio2, size_t writebuf2); +# endif + /* * If successful, returns 1 and in *bio1, *bio2 two BIO pair endpoints. * Otherwise returns 0 and sets *bio1 and *bio2 to NULL. Size 0 uses default diff --git a/include/openssl/bioerr.h b/include/openssl/bioerr.h index 72b0b1ffa5..e4fdb64974 100644 --- a/include/openssl/bioerr.h +++ b/include/openssl/bioerr.h @@ -41,10 +41,12 @@ # define BIO_R_LOOKUP_RETURNED_NOTHING 142 # define BIO_R_MALFORMED_HOST_OR_SERVICE 130 # define BIO_R_NBIO_CONNECT_ERROR 110 +# define BIO_R_NON_FATAL 112 # define BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED 143 # define BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED 144 # define BIO_R_NO_PORT_DEFINED 113 # define BIO_R_NO_SUCH_FILE 128 +# define BIO_R_NULL_PARAMETER 115 /* unused */ # define BIO_R_TFO_DISABLED 106 # define BIO_R_TFO_NO_KERNEL_SUPPORT 108 # define BIO_R_TRANSFER_ERROR 104 @@ -64,7 +66,7 @@ # define BIO_R_UNSUPPORTED_PROTOCOL_FAMILY 131 # define BIO_R_WRITE_TO_READ_ONLY_BIO 126 # define BIO_R_WSASTARTUP 122 -# define BIO_R_NON_FATAL 149 # define BIO_R_PORT_MISMATCH 150 +# define BIO_R_PEER_ADDR_NOT_AVAILABLE 151 #endif diff --git a/test/bio_dgram_test.c b/test/bio_dgram_test.c index 7f147283b9..4dd2893c1b 100644 --- a/test/bio_dgram_test.c +++ b/test/bio_dgram_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * 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 @@ -9,6 +9,7 @@ #include #include +#include #include "testutil.h" #include "internal/sockets.h" @@ -450,6 +451,299 @@ static int test_bio_dgram(int idx) bio_dgram_cases[idx].local); } +static int random_data(const uint32_t *key, uint8_t *data, size_t data_len, size_t offset) +{ + int ret = 0, outl; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *cipher = NULL; + static const uint8_t zeroes[2048]; + uint32_t counter[4] = {0}; + + counter[0] = (uint32_t)offset; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + goto err; + + cipher = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL); + if (cipher == NULL) + goto err; + + if (EVP_EncryptInit_ex2(ctx, cipher, (uint8_t *)key, (uint8_t *)counter, NULL) == 0) + goto err; + + while (data_len > 0) { + outl = data_len > sizeof(zeroes) ? (int)sizeof(zeroes) : (int)data_len; + if (EVP_EncryptUpdate(ctx, data, &outl, zeroes, outl) != 1) + goto err; + + data += outl; + data_len -= outl; + } + + ret = 1; +err: + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + return ret; +} + +static int test_bio_dgram_pair(void) +{ + int testresult = 0, blen, mtu1, mtu2, r; + BIO *bio1 = NULL, *bio2 = NULL; + uint8_t scratch[2048 + 4], scratch2[2048]; + uint32_t key[8]; + size_t i, num_dgram, num_processed = 0; + BIO_MSG msgs[2] = {0}, rmsgs[2] = {0}; + BIO_ADDR *addr1 = NULL, *addr2 = NULL, *addr3 = NULL, *addr4 = NULL; + struct in_addr in_local; + size_t total = 0; + const uint32_t ref_caps = BIO_DGRAM_CAP_HANDLES_SRC_ADDR + | BIO_DGRAM_CAP_HANDLES_DST_ADDR + | BIO_DGRAM_CAP_PROVIDES_SRC_ADDR + | BIO_DGRAM_CAP_PROVIDES_DST_ADDR; + + in_local.s_addr = ntohl(0x7f000001); + + 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; + + mtu1 = BIO_dgram_get_mtu(bio1); + if (!TEST_int_ge(mtu1, 1280)) + goto err; + + mtu2 = BIO_dgram_get_mtu(bio2); + if (!TEST_int_ge(mtu2, 1280)) + goto err; + + if (!TEST_int_eq(mtu1, mtu2)) + goto err; + + if (!TEST_int_le(mtu1, sizeof(scratch) - 4)) + goto err; + + for (i = 0;; ++i) { + if (!TEST_int_eq(random_data(key, scratch, sizeof(scratch), i), 1)) + goto err; + + blen = (*(uint32_t*)scratch) % mtu1; + r = BIO_write(bio1, scratch + 4, blen); + if (r == -1) + break; + + if (!TEST_int_eq(r, blen)) + goto err; + + total += blen; + if (!TEST_size_t_lt(total, 1 * 1024 * 1024)) + goto err; + } + + /* + * Should be able to fit at least 9 datagrams in default write buffer size + * in worst case + */ + if (!TEST_int_ge(i, 9)) + goto err; + + /* Check we read back the same data */ + num_dgram = i; + for (i = 0; i < num_dgram; ++i) { + if (!TEST_int_eq(random_data(key, scratch, sizeof(scratch), i), 1)) + goto err; + + blen = (*(uint32_t*)scratch) % mtu1; + r = BIO_read(bio2, scratch2, sizeof(scratch2)); + if (!TEST_int_eq(r, blen)) + goto err; + + if (!TEST_mem_eq(scratch + 4, blen, scratch2, blen)) + goto err; + } + + /* Should now be out of data */ + if (!TEST_int_eq(BIO_read(bio2, scratch2, sizeof(scratch2)), -1)) + goto err; + + /* sendmmsg/recvmmsg */ + if (!TEST_int_eq(random_data(key, scratch, sizeof(scratch), 0), 1)) + goto err; + + msgs[0].data = scratch; + msgs[0].data_len = 19; + msgs[1].data = scratch + 19; + msgs[1].data_len = 46; + + if (!TEST_true(BIO_sendmmsg(bio1, msgs, sizeof(BIO_MSG), OSSL_NELEM(msgs), 0, + &num_processed)) + || !TEST_size_t_eq(num_processed, 2)) + goto err; + + rmsgs[0].data = scratch2; + rmsgs[0].data_len = 64; + rmsgs[1].data = scratch2 + 64; + rmsgs[1].data_len = 64; + if (!TEST_true(BIO_recvmmsg(bio2, rmsgs, sizeof(BIO_MSG), OSSL_NELEM(rmsgs), 0, + &num_processed)) + || !TEST_size_t_eq(num_processed, 2)) + goto err; + + if (!TEST_mem_eq(rmsgs[0].data, rmsgs[0].data_len, scratch, 19)) + goto err; + + if (!TEST_mem_eq(rmsgs[1].data, rmsgs[1].data_len, scratch + 19, 46)) + goto err; + + /* sendmmsg/recvmmsg with peer */ + addr1 = BIO_ADDR_new(); + if (!TEST_ptr(addr1)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_rawmake(addr1, AF_INET, &in_local, + sizeof(in_local), 1234), 1)) + goto err; + + addr2 = BIO_ADDR_new(); + if (!TEST_ptr(addr2)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_rawmake(addr2, AF_INET, &in_local, + sizeof(in_local), 2345), 1)) + goto err; + + addr3 = BIO_ADDR_new(); + if (!TEST_ptr(addr3)) + goto err; + + addr4 = BIO_ADDR_new(); + if (!TEST_ptr(addr4)) + goto err; + + 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)) + || !TEST_size_t_eq(num_processed, 0)) + goto err; + + if (!TEST_int_eq(BIO_dgram_set_caps(bio2, ref_caps), 1)) + goto err; + + if (!TEST_int_eq(BIO_dgram_get_caps(bio2), ref_caps)) + goto err; + + 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)) + goto err; + + if (!TEST_int_eq(BIO_dgram_set_caps(bio1, ref_caps), 1)) + goto err; + + /* succeeds with cap now available */ + if (!TEST_true(BIO_sendmmsg(bio1, msgs, sizeof(BIO_MSG), 1, 0, &num_processed)) + || !TEST_size_t_eq(num_processed, 1)) + goto err; + + /* enable local addr support */ + if (!TEST_int_eq(BIO_dgram_set_local_addr_enable(bio2, 1), 1)) + goto err; + + rmsgs[0].data = scratch2; + rmsgs[0].data_len = 64; + rmsgs[0].peer = addr3; + rmsgs[0].local = addr4; + if (!TEST_true(BIO_recvmmsg(bio2, rmsgs, sizeof(BIO_MSG), OSSL_NELEM(rmsgs), 0, + &num_processed)) + || !TEST_size_t_eq(num_processed, 1)) + goto err; + + if (!TEST_mem_eq(rmsgs[0].data, rmsgs[0].data_len, msgs[0].data, 19)) + goto err; + + /* We didn't set the source address so this should be zero */ + if (!TEST_int_eq(BIO_ADDR_family(addr3), 0)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_family(addr4), AF_INET)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_rawport(addr4), 1234)) + goto err; + + /* test source address */ + msgs[0].local = addr2; + + if (!TEST_int_eq(BIO_dgram_set_local_addr_enable(bio1, 1), 1)) + goto err; + + if (!TEST_true(BIO_sendmmsg(bio1, msgs, sizeof(BIO_MSG), 1, 0, &num_processed)) + || !TEST_size_t_eq(num_processed, 1)) + goto err; + + rmsgs[0].data = scratch2; + rmsgs[0].data_len = 64; + if (!TEST_true(BIO_recvmmsg(bio2, rmsgs, sizeof(BIO_MSG), OSSL_NELEM(rmsgs), 0, &num_processed)) + || !TEST_size_t_eq(num_processed, 1)) + goto err; + + if (!TEST_mem_eq(rmsgs[0].data, rmsgs[0].data_len, + msgs[0].data, msgs[0].data_len)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_family(addr3), AF_INET)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_rawport(addr3), 2345)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_family(addr4), AF_INET)) + goto err; + + if (!TEST_int_eq(BIO_ADDR_rawport(addr4), 1234)) + goto err; + + /* test truncation, pending */ + r = BIO_write(bio1, scratch, 64); + if (!TEST_int_eq(r, 64)) + goto err; + + memset(scratch2, 0, 64); + if (!TEST_int_eq(BIO_dgram_set_no_trunc(bio2, 1), 1)) + goto err; + + if (!TEST_int_eq(BIO_read(bio2, scratch2, 32), -1)) + goto err; + + if (!TEST_int_eq(BIO_pending(bio2), 64)) + goto err; + + if (!TEST_int_eq(BIO_dgram_set_no_trunc(bio2, 0), 1)) + goto err; + + if (!TEST_int_eq(BIO_read(bio2, scratch2, 32), 32)) + goto err; + + if (!TEST_mem_eq(scratch, 32, scratch2, 32)) + goto err; + + testresult = 1; +err: + BIO_free(bio1); + BIO_free(bio2); + BIO_ADDR_free(addr1); + BIO_ADDR_free(addr2); + BIO_ADDR_free(addr3); + BIO_ADDR_free(addr4); + return testresult; +} + #endif /* !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK) */ int setup_tests(void) @@ -461,6 +755,8 @@ int setup_tests(void) #if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK) ADD_ALL_TESTS(test_bio_dgram, OSSL_NELEM(bio_dgram_cases)); + ADD_TEST(test_bio_dgram_pair); #endif + return 1; } diff --git a/test/build.info b/test/build.info index 661076c2f8..69c4f12c57 100644 --- a/test/build.info +++ b/test/build.info @@ -63,7 +63,7 @@ IF[{- !$disabled{tests} -}] keymgmt_internal_test hexstr_test provider_status_test defltfips_test \ bio_readbuffer_test user_property_test pkcs7_test upcallstest \ provfetchtest prov_config_test rand_test ca_internals_test \ - bio_tfo_test membio_test list_test fips_version_test + bio_tfo_test membio_test bio_dgram_test list_test fips_version_test IF[{- !$disabled{'deprecated-3.0'} -}] PROGRAMS{noinst}=enginetest @@ -404,6 +404,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[membio_test]=../include ../apps/include .. DEPEND[membio_test]=../libcrypto libtestutil.a + SOURCE[bio_dgram_test]=bio_dgram_test.c + INCLUDE[bio_dgram_test]=../include ../apps/include .. + DEPEND[bio_dgram_test]=../libcrypto libtestutil.a + SOURCE[params_api_test]=params_api_test.c INCLUDE[params_api_test]=../include ../apps/include DEPEND[params_api_test]=../libcrypto libtestutil.a diff --git a/test/threadstest.c b/test/threadstest.c index 250b12d037..505b2cf07e 100644 --- a/test/threadstest.c +++ b/test/threadstest.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "internal/tsan_assist.h" #include "internal/nelem.h" #include "testutil.h" @@ -44,6 +45,8 @@ static const char *default_provider[] = { "default", NULL }; static const char *fips_provider[] = { "fips", NULL }; static const char *fips_and_default_providers[] = { "default", "fips", NULL }; +static CRYPTO_RWLOCK *global_lock; + #ifdef TSAN_REQUIRES_LOCKING static CRYPTO_RWLOCK *tsan_lock; #endif @@ -266,6 +269,19 @@ static void multi_intialise(void) memset(multi_provider, 0, sizeof(multi_provider)); } +static void multi_set_success(int ok) +{ + if (CRYPTO_THREAD_write_lock(global_lock) == 0) { + /* not synchronized, but better than not reporting failure */ + multi_success = ok; + return; + } + + multi_success = ok; + + CRYPTO_THREAD_unlock(global_lock); +} + static void thead_teardown_libctx(void) { OSSL_PROVIDER **p; @@ -407,7 +423,7 @@ static void thread_general_worker(void) EVP_CIPHER_free(ciph); EVP_PKEY_free(pkey); if (!testresult) - multi_success = 0; + multi_set_success(0); } static void thread_multi_simple_fetch(void) @@ -417,7 +433,7 @@ static void thread_multi_simple_fetch(void) if (md != NULL) EVP_MD_free(md); else - multi_success = 0; + multi_set_success(0); } static EVP_PKEY *shared_evp_pkey = NULL; @@ -466,7 +482,7 @@ static void thread_shared_evp_pkey(void) err: EVP_PKEY_CTX_free(ctx); if (!success) - multi_success = 0; + multi_set_success(0); } static void thread_provider_load_unload(void) @@ -475,7 +491,7 @@ static void thread_provider_load_unload(void) if (!TEST_ptr(deflt) || !TEST_true(OSSL_PROVIDER_available(multi_libctx, "default"))) - multi_success = 0; + multi_set_success(0); OSSL_PROVIDER_unload(deflt); } @@ -532,7 +548,7 @@ static void thread_downgrade_shared_evp_pkey(void) * downgrading */ if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL) - multi_success = 0; + multi_set_success(0); } static int test_multi_downgrade_shared_pkey(void) @@ -588,7 +604,7 @@ static void test_multi_load_worker(void) if (!TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, multi_load_provider)) || !TEST_true(OSSL_PROVIDER_unload(prov))) - multi_success = 0; + multi_set_success(0); } static int test_multi_default(void) @@ -644,7 +660,7 @@ static void test_obj_create_one(void) if (!TEST_int_ne(id, 0) || !TEST_true(id = OBJ_create(oid, sn, ln)) || !TEST_true(OBJ_add_sigid(id, NID_sha3_256, NID_rsa))) - multi_success = 0; + multi_set_success(0); } static int test_obj_add(void) @@ -657,7 +673,7 @@ static int test_obj_add(void) static void test_lib_ctx_load_config_worker(void) { if (!TEST_int_eq(OSSL_LIB_CTX_load_config(multi_libctx, config_file), 1)) - multi_success = 0; + multi_set_success(0); } static int test_lib_ctx_load_config(void) @@ -667,6 +683,64 @@ static int test_lib_ctx_load_config(void) 1, default_provider); } +#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK) +static BIO *multi_bio1, *multi_bio2; + +static void test_bio_dgram_pair_worker(void) +{ + ossl_unused int r; + int ok = 0; + uint8_t ch = 0; + uint8_t scratch[64]; + BIO_MSG msg = {0}; + size_t num_processed = 0; + + if (!TEST_int_eq(RAND_bytes_ex(multi_libctx, &ch, 1, 64), 1)) + goto err; + + msg.data = scratch; + msg.data_len = sizeof(scratch); + + /* + * We do not test for failure here as recvmmsg may fail if no sendmmsg + * has been called yet. The purpose of this code is to exercise tsan. + */ + if (ch & 2) + r = BIO_sendmmsg(ch & 1 ? multi_bio2 : multi_bio1, &msg, + sizeof(BIO_MSG), 1, 0, &num_processed); + else + r = BIO_recvmmsg(ch & 1 ? multi_bio2 : multi_bio1, &msg, + sizeof(BIO_MSG), 1, 0, &num_processed); + + ok = 1; +err: + if (ok == 0) + multi_set_success(0); +} + +static int test_bio_dgram_pair(void) +{ + int r; + BIO *bio1 = NULL, *bio2 = NULL; + + r = BIO_new_bio_dgram_pair(&bio1, 0, &bio2, 0); + if (!TEST_int_eq(r, 1)) + goto err; + + multi_bio1 = bio1; + multi_bio2 = bio2; + + r = thread_run_test(&test_bio_dgram_pair_worker, + MAXIMUM_THREADS, &test_bio_dgram_pair_worker, + 1, default_provider); + +err: + BIO_free(bio1); + BIO_free(bio2); + return r; +} +#endif + typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, @@ -713,6 +787,9 @@ int setup_tests(void) if (!TEST_ptr(privkey)) return 0; + if (!TEST_ptr(global_lock = CRYPTO_THREAD_lock_new())) + return 0; + #ifdef TSAN_REQUIRES_LOCKING if (!TEST_ptr(tsan_lock = CRYPTO_THREAD_lock_new())) return 0; @@ -736,6 +813,9 @@ int setup_tests(void) ADD_TEST(test_multi_load_unload_provider); ADD_TEST(test_obj_add); ADD_TEST(test_lib_ctx_load_config); +#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK) + ADD_TEST(test_bio_dgram_pair); +#endif return 1; } @@ -745,4 +825,5 @@ void cleanup_tests(void) #ifdef TSAN_REQUIRES_LOCKING CRYPTO_THREAD_lock_free(tsan_lock); #endif + CRYPTO_THREAD_lock_free(global_lock); } diff --git a/util/libcrypto.num b/util/libcrypto.num index 82e937f60a..50be3df6de 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5459,3 +5459,5 @@ BIO_err_is_non_fatal ? 3_1_0 EXIST::FUNCTION:SOCK X509_get_default_cert_uri ? 3_1_0 EXIST::FUNCTION: X509_get_default_cert_uri_env ? 3_1_0 EXIST::FUNCTION: X509_get_default_cert_path_env ? 3_1_0 EXIST::FUNCTION: +BIO_s_dgram_pair ? 3_1_0 EXIST::FUNCTION:DGRAM +BIO_new_bio_dgram_pair ? 3_1_0 EXIST::FUNCTION:DGRAM diff --git a/util/other.syms b/util/other.syms index 77e2a9e0c6..36ce26ee36 100644 --- a/util/other.syms +++ b/util/other.syms @@ -148,6 +148,13 @@ BIO_destroy_bio_pair define BIO_dgram_get_local_addr_cap define BIO_dgram_get_local_addr_enable define BIO_dgram_set_local_addr_enable define +BIO_dgram_set_no_trunc define +BIO_dgram_get_no_trunc define +BIO_dgram_get_caps define +BIO_dgram_set_caps define +BIO_dgram_get_effective_caps define +BIO_dgram_get_mtu define +BIO_dgram_set_mtu define BIO_do_accept define BIO_do_connect define BIO_do_handshake define