BIO_S_DGRAM_PAIR(3ossl)

πŸ’– BIO_S_DGRAM_PAIR πŸ’–

okay bestie, so this is the OpenSSL BIO_S_DGRAM_PAIR(3ossl) page, and it's literally all about the datagram pair BIO β€” which is lowkey one of the coolest little gizmos in libssl's toybox. Sit down, grab a strawberry πŸ“, we're doing this.

✨ NAME ✨

So this whole page covers a whole lil squad of functions, bestie:

  • 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

...and together they're all about the datagram pair BIO. periodt πŸ’…


πŸ¦‹ 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);

🌸 DESCRIPTION 🌸

BIO_s_dgram_pair() hands you back the method for a BIO datagram pair. And what even is a BIO datagram pair, you ask? It's basically like a BIO pair (go look at BIO_s_bio(3) if you need the lore) but with datagram semantics. Translated out of nerd: whatever length you hand to a write call is exactly the length that comes back out of a read call. no cap. And if the buffer you gave to the read call is too short? Then the datagram gets truncated, OR the read just fails β€” it depends on how the BIO is configured, so pay attention bestie πŸ‘€

The BIO datagram pair also attaches certain metadata to every single write β€” stuff like source and destination addresses. You can get that info back when you read. ✨

A typical application of a BIO datagram pair is letting an application keep all the datagram network I/O that libssl asks for under the application's own control. main character energy for real.

The BIO datagram pair is designed to support multithreaded use, as long as you observe certain restrictions β€” go check the THREADING section, it's important.

The BIO datagram pair also lets each half of a pair signal to the other half whether they support certain capabilities</c>. See CAPABILITY INDICATION below, babe.

BIO_new_bio_dgram_pair() is the combo-deal function β€” it bundles together the calls to BIO_new(3), BIO_make_bio_pair(3) and BIO_set_write_buf_size(3) to create a connected pair of BIOs, bio1 and bio2, with write buffer sizes writebuf1 and writebuf2. If either size is zero, then the default size gets used. πŸŽ€

BIO_make_bio_pair(3) can be used to join two datagram pair BIOs into a pair. Both BIOs have to be using the method returned by BIO_s_dgram_pair(), and neither of them can already be associated in a pair. no cheating πŸ’…

BIO_destroy_bio_pair(3) destroys the association between two connected BIOs. And heads up β€” freeing either half of the pair automatically destroys the association. it's giving "we go down together" 😭

BIO_reset(3) clears out any data sitting in the write buffer of the given BIO. Which means the opposite BIO in the pair won't have any data waiting to be read anymore. bye data πŸ¦‹

The BIO keeps a fixed size internal write buffer. When that buffer is full, further writes will fail until it gets drained via calls to BIO_read(3). You can change the size with BIO_set_write_buf_size(3) and check it with BIO_get_write_buf_size(3).

not me reminding you that the write buffer is partially eaten up by metadata stored internally that's attached to each datagram β€” like source and destination addresses. The size of that overhead is undefined and may change between releases. so don't go doing math on it, bestie.

The standard BIO_ctrl_pending(3) call has modified behaviour here β€” it returns the size of the next datagram waiting to be read, in bytes. An application can use this to make sure it's handing an adequate buffer to a subsequent read call. If no datagram is waiting to be read, you get zero back. 🫢

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. literally nothing happens.

BIO_eof(3) returns 1 only if the given BIO datagram pair BIO isn't currently connected to a peer BIO. that's the whole vibe check.

