BIO_S_DATAGRAM(3)

πŸ“¦ version 4.4.0, in alpine 3.24, arch rolling

bestie, welcome to BIO_S_DATAGRAM(3) β€” the datagram socket BIO page, and yes it’s giving β€œnetworking but make it wrappers” πŸ’…

πŸ’– NAME πŸ’–

BIO_s_datagram, BIO_new_dgram, BIO_dgram_set_peer, BIO_ctrl_dgram_connect, BIO_dgram_get_peer, BIO_ctrl_set_connected, BIO_dgram_recv_timedout, BIO_dgram_send_timedout, BIO_dgram_non_fatal_error β€” datagram socket BIO 🫢

✨ SYNOPSIS ✨

       library β€œlibcrypto” #include <openssl/bio.h>

       const BIO_METHOD *
       BIO_s_datagram(void);

       BIO *
       BIO_new_dgram(int fd, int close_flag);

       int
       BIO_dgram_set_peer(BIO *b, struct sockaddr *sa);

       int
       BIO_ctrl_dgram_connect(BIO *b, struct sockaddr *sa);

       int
       BIO_dgram_get_peer(BIO *b, struct sockaddr *sa);

       int
       BIO_ctrl_set_connected(BIO *b, long argl, struct sockaddr *sa);

       int
       BIO_dgram_recv_timedout(BIO *b);

       int
       BIO_dgram_send_timedout(BIO *b);

       int
       BIO_dgram_non_fatal_error(int errnum);

🫢 DESCRIPTION 🫢

BIO_s_datagram() hands you the datagram socket BIO method. The classic main-character use case is sending data over the IPv4 or IPv6 udp(4) protocol. ✨

When you call it on a datagram socket BIO object, BIO_method_type(3) returns the constant BIO_TYPE_DGRAM, and BIO_method_name(3) returns a pointer to the static string "datagram socket".

🧚 Constructors and destructors 🧚

BIO_new(3) allocates a fresh datagram socket BIO object and initializes all its data to zero. That includes the datagram socket file descriptor, the peer address, the init flag that you can retrieve with BIO_get_init(3), the connected flag, the MTU, and all timeout and error information. The reference count and the close flag are set to 1. Periodt.

BIO_new_dgram() allocates and initializes a new datagram socket BIO object with BIO_new(3), sets the datagram socket file descriptor and the close flag according to its arguments, and sets the init flag to 1.

If the reference count reaches 0 in BIO_free(3) and the close and init flags are set, shutdown(2) and close(2) are called on the datagram socket file descriptor before freeing the storage used by the BIO object. Cleanup slay πŸ’…

When a chain containing a datagram socket BIO is copied with BIO_dup_chain(3), these get automatically copied from the original BIO object to the new one:

But these are not copied β€” they get initialized to zero instead:

  • the peer address
  • the connected flag
  • the MTU
  • all timeout and error information

🌸 Initialization and configuration 🌸

If the close flag is set in b, BIO_set_fd(3) clears all flags that are set in b, and if the init flag was set, it calls shutdown(2) and close(2) on the previously assigned file descriptor. In any case, BIO_set_fd(3) then sets the new file descriptor and the new close flag according to its arguments and sets the init flag to 1.

If the init flag is set in b, BIO_get_fd(3) returns its datagram socket file descriptor, and unless the c argument is a NULL pointer, it also stores the file descriptor in *c. If the init flag is not set, BIO_get_fd(3) fails and returns -1.

BIO_set_close(3) sets the close flag in b to the flag argument. BIO_get_close(3) returns the value of the close flag from b.

For datagram socket BIO objects, the shutdown flag is the same flag as the close flag. So BIO_set_shutdown(3) has the same effect as BIO_set_close(3), and BIO_get_shutdown(3) has the same effect as BIO_get_close(3). Same vibe, different name πŸ¦‹

BIO_dgram_set_peer() copies sa as the peer address into b.

BIO_ctrl_dgram_connect() does exactly the same as BIO_dgram_set_peer(). Its name is even more misleading than the name of BIO_ctrl_set_connected(). In addition to what is said there, BIO_ctrl_dgram_connect() does not even set the connected flag in b. Not me expecting it to actually connect 😭

