BIO_SHOULD_RETRY(3)

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

πŸ’… BIO_SHOULD_RETRY(3) β€” the BIO retry functions, explained by your favorite Unix bestie

okay so this is a page from the Library Functions Manual, specifically libcrypto, and it's literally all about the BIO retry squad β€” the little functions that tell you why your BIO couldn't read or write and whether you should just… try again. no cap, this one's a keeper 🫢


✨ NAME ✨

BIO_should_read, BIO_should_write, BIO_should_io_special, BIO_retry_type, BIO_should_retry, BIO_get_retry_BIO, BIO_get_retry_reason, BIO_set_retry_reason β€” the BIO retry functions πŸ’«


🌸 SYNOPSIS 🌸

bestie, it's libcrypto, so like:

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

int
BIO_should_read(BIO *b);

int
BIO_should_write(BIO *b);

int
BIO_should_io_special(BIO *b);

int
BIO_retry_type(BIO *b);

int
BIO_should_retry(BIO *b);

#define BIO_FLAGS_READ             0x01
#define BIO_FLAGS_WRITE            0x02
#define BIO_FLAGS_IO_SPECIAL       0x04
#define BIO_FLAGS_RWS \
     (BIO_FLAGS_READ|BIO_FLAGS_WRITE|BIO_FLAGS_IO_SPECIAL)
#define BIO_FLAGS_SHOULD_RETRY     0x08

BIO *
BIO_get_retry_BIO(BIO *bio, int *reason);

int
BIO_get_retry_reason(BIO *bio);

void
BIO_set_retry_reason(BIO *bio, int reason);

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so picture this: your BIO just failed to read or write, and you're like "why 😭". these functions are literally the answer. they determine why a BIO isn't able to read or write data, and you'd typically call them right after a failed BIO_read(3) or BIO_write(3) call.

BIO_should_retry() returns 1 if the call that produced this condition should be retried at a later time, or 0 if an error occurred. like, "try again later bestie" vs "it's actually cooked".
BIO_should_read() returns 1 if the cause of the retry condition is that a BIO needs to read data, or 0 otherwise 🌸
BIO_should_write() returns 1 if the cause of the retry condition is that a BIO needs to write data, or 0 otherwise ✨
BIO_should_io_special() returns 1 if some special condition (i.e. a reason other than reading or writing) is the cause of the retry condition, or 0 otherwise πŸ‘‘

BIO_retry_type() returns the bitwise OR of one or more of the flags BIO_FLAGS_READ, BIO_FLAGS_WRITE, and BIO_FLAGS_IO_SPECIAL representing the cause of the current retry condition, or 0 if there is no retry condition. and get this β€” current BIO types only set one of the flags at a time πŸ’…

πŸ” BIO_get_retry_BIO()

BIO_get_retry_BIO() determines the precise reason for the special condition. it walks the BIO chain starting at bio and returns the BIO that caused this condition. if there's no special condition, bio itself is returned. and if reason is not a NULL pointer, *reason gets set to one of these reason codes:

🌈 the getters and the setter

BIO_get_retry_reason() returns one of the above reason codes for a special condition that occurred in bio. important: it does not walk the chain, and it returns 0 if no special condition occurred in bio itself. no cap, it stays in its lane 🧚

BIO_set_retry_reason() sets the retry reason for a special condition for the given bio. it's intended to be called by functions implementing a BIO type, rather than by functions merely using BIOs. iykyk β€” this one's for the implementers, not the consumers πŸ’œ

btw, BIO_should_retry(), BIO_should_read(), BIO_should_write(), BIO_should_io_special(), and BIO_retry_type() are implemented as macros. iconic.

πŸ’€ the "error condition" situation

if BIO_should_retry() returns false, then the precise "error condition" depends on the BIO type that caused it and the return code of the BIO operation. for example, if a call to BIO_read(3) on a socket BIO returns 0 and BIO_should_retry() is false, then the cause will be that the connection closed 😭 a similar condition on a file BIO will mean that it has reached EOF. some BIO types may place additional information on the error queue. for more details, see the individual BIO type manual pages πŸŽ€

πŸ”₯ blocking mode, aka the chill zone

if the underlying I/O structure is in a blocking mode, almost all current BIO types will not request a retry, because the underlying I/O calls won't. so if your app knows the BIO type will never signal a retry, it need not call BIO_should_retry() after a failed BIO I/O call. this is typically done with file BIOs. periodt.

SSL BIOs are the only current exception to this rule πŸ‘‘ β€” they can request a retry even if the underlying I/O structure is blocking, if a handshake occurs during a call to BIO_read(3). an application can retry the failed call immediately, or avoid this whole situation by setting SSL_MODE_AUTO_RETRY on the underlying SSL structure.

πŸ• about retrying immediately (don't)

while an application may retry a failed non-blocking call immediately, this is likely to be very inefficient, because the call will fail repeatedly until data can be processed or is available. bestie, it's giving "checking the fridge every 30 seconds". an application will normally wait until the necessary condition is satisfied β€” how this is done depends on the underlying I/O structure.

for example, if the cause is ultimately a socket and BIO_should_read() is true, then a call to select(2) may be made to wait until data is available and then retry the BIO operation. by combining the retry conditions of several non-blocking BIOs in a single select(2) call, it's possible to service several BIOs in a single thread β€” though the performance may be poor if SSL BIOs are present, because long delays can occur during the initial handshake process. slay for efficiency, but like, with caveats πŸ’…

⚠️ the blocking-forever warning

it is possible for a BIO to block indefinitely if the underlying I/O structure cannot process or return any data. this depends on the behaviour of the platform's I/O functions. this is often not desirable: one solution is to use non-blocking I/O and use a timeout on the select(2) (or equivalent) call. please do not let your program hang forever, it's not cute 😭

πŸŽ€ SEE ALSO πŸŽ€

BIO_new(3), BIO_read(3)


⭐ HISTORY ⭐

BIO_should_read(), BIO_should_write(), BIO_retry_type(), and BIO_should_retry() first appeared in SSLeay 0.6.0.

BIO_should_io_special(), BIO_get_retry_BIO(), and BIO_get_retry_reason() first appeared in SSLeay 0.8.0.

all these functions have been available since OpenBSD 2.4 🌈

BIO_set_retry_reason() first appeared in OpenSSL 1.1.0 and has been available since OpenBSD 7.1 ✨


πŸ’€ BUGS πŸ’€

the OpenSSL ASN.1 functions cannot gracefully deal with non-blocking I/O: they cannot retry after a partial read or write. this is usually worked around by only passing the relevant data to ASN.1 functions when the entire structure can be read or written. not me… not the ASN.1 functions… πŸ™ˆ


and that's the whole page bestie!! 🫢 this one came from Debian, dated June 8, 2025, and it's BIO_SHOULD_RETRY(3) β€” go forth and retry responsibly πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from