BIO_get_write_guarantee(3) and BIO_ctrl_get_write_guarantee(3) return how large a datagram the next call to BIO_write(3) can accept. If there isn't enough space in the write buffer to accept another datagram equal in size to the configured MTU, zero is returned (see below). This exists to dodge the cursed situation where an application tries to read a datagram from a network intending to write it to a BIO datagram pair, but the received datagram ends up being too large to write to the BIO datagram pair. the horror 😭

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 (that's the default, bestie), the datagram gets silently truncated. This default behaviour keeps compatibility with the semantics of the Berkeley sockets API. iykyk.

BIO_dgram_set_mtu() and BIO_dgram_get_mtu() can set 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 gets set on both halves of the pair. Now β€” 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 figure out a requested MTU. When a BIO datagram pair BIO is created, the MTU is set to an unspecified but valid value. mysterious ✨

BIO_dgram_set0_local_addr() can set the local BIO_ADDR to be used when sending a datagram via a BIO datagram pair. That 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. so don't you dare free it yourself πŸ™ˆ

BIO_flush(3) is a no-op. that's it. that's the sentence. πŸ’€


πŸŽ€ NOTES πŸŽ€

The halves of a BIO datagram pair have independent lifetimes and must be separately freed. they're giving siblings not twins.


🧚 THREADING 🧚

These ones may be used by multiple threads simultaneously on the same BIO datagram pair:

Specific BIO_ctrl(3) operations may also be used β€” namely BIO_CTRL_PENDING, BIO_CTRL_FLUSH and BIO_C_GET_WRITE_GUARANTEE.

and now the scary part 😭 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. undefined behaviour is not a vibe. do not do it.

πŸ‘‘ CAPABILITY INDICATION πŸ‘‘

The BIO datagram pair can be used to enqueue datagrams that have source and destination addresses attached. And it is so 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. Like, for example β€” if datagrams are queued with destination addresses set but they're just read by simple calls to BIO_read(3), then the destination addresses get discarded. gone. poof. πŸ’¨

Each half of a BIO datagram pair can have capability flags set on it. These indicate whether source and destination addresses will be honoured by the reader, and whether they'll be provided by the writer. You should set these capability flags via a call to BIO_dgram_set_caps(), and those capabilities will 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 using BIO_dgram_get_caps().

okay here's the part everyone gets confused about, so listen up bestie: BIO_dgram_set_caps() on a given BIO controls the capabilities advertised to the peer, while BIO_dgram_get_effective_caps() on a given BIO determines the capabilities advertised by the peer of that BIO. two different directions, babes. πŸ’…

The following capabilities are available:

  • 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. pinky promise 🫢
  • 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 the fact 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 the fact 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 hasn't advertised the BIO_DGRAM_CAP_HANDLES_DST_ADDR capability, the operation fails. So yeah β€” capability negotiation is mandatory. not optional, not a suggestion, mandatory.

If a caller attempts to specify a source address when writing, or requests a destination address when receiving, and local address support hasn't 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) doesn't return 1 β€” which would mean the peer hasn't advertised both the BIO_DGRAM_CAP_HANDLES_SRC_ADDR and the BIO_DGRAM_CAP_PROVIDES_DST_ADDR capability β€” then the operation fails. it's giving strict.

And finally: 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 β€” and this is the twist πŸ¦‹ β€” these capability flags do not affect the behaviour of the BIO datagram pair. they're just, like, announcing your intentions.


🌈 RETURN VALUES 🌈

  • BIO_new_bio_dgram_pair() returns 1 on success, with the new BIOs available in bio1 and bio2. Or 0 on failure, with NULL pointers stored into the locations for bio1 and bio2. Check the error stack for more info, bestie.
  • 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 isn't supported.
  • BIO_dgram_set0_local_addr() returns 1 on success and <= 0 otherwise.

⭐ SEE ALSO ⭐

BIO_s_bio(3), bio(7)

go read those too, they're the prequels πŸ’œ


πŸ”₯ HISTORY πŸ”₯

BIO_s_dgram_pair(), BIO_new_bio_dgram_pair() were added in OpenSSL 3.2. baby functions, growing up so fast πŸ₯Ή


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 BIO_S_DGRAM_PAIR(3ossl) from OpenSSL 3.5.9, dated 2026-09-29 β€” slay, go forth and write your datagrams πŸ’–βœ¨πŸŽ€πŸ¦‹πŸŒΈπŸ’…πŸ«Ά

πŸ“¦ where this page comes from