BIO_dgram_get_peer() copies the peer address from b to *sa. Before calling this function, the caller has to make sure that the peer address is indeed set in b and that sufficient memory is available starting at sa to copy a complete struct sockaddr, struct sockaddr_in, or struct sockaddr_in6 to that place, depending on which address family b is currently used for. This is a real β€œbestie, check your buffer” moment πŸ™ˆ

Unless sa is NULL, BIO_ctrl_set_connected() sets the connected flag in b and copies sa as the peer address into b. If sa is NULL, BIO_ctrl_set_connected() clears the connected flag and the peer address in b.

Considering that communication using a datagram protocol is connectionless, the name of this function is misleading. It is neither establishing nor terminating a connection, nor changing anything with respect to the state of the datagram socket β€” it is merely modifying some purely informational data in the wrapping BIO object. The additional argl argument is passed through to the callbacks documented in BIO_set_callback(3) if any such callbacks are installed, but it is otherwise ignored.

BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT interprets the parg argument as a pointer to a struct timeval and sets the read timeout to the specified absolute UTC time.

BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, BIO_CTRL_DGRAM_GET_RECV_TIMEOUT, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, or BIO_CTRL_DGRAM_GET_SEND_TIMEOUT interprets the parg argument as a pointer to a struct timeval and calls setsockopt(2) or getsockopt(2) on the datagram socket file descriptor of b with an argument of SO_RCVTIMEO or SO_SNDTIMEO, respectively.

BIO_CTRL_DGRAM_SET_RECV_TIMEOUT and BIO_CTRL_DGRAM_SET_SEND_TIMEOUT return 1 on success. BIO_CTRL_DGRAM_GET_RECV_TIMEOUT and BIO_CTRL_DGRAM_GET_SEND_TIMEOUT return the number of bytes written to *parg. All four return -1 on failure.

Remember that BIO_read(3) may actually use a shorter timeout when BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT is in effect. Lowkey important πŸ‘€

BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_GET_FALLBACK_MTU returns 1232 if the peer address is an IPv6 address that is not IPv4 mapped, or 548 otherwise. Making sure that a peer address is set before issuing this command is the responsibility of the caller.

BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_SET_MTU sets the MTU attribute of b to the value of the larg argument and also returns that argument. BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_GET_MTU returns the MTU attribute of b, or 0 if it was not set.

BIO_ctrl(3) with a cmd of BIO_CTRL_DGRAM_MTU_EXCEEDED returns 1 if the most recent non-fatal failure of BIO_read(3) or BIO_write(3) was caused by EMSGSIZE, or 0 otherwise. This command also clears the errno(2) value that was saved internally for this particular purpose, so that issuing the same command again will return 0 until the next EMSGSIZE failure occurs. One-shot info, bestie πŸ’€

BIO_dgram_recv_timedout() and BIO_dgram_send_timedout() check whether the most recent non-fatal failure of BIO_read(3) or BIO_write(3) was caused by EAGAIN. Despite having different names, both functions do exactly the same thing, and both inspect the most recent non-fatal I/O failure, no matter whether it occurred during a receive or send operation. Both functions also clear the errno(2) value that was saved internally for this particular purpose, so that calling these functions again will return 0 until the next EAGAIN failure occurs.

Datagram socket BIOs do not support BIO_eof(3), BIO_get_mem_data(3), BIO_pending(3), BIO_reset(3), BIO_seek(3), BIO_tell(3), and BIO_wpending(3). Attempting any such operation results in failure and returns a value of 0. Not every BIO wants to do every trick, and that’s valid 🌈

Control commands correspond to accessor functions as follows:

  • BIO_C_GET_FD β€” BIO_get_fd(3)
  • BIO_C_SET_FD β€” BIO_set_fd(3)
  • BIO_CTRL_DGRAM_CONNECT β€” BIO_ctrl_dgram_connect() (deprecated)
  • BIO_CTRL_DGRAM_GET_PEER β€” BIO_dgram_get_peer()
  • BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP β€” BIO_dgram_recv_timedout()
  • BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP β€” BIO_dgram_send_timedout()
  • BIO_CTRL_DGRAM_SET_CONNECTED β€” BIO_ctrl_set_connected()
  • BIO_CTRL_DGRAM_SET_PEER β€” BIO_dgram_set_peer()
  • BIO_CTRL_GET_CLOSE β€” BIO_get_close(3)
  • BIO_CTRL_SET_CLOSE β€” BIO_set_close(3)

πŸ¦‹ Input and output operations πŸ¦‹

BIO_read(3) attempts to read up to len bytes into buf from the datagram socket file descriptor using recvfrom(2). If a read timeout is set, setsockopt(2) is used with an argument of SO_RCVTIMEO to temporarily shorten the timeout on the datagram socket during the recvfrom(2) call, such that it returns before the read timeout expires.

If recvfrom(2) succeeds and the connected flag is not yet set, BIO_read(3) also copies the peer address received from recvfrom(2) into b.

If recvfrom(2) is attempted, BIO_read(3) clears the flags BIO_FLAGS_WRITE and BIO_FLAGS_IO_SPECIAL in b, and clears or sets the flags BIO_FLAGS_READ and BIO_FLAGS_SHOULD_RETRY as appropriate.

If the connected flag is set in b, BIO_write(3) attempts to write(2) len bytes from buf to the datagram socket file descriptor. If the connected flag is not set, it attempts to transmit len bytes from buf to the peer using sendto(2).

If write(2) or sendto(2) is attempted, BIO_write(3) clears the flags BIO_FLAGS_READ and BIO_FLAGS_IO_SPECIAL in b, and clears or sets the flags BIO_FLAGS_WRITE and BIO_FLAGS_SHOULD_RETRY as appropriate.

The effect of BIO_puts(3) is similar to the effect of BIO_write(3) with a len argument of strlen(string).

Datagram socket BIOs do not support BIO_gets(3). Calling this function fails and returns -2.

BIO_flush(3) has no effect on a datagram socket BIO. It always succeeds and returns 1. Flush said β€œI’m just here for the vibe” πŸŽ€

πŸ’… RETURN VALUES πŸ’…

BIO_s_datagram() returns the datagram socket BIO method.

BIO_new_dgram() returns a newly allocated datagram socket BIO object, or NULL on failure.

BIO_dgram_set_peer(), BIO_ctrl_dgram_connect(), and BIO_ctrl_set_connected() return 1 on success or a value less than or equal to zero on failure. They can only fail if b is not a datagram socket BIO object.

BIO_dgram_get_peer() returns the number of bytes copied to sa, or a value less than or equal to zero on failure. It can only fail if b is not a datagram socket BIO object.

BIO_dgram_recv_timedout() and BIO_dgram_send_timedout() return 1 if the most recent non-fatal I/O error was caused by EAGAIN, or 0 otherwise.

BIO_dgram_non_fatal_error() returns 1 if errnum is EAGAIN, EALREADY, EINPROGRESS, or EINTR, or 0 otherwise β€” even if errnum is 0.

🌈 SEE ALSO 🌈

πŸŽ€ HISTORY πŸŽ€

BIO_s_datagram(), BIO_new_dgram(), BIO_dgram_set_peer(), BIO_ctrl_dgram_connect(), BIO_ctrl_set_connected(), BIO_dgram_recv_timedout(), BIO_dgram_send_timedout(), and BIO_dgram_non_fatal_error() first appeared in OpenSSL 0.9.8 and have been available since OpenBSD 4.5.

BIO_dgram_get_peer() first appeared in OpenSSL 0.9.8m and has been available since OpenBSD 4.9.

πŸ’€ BUGS (yes bestie, we gotta talk about it) πŸ’€

Okay so, real talk: if getsockopt(2) or setsockopt(2) decides to fail during a BIO_read(3), the library's whole response is... printing an error message to standard error output and then just ignoring the problem. Like, ma'am? 😭 So you could end up running with timeout values you never actually intended β€” not it. Keep that in mind, fr fr.

Also β€” BIO_read(3) and BIO_write(3) may clear the global variable errno(2) before they even attempt the recvfrom(2) or sendto(2) system call. But here's the ✨ fun ✨ part: they may not clear it if they fail before reaching that point. So the vibe of errno after a failure is lowkey a mystery sometimes. Check accordingly, iykyk. 🫢


And that's a wrap on BIO_S_DATAGRAM(3)! 🌈 This icon of a page is served up by Debian and dated June 8, 2025 β€” go forth and datagram responsibly, bestie πŸ’…βœ¨πŸŽ€

πŸ“¦ where this page comes from