BIO_S_BIO(3ossl)
πΈ NAME πΈ
okay so this whole page is about the BIO pair family, and it covers a LOT of functions, so buckle up bestie:
BIO_s_bio, BIO_make_bio_pair, BIO_destroy_bio_pair, BIO_shutdown_wr, BIO_set_write_buf_size, BIO_get_write_buf_size, BIO_new_bio_pair, BIO_get_write_guarantee, BIO_ctrl_get_write_guarantee, BIO_get_read_request, BIO_ctrl_get_read_request, BIO_ctrl_reset_read_request β all of these are the BIO pair BIO. that's the vibe. this is BIO_S_BIO(3ossl) from the OpenSSL manual, btw π
β¨ SYNOPSIS β¨
#include <openssl/bio.h>
const BIO_METHOD *BIO_s_bio(void);
int BIO_make_bio_pair(BIO *b1, BIO *b2);
int BIO_destroy_bio_pair(BIO *b);
int BIO_shutdown_wr(BIO *b);
int BIO_set_write_buf_size(BIO *b, long size);
size_t BIO_get_write_buf_size(BIO *b, long size);
int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2);
int BIO_get_write_guarantee(BIO *b);
size_t BIO_ctrl_get_write_guarantee(BIO *b);
int BIO_get_read_request(BIO *b);
size_t BIO_ctrl_get_read_request(BIO *b);
int BIO_ctrl_reset_read_request(BIO *b);
π¦ DESCRIPTION π¦
BIO_s_bio() returns the method for a BIO pair. so what even is a BIO pair? bestie it's literally two source/sink BIOs holding hands π«Ά β anything you write to one half gets buffered, and then the other half can read it. cute, right? but the catch: both halves must usually be handled by the same application thread, because no locking is done on the internal data structures. so like, no passing halves around between threads and hoping for the best, ngl π΅
since BIO chains typically end in a source/sink BIO, you can make this thing one half of a BIO pair and then have all the data processed by the chain under application control. main character energy for your app, honestly π
one typical use of BIO pairs is putting TLS/SSL I/O under application control β very useful when the app wants a non-standard transport for TLS/SSL, or when the normal socket routines are just... inappropriate. not their fault, they're doing their best π
- calls to
BIO_read_ex()will read data from the buffer or request a retry if no data is available - calls to
BIO_write_ex()will place data in the buffer or request a retry if the buffer is full - the standard calls
BIO_ctrl_pending()andBIO_ctrl_wpending()can be used to determine the amount of pending data in the read or write buffer BIO_reset()clears any data in the write buffer
BIO_make_bio_pair() joins two separate BIOs into a connected pair. shipping them, fr fr π
BIO_destroy_pair() destroys the association between two connected BIOs. (yes the synopsis spells it BIO_destroy_bio_pair and this paragraph spells it BIO_destroy_pair, we don't make the rules π) also: freeing up any half of the pair will automatically destroy the association.
BIO_shutdown_wr() is used to close down a BIO b. after this call, no further writes on BIO b are allowed (they will return an error). reads on the other half of the pair will return any pending data, or EOF once all pending data has been read. bye-bye writing half βοΈ
BIO_set_write_buf_size() sets the write buffer size of BIO b to size. if the size is not initialized, a default value is used β currently 17K, which is sufficient for a maximum size TLS record. iconic default, no cap π
BIO_get_write_buf_size() returns the size of the write buffer.
BIO_new_bio_pair() is the combo deal: it combines the calls to BIO_new(), BIO_make_bio_pair() and BIO_set_write_buf_size() 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 is used. but heads up π¨ BIO_new_bio_pair() does not check whether bio1 or bio2 do point to some other BIO β the values are overwritten, and BIO_free() is not called. so don't be leaking, bestie π
BIO_get_write_guarantee() and BIO_ctrl_get_write_guarantee() return the maximum length of data that can be currently written to the BIO. writes larger than this value will return a value from BIO_write_ex() less than the amount requested, or, if the buffer is full, request a retry. and psa: BIO_ctrl_get_write_guarantee() is a function whereas BIO_get_write_guarantee() is a macro π§
BIO_get_read_request() and BIO_ctrl_get_read_request() return the amount of data requested β or the buffer size if it is less β if the last read attempt at the other half of the BIO pair failed due to an empty buffer. what's it for? determining how much data should be written to the BIO so the next read will succeed π‘ this is most useful in TLS/SSL applications where the amount of data read is usually meaningful rather than just a buffer size. after a successful read this call will return zero. it also will return zero once new data has been written satisfying the read request or part of it. and note: BIO_get_read_request() never returns an amount larger than that returned by BIO_get_write_guarantee().
BIO_ctrl_reset_read_request() can also be used to reset the value returned by BIO_get_read_request() to zero. handy lil reset button π
π NOTES π
both halves of a BIO pair should be freed. yes, both. even if one half is implicit freed due to a BIO_free_all() or SSL_free() call, the other half needs to be freed. say it with me: free your halves π₯
when used in bidirectional applications (such as TLS/SSL), care should be taken to flush any data in the write buffer. this can be done by calling BIO_pending() on the other half of the pair and, if any data is pending, reading it and sending it to the underlying transport. this must be done before any normal processing (such as calling select()) due to a request and BIO_should_read() being true.
to see why this is important, consider a case where a request is sent using BIO_write_ex() and a response read with BIO_read_ex() β this can occur during a TLS/SSL handshake, for example. BIO_write_ex() will succeed and place data in the write buffer. BIO_read_ex() will initially fail and BIO_should_read() will be true. if the application then waits for data to be available on the underlying transport before flushing the write buffer, it will never succeed β because the request was never sent! πππ
BIO_eof() is true if no data is in the peer BIO and the peer BIO has been shutdown.
and for the record, these are all implemented as macros: BIO_make_bio_pair(), BIO_destroy_bio_pair(), BIO_shutdown_wr(), BIO_set_write_buf_size(), BIO_get_write_buf_size(), BIO_get_write_guarantee(), and BIO_get_read_request() π
β RETURN VALUES β
BIO_new_bio_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 information, bestie, the tea is in there π΅
[XXXXX: More return values need to be added here] π yeah. that's literally in the page. we're waiting on it too.
π EXAMPLES π
the BIO pair can be used to have full control over the network access of an application. the app can call select() on the socket as required without having to go through the SSL-interface. obsessed.
BIO *internal_bio, *network_bio;
...
BIO_new_bio_pair(&internal_bio, 0, &network_bio, 0);
SSL_set_bio(ssl, internal_bio, internal_bio);
SSL_operations(); /* e.g. SSL_read and SSL_write */
...
application | TLS-engine
| |
+----------> SSL_operations()
| /\ ||
| || \/
| BIO-pair (internal_bio)
| BIO-pair (network_bio)
| || /\
| \/ ||
+-----------< BIO_operations()
| |
| |
socket
...
SSL_free(ssl); /* implicitly frees internal_bio */
BIO_free(network_bio);
...
so since the BIO pair will only buffer the data and never directly access the connection, it behaves nonblocking and will return as soon as the write buffer is full or the read buffer is drained. then the application has to flush the write buffer and/or fill the read buffer π«‘
use the BIO_ctrl_pending() to find out whether data is buffered in the BIO and must be transferred to the network. use BIO_ctrl_get_read_request() to find out how many bytes must be written into the buffer before the SSL_operation() can successfully be continued. iykyk π
π WARNINGS π
as the data is buffered, SSL_operation() may return with an ERROR_SSL_WANT_READ condition, but there is still data in the write buffer. β οΈ an application must not rely on the error value of SSL_operation() but must assure that the write buffer is always flushed first. otherwise a deadlock may occur as the peer might be waiting for the data before being able to continue. and a deadlock is not the slay we're going for π
π₯ SEE ALSO π₯
π COPYRIGHT π
Copyright 2000-2020 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>.
okay that's the whole page, bestie, you're officially a BIO pair expert πβ¨ this was BIO_S_BIO(3ossl) from OpenSSL, version 3.5.0, dated 2026-09-25 β go forth and flush your write buffers ππΈπ¦
π¦ where this page comes from
- opensuse-leap 16.0: openssl-3-doc 3.5.0-160000.11.1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms