{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),
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+#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
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]=\
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
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
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 \
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 \
--- /dev/null
+=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 <openssl/bio.h>
+
+ 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<BIO_s_bio(3)>) 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<BIO_new(3)>,
+L<BIO_make_bio_pair(3)> and L<BIO_set_write_buf_size(3)> to create a connected
+pair of BIOs B<bio1>, B<bio2> with write buffer sizes B<writebuf1> and
+B<writebuf2>. If either size is zero then the default size is used.
+
+L<BIO_make_bio_pair(3)> 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<BIO_destroy_bio_pair(3)> destroys the association between two connected BIOs.
+Freeing either half of the pair will automatically destroy the association.
+
+L<BIO_reset(3)> 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<BIO_read(3)>. The size of the buffer can be changed using
+L<BIO_set_write_buf_size(3)> and queried using L<BIO_get_write_buf_size(3)>.
+
+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<BIO_ctrl_pending(3)> 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<BIO_eof(3)> returns 1 only if the given BIO datagram pair BIO is not currently
+connected to a peer BIO.
+
+L<BIO_get_write_guarantee(3)> and L<BIO_ctrl_get_write_guarantee(3)> return how
+large a datagram the next call to L<BIO_write(3)> 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<BIO_flush(3)> is a no-op.
+
+=head1 NOTES
+
+The halves of a BIO datagram pair have independent lifetimes and must be
+separately freed.
+
+=head1 THREADING
+
+L<BIO_recvmmsg(3)>, L<BIO_sendmmsg(3)>, L<BIO_read(3)>, L<BIO_write(3)>,
+L<BIO_pending(3)>, L<BIO_get_write_guarantee(3)> and L<BIO_flush(3)> may be used
+by multiple threads simultaneously on the same BIO datagram pair. Specific
+L<BIO_ctrl(3)> 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<BIO_ctrl(3)> 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<BIO_read(3)>, 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<BIO_DGRAM_CAP_HANDLES_SRC_ADDR>
+
+The user of the datagram pair BIO promises to honour source addresses provided
+with datagrams written to the BIO pair.
+
+=item B<BIO_DGRAM_CAP_HANDLES_DST_ADDR>
+
+The user of the datagram pair BIO promises to honour destination addresses provided
+with datagrams written to the BIO pair.
+
+=item B<BIO_DGRAM_CAP_PROVIDES_SRC_ADDR>
+
+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<BIO_DGRAM_CAP_PROVIDES_DST_ADDR>
+
+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<BIO_sendmmsg(3)>) and the peer has not advertised the
+B<BIO_DGRAM_CAP_HANDLES_DST_ADDR> 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<BIO_dgram_set_local_addr_enable(3)>.
+
+If a caller attempts to enable local address support using
+L<BIO_dgram_set_local_addr_enable(3)> and L<BIO_dgram_get_local_addr_cap(3)>
+does not return 1 (meaning that the peer has not advertised both the
+B<BIO_DGRAM_CAP_HANDLES_SRC_ADDR> and the B<BIO_DGRAM_CAP_PROVIDES_DST_ADDR>
+capability), the operation fails.
+
+B<BIO_DGRAM_CAP_PROVIDES_SRC_ADDR> and B<BIO_DGRAM_CAP_PROVIDES_DST_ADDR>
+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<bio1> and B<bio2>, or 0 on failure, with NULL pointers stored into the
+locations for B<bio1> and B<bio2>. 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<BIO_s_bio(3)>, L<bio(7)>
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
The I<local> field was set to a non-NULL value, but local address support is not
available or not enabled on the BIO.
+=item B<BIO_R_PEER_ADDR_NOT_AVAILABLE>
+
+The I<peer> field was set to a non-NULL value, but peer address support is not
+available on the BIO.
+
=item B<BIO_R_UNSUPPORTED_METHOD>
The BIO_sendmmsg() or BIO_recvmmsg() method is not supported on the BIO.
# 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
# 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) \
(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))
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);
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
# 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
# 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
/*
- * 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
#include <string.h>
#include <openssl/bio.h>
+#include <openssl/rand.h>
#include "testutil.h"
#include "internal/sockets.h"
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)
#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;
}
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
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
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
+#include <openssl/rand.h>
#include "internal/tsan_assist.h"
#include "internal/nelem.h"
#include "testutil.h"
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
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;
EVP_CIPHER_free(ciph);
EVP_PKEY_free(pkey);
if (!testresult)
- multi_success = 0;
+ multi_set_success(0);
}
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;
err:
EVP_PKEY_CTX_free(ctx);
if (!success)
- multi_success = 0;
+ multi_set_success(0);
}
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);
}
* 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)
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)
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)
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)
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,
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;
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;
}
#ifdef TSAN_REQUIRES_LOCKING
CRYPTO_THREAD_lock_free(tsan_lock);
#endif
+ CRYPTO_THREAD_lock_free(global_lock);
}
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
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