123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137 |
- /*
- * 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_BIO_LIB);
- 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_BIO_LIB);
- 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);
- if (buf != NULL)
- 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;
- 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
|