BIO_S_DGRAM_PAIR(3ossl)
π BIO_s_dgram_pair β datagram pair BIO, but make it a whole vibe (3ossl)
okay bestie, gather round, because this is the OpenSSL 3ossl manual page for the datagram pair BIO family, and honestly? it's giving networking with boundaries β¨
πΈ 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, BIO_dgram_set0_local_addr β the datagram pair BIO. that's the whole squad π«Ά
π 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);
int BIO_dgram_set0_local_addr(BIO *bio, BIO_ADDR *addr);
like, copy-paste it, it's fine, i won't tell π
π¦ DESCRIPTION π¦
BIO_s_dgram_pair() hands you the method for a BIO datagram pair. and a BIO datagram pair is literally a glow-up of a BIO pair (peep BIO_s_bio(3)) except it has datagram semantics. what does that mean in real-person terms? the length of the buffer you pass to a write call will match the length that comes back out of a read call. periodt. but β and this is the plot twist π β if the buffer you pass to a read call is too short, the datagram gets truncated or the read straight-up fails, and which one happens depends on how the BIO is configured.
the BIO datagram pair also attaches metadata to each write, like source and destination addresses. that info can be retrieved on read. it's giving envelope with a return address βοΈ
a classic use case: you want your app to keep all the datagram network I/O that libssl asks for under application control. very main character of you.
this BIO is designed to support multithreaded use, but only where certain restrictions are observed β go read THREADING, i'm serious π₯
and each half of a pair can signal to the other half whether it supports certain capabilities β that's CAPABILITY INDICATION, keep scrolling bestie.
BIO_new_bio_dgram_pair() is the shortcut combo: it's BIO_new(3), BIO_make_bio_pair(3) and BIO_set_write_buf_size(3) rolled into one, creating a connected pair of BIOs bio1, bio2 with write buffer sizes writebuf1 and writebuf2. if either size is zero, the default size is used. no notes. π
BIO_make_bio_pair(3) can 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 them may currently be associated in a pair. no cheating, no side-eyes.
BIO_destroy_bio_pair(3) destroys the association between two connected BIOs. and freeing either half of the pair will automatically destroy the association β it's a package deal, they're bonded π«Ά
BIO_reset(3) clears any data in the write buffer of the given BIO. so the opposite BIO in the pair will no longer have any data waiting to be read. poof, gone, ghosted.
the BIO keeps a fixed size internal write buffer. when it's full, further writes fail until the buffer is drained via calls to BIO_read(3). you can change the size with BIO_set_write_buf_size(3) and ask about it with BIO_get_write_buf_size(3).
β οΈ heads up: the write buffer is partially consumed by metadata stored internally that's attached to each datagram, like source and destination addresses. the size of this overhead is undefined and may change between releases. so don't do math on it, bestie, i'm begging π
the standard BIO_ctrl_pending(3) call has modified behaviour here β it returns the size, in bytes, of the next datagram waiting to be read. you can use it to make sure you hand an adequate buffer to a subsequent read call. if no datagram is waiting, you get zero back. zero. nothing. the void.
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. it's a nothing-burger.
BIO_eof(3) returns 1 only if the given BIO datagram pair BIO is not currently connected to a peer BIO.
BIO_get_write_guarantee(3) and BIO_ctrl_get_write_guarantee(3) tell you how large a datagram the next call to BIO_write(3) can accept. if there's not enough space in the write buffer to accept another datagram equal in size to the configured MTU, zero is returned (see below). the whole point is to dodge that tragic situation where an app reads a datagram from a network intending to write it to a BIO datagram pair, but the received datagram ends up too big to write. we've all been there. we don't want to be there again. π
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 gets silently truncated. that default keeps compatibility with the semantics of the Berkeley sockets API, iykyk.
BIO_dgram_set_mtu() and BIO_dgram_get_mtu() set and get an informational MTU value on the BIO datagram pair. if you use BIO_dgram_set_mtu() on a BIO that's 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 other code may use it to determine a requested MTU. when a BIO datagram pair BIO is created, the MTU is set to an unspecified but valid value. mysterious. iconic. π
BIO_dgram_set0_local_addr() sets the local BIO_ADDR to be used when sending a datagram via a BIO datagram pair. this becomes the peer address when receiving on the other half of the pair. if the BIO is used in a call to BIO_sendmmsg(3) and a local address is explicitly specified, then the explicitly specified local address takes precedence. the reference to the BIO_ADDR is passed to the BIO by this call and will be freed automatically when the BIO is freed. no manual cleanup era, we love that for you π
BIO_flush(3) is a no-op. it does nothing. it's just there. vibes only.
π NOTES π
the halves of a BIO datagram pair have independent lifetimes and must be separately freed. they're connected, not codependent. therapy-speak, honestly.
π§ THREADING π§
BIO_recvmmsg(3), BIO_sendmmsg(3), BIO_read(3), BIO_write(3), BIO_pending(3), BIO_get_write_guarantee(3) and BIO_flush(3) may be used by multiple threads simultaneously on the same BIO datagram pair. slay, concurrency. β¨
specific BIO_ctrl(3) operations β namely BIO_CTRL_PENDING, BIO_CTRL_FLUSH and BIO_C_GET_WRITE_GUARANTEE β may also be used.
π but invoking any other BIO call, or any other 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. do not. i am asking nicely. do not.
π CAPABILITY INDICATION π
the BIO datagram pair can be used to enqueue datagrams which have source and destination addresses attached. and it's super important that the component consuming one side of a BIO datagram pair understands whether the other side of the pair will honour any source and destination addresses it attaches to each datagram. example: if datagrams are queued with destination addresses set but then just read by simple calls to BIO_read(3), the destination addresses will be discarded. thrown away. disrespected. π
each half of a BIO datagram pair can have capability flags set on it, indicating whether source and destination addresses will be honoured by the reader, and whether they'll be provided by the writer. set these via BIO_dgram_set_caps(), and they'll be reflected in the value returned by BIO_dgram_get_effective_caps() on the opposite BIO. if you need it, the capability value you previously set can be retrieved with BIO_dgram_get_caps().
π₯ key distinction, get this right:BIO_dgram_set_caps()on a given BIO controls the capabilities advertised to the peer, andBIO_dgram_get_effective_caps()on a given BIO determines the capabilities advertised by the peer of that BIO. one's what you say about yourself, the other's what they say about themselves. it's giving healthy communication.
π the capabilities, bestie π
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.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.BIO_DGRAM_CAP_PROVIDES_SRC_ADDRβ the user of the datagram pair BIO advertises that it will provide source addressing information with future writes to the BIO pair, where available.BIO_DGRAM_CAP_PROVIDES_DST_ADDRβ the user of the datagram pair BIO advertises that it will provide destination addressing information with future writes to the BIO pair, where available.
if a caller attempts to specify a destination address (for example, using BIO_sendmmsg(3)) and the peer has not advertised the BIO_DGRAM_CAP_HANDLES_DST_ADDR capability, the operation fails. so like... capability negotiation is mandatory, fr fr.
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 BIO_dgram_set_local_addr_enable(3).
if a caller attempts to enable local address support using BIO_dgram_set_local_addr_enable(3) and BIO_dgram_get_local_addr_cap(3) does not return 1 (meaning the peer has not advertised both the BIO_DGRAM_CAP_HANDLES_SRC_ADDR and the BIO_DGRAM_CAP_PROVIDES_DST_ADDR capability), the operation fails.
BIO_DGRAM_CAP_PROVIDES_SRC_ADDR and 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. they're informational, they're a promise, they're a note in the group chat π
β RETURN VALUES β
BIO_new_bio_dgram_pair()returns1on success, with the new BIOs available inbio1andbio2β or0on failure, withNULLpointers stored into the locations forbio1andbio2. check the error stack for more info.BIO_dgram_set_no_trunc(),BIO_dgram_set_caps()andBIO_dgram_set_mtu()return1on success and0on failure.BIO_dgram_get_no_trunc()returns1if no-truncate mode is enabled on a BIO, or0if no-truncate mode is not enabled or not supported on a given BIO.BIO_dgram_get_effective_caps()andBIO_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.BIO_dgram_set0_local_addr()returns1on success and<= 0otherwise.
π SEE ALSO π
BIO_s_bio(3), bio(7) β go read them, they're the lore π«Ά
πΈ HISTORY πΈ
BIO_s_dgram_pair(), BIO_new_bio_dgram_pair() were added in OpenSSL 3.2. new-ish, but already legendary.
π COPYRIGHT π
Copyright 2022-2025 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>.
and that's the whole page, bestie π this was OpenSSL 3.5.6, dated 2026-04-07, and this manual page is BIO_S_DGRAM_PAIR(3ossl) β ily, go write some datagrams πβ¨
π¦ where this page comes from
- freebsd 15.1: base 15.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms