OPENSSL-QUIC(7SSL)
π NAME π
okay bestie, so this page is openssl-quic (it lives in the 7SSL corner of the OpenSSL manual, iykyk) β and it's literally all about OpenSSL QUIC β¨
π DESCRIPTION π
OpenSSL 3.2 and later? Yeah, they come with support for the QUIC transport protocol. Periodt. And you can use OpenSSL's QUIC powers for both client and server applications, no cap π«Ά This man page is here to walk you through how to let applications speak QUIC using the libssl API.
Here's the cute part: the QUIC protocol maps onto the standard SSL API π
A QUIC connection is represented by an SSL object exactly the same way a TLS connection is. So if you already have an app using the libssl API, you only need minimal changes to bring QUIC protocol support in. It's giving "glow up, but make it effortless" π
For clients, you can pair either OSSL_QUIC_client_method(3) or OSSL_QUIC_client_thread_method(3) with SSL_CTX_new(3). Wanna know the difference between those two? It's covered further down, keep reading bestie π For servers, there's literally only one option: the SSL method OSSL_QUIC_server_method(3) with SSL_CTX_new(3).
The rest of this man page goes through all of this, in order:
- Default stream mode versus multi-stream mode for clients π―
- The changes to existing
libsslAPIs which are driven by QUIC-related implementation requirements β and which existing applications should lowkey keep in mind π§ - Aspects which must be considered by existing applications when adopting QUIC, including potential changes which may be needed β οΈ
- Recommended usage approaches for new applications π±
- New, QUIC-specific APIs π₯
π¦ CLIENT MODES OF OPERATION π¦
So when a client creates a QUIC connection, by default it operates in default stream mode, which is intended to provide compatibility with existing non-QUIC application usage patterns π« In this mode, the connection has a single stream associated with it. Calls to SSL_read(3) and SSL_write(3) on the QUIC connection SSL object read and write from that stream. And whether the stream is client-initiated or server-initiated from a QUIC perspective? That depends on whether SSL_read(3) or SSL_write(3) is called first. Sneaky, right? π
Default stream mode is primarily for compatibility with existing applications. For new applications utilizing QUIC, it's recommended to disable this mode and instead adopt the multi-stream API β flip to the RECOMMENDATIONS FOR NEW APPLICATIONS section for more details, bestie π
πΈ Default Stream Mode πΈ
A QUIC client connection can be used in either default stream mode or multi-stream mode. By default, a newly created QUIC connection SSL object uses default stream mode. Like, it just shows up that way, no notes π
In default stream mode, a stream is implicitly created and bound to the QUIC connection SSL object. So SSL_read(3) and SSL_write(3) calls to the QUIC connection SSL object work by default, and they're mapped to that stream. Easy!
When default stream mode is used, any API function which can be called on a QUIC stream SSL object can also be called on a QUIC connection SSL object β and in that case, it affects the default stream bound to the connection π
The identity of a QUIC stream, including its stream ID, varies depending on whether a stream is client-initiated or server-initiated. In default stream mode, if a client application calls SSL_read(3) first before any call to SSL_write(3) on the connection, it is assumed that the application protocol is using a server-initiated stream, and the SSL_read(3) call will not complete (either blocking, or failing appropriately if nonblocking mode is configured) until the server initiates a stream π Conversely, if the client application calls SSL_write(3) before any call to SSL_read(3) on the connection, it is assumed that a client-initiated stream is to be used, and such a stream is created automatically. Boom, stream created, main character energy β¨
Default stream mode is intended to aid compatibility with legacy applications. New applications adopting QUIC should use multi-stream mode, described below, and avoid use of the default stream functionality. No cap.
It is possible to use additional streams in default stream mode using SSL_new_stream(3) and SSL_accept_stream(3) β but heads up, the default incoming stream policy will need to be changed using SSL_set_incoming_stream_policy(3) in order to use SSL_accept_stream(3) in this case π However, applications using additional streams are strongly recommended to use multi-stream mode instead. Like, strongly strongly. π«Ά
Also lowkey important: callingSSL_new_stream(3)orSSL_accept_stream(3)before a default stream has been associated with the QUIC connectionSSLobject will inhibit future creation of a default stream. So don't do that and then act surprised π
π Multi-Stream Mode π
The recommended usage mode for new applications adopting QUIC is multi-stream mode, in which no default stream is attached to the QUIC connection SSL object, and attempts to call SSL_read(3) and SSL_write(3) on the QUIC connection SSL object fail. Yeah, they just don't work there, bestie π€ Instead, an application calls SSL_new_stream(3) or SSL_accept_stream(3) to create individual stream SSL objects for sending and receiving application data using SSL_read(3) and SSL_write(3). One stream per vibe π
To use multi-stream mode, call SSL_set_default_stream_mode(3) with an argument of SSL_DEFAULT_STREAM_MODE_NONE. And bestie, listen β this function must be called prior to initiating the connection. The default stream mode cannot be changed after initiating a connection. Periodt. π
When multi-stream mode is used, meaning that no default stream is associated with the connection, calls to API functions which are defined as operating on a QUIC stream fail if called on the QUIC connection SSL object. For example, calls such as SSL_write(3) or SSL_get_stream_id(3) will fail. So don't try it πβ
π CHANGES TO EXISTING APIS π
Okay bestie, here's the tea β β most of the SSL APIs you already know and love, like SSL_read(3) and SSL_write(3), work exactly the way they do for TLS connections. Same semantics, no glow-up needed. But there are some exceptions, and you're gonna want to know them or you'll be crying in the group chat at 3am. π
So here's the full rundown of how existing APIs changed for QUIC:
- Since QUIC rides on UDP,
SSL_set_bio(3),SSL_set0_rbio(3)andSSL_set0_wbio(3)still function like before β but they now have to receive a BIO with datagram semantics. Basically there are four routes an app can take for its network BIO:BIO_s_datagram(3)β the recommended pick for most apps β¨ it replacesBIO_s_socket(3)and hands you a UDP socket.BIO_s_dgram_pair(3)β gives you BIO pair-like functionality but with datagram semantics. Chef's kiss for existing apps that use a BIO pair or memory BIO to manage libssl's communication with the network.BIO_s_dgram_mem(3)β a simple memory BIO-like interface, again with datagram semantics. But heads up: unlikeBIO_s_dgram_pair(3), it's unidirectional. One-way street, bestie. π£οΈ- Or you can go full DIY and implement a custom BIO π₯ but then the new
BIO_sendmmsg(3)andBIO_recvmmsg(3)APIs must be supported. No skipping that.
SSL_set_fd(3),SSL_set_rfd(3)andSSL_set_wfd(3)traditionally instantiate aBIO_s_socket(3). For QUIC? They instantiate aBIO_s_datagram(3)instead π¦ Which is equivalent to instantiating aBIO_s_datagram(3)and then usingSSL_set0_rbio(3)andSSL_set0_wbio(3).- Blocking mode got a whole new era. Back in the day, whether the application-level I/O APIs (like
SSL_read(3)andSSL_write(3)) blocked was directly correlated with whether the underlying network socket was configured to block. That is no longer the case. Nope. Apps must now explicitly configure the application-level blocking mode they want usingSSL_set_blocking_mode(3). Go readSSL_set_blocking_mode(3)for the deets, iykyk. - Network-level I/O must always be performed in a nonblocking manner π¨ The app can still enjoy blocking semantics for calls to application-level I/O functions like
SSL_read(3)andSSL_write(3), but the underlying network BIO handed to QUIC (like aBIO_s_datagram(3)) must be configured in nonblocking mode. For that application-level blocking functionality, seeSSL_set_blocking_mode(3). BIO_new_ssl_connect(3)got an upgrade π it's been changed to automatically use aBIO_s_datagram(3)when used with QUIC. So if your app uses this, you don't gotta change the BIO you use at all. Slay.BIO_new_buffer_ssl_connect(3)β cannot be used with QUIC, periodt. Apps have to switch over toBIO_new_ssl_connect(3)instead. Sorry not sorry. πSSL_shutdown(3)has significant changes in how QUIC connections must be shut down. And bestie, listen: the full RFC-conformant QUIC shutdown process may take an extended amount of time. That might not be the vibe for short-lived processes that should exit immediately after they finish using a QUIC connection. Good news though β there's a rapid shutdown mode for exactly those apps. Details inSSL_shutdown(3).SSL_want(3),SSL_want_read(3)andSSL_want_write(3)no longer reflect the I/O state of the network BIO passed to the QUIC SSL object. Instead they reflect the flow control state of the QUIC stream associated with the SSL object. Different energy entirely.
When used in nonblocking mode, SSL_ERROR_WANT_READ means the receive part of a QUIC stream doesn't currently have any more data available to be read, and SSL_ERROR_WANT_WRITE means the stream's internal buffer is full. π€
So how do you know if the QUIC implementation currently wants to be told about incoming network datagrams? Use the new SSL_net_read_desired(3) β¨ And likewise, to find out whether it currently wants to be informed when it's possible to transmit network datagrams, use the new SSL_net_write_desired(3). Only apps that want to manage their own event loops need these β check out APPLICATION-DRIVEN EVENT LOOPS for more on that.
- ALPN is mandatory when using QUIC. Attempts to connect without configuring ALPN will fail. No ALPN, no vibes. For how to configure it, see
SSL_set_alpn_protos(3). - Whether QUIC runs in client or server mode is decided by the
SSL_METHODyou use β not by calls toSSL_set_connect_state(3)orSSL_set_accept_state(3). You don't have to call either of them before connecting, but if you do call one, the function you called must be congruent with theSSL_METHODyou're using. Mismatch = mess. π SSL_set_min_proto_version(3)andSSL_set_max_proto_version(3)aren't used, and whatever values you pass them get ignored β because OpenSSL QUIC currently always uses TLS 1.3. Always. No negotiating. π- And here's the list of libssl functionality that is not available when used with QUIC:
- Async functionality
SSL_MODE_AUTO_RETRY- Record Padding and Fragmentation (
SSL_set_block_padding(3), etc.) SSL_stateless(3)support- SRTP functionality
- TLSv1.3 Early Data
- TLS Next Protocol Negotiation cannot be used and is superseded by ALPN, which must be used instead. The use of ALPN is mandatory with QUIC.
- Post-Handshake Client Authentication is not available as QUIC prohibits its use.
- QUIC requires the use of TLSv1.3 or later, therefore functionality only relevant to older TLS versions is not available.
- Some cipher suites which are generally available for TLSv1.3 are not available for QUIC, such as
TLS_AES_128_CCM_8_SHA256. Your application may need to adjust the list of acceptable cipher suites it passes to libssl. - CCM mode is not currently supported.
And there's also this next batch of libssl functionality that isn't available with QUIC either β but for these, calls to the relevant functions are just treated as no-ops (they quietly do nothing, like me when someone says "just read the docs"):
- Readahead (
SSL_set_read_ahead(3), etc.)
π CONSIDERATIONS FOR EXISTING APPLICATIONS π
okay so bestie, if you're an existing app trying to adopt QUIC, run through this list to figure out what changes you're gonna need. no cap, it's a whole checklist:
- A client application wishing to use QUIC must use
OSSL_QUIC_client_method(3)orOSSL_QUIC_client_thread_method(3)as its SSL method. For more information on the differences between these two methods, see THREAD ASSISTED MODE. π - A server application wishing to use QUIC must use
OSSL_QUIC_server_method(3). The server can then accept new connections withSSL_accept_connection(3). server era fr fr. - Determine how to provide QUIC with network access. Determine which of the below apply for your application:
- Your application uses
BIO_s_socket(3)to construct a BIO which is passed to the SSL object to provide it with network access. Changes needed: Change your application to useBIO_s_datagram(3)instead when using QUIC. The socket must be configured in nonblocking mode. You may or may not need to useSSL_set1_initial_peer_addr(3)to set the initial peer address; see the QUIC-SPECIFIC APIS section for details. π¦ - Your application uses
BIO_new_ssl_connect(3)to construct a BIO which is passed to the SSL object to provide it with network access. Changes needed: No changes needed. Use of QUIC is detected automatically and a datagram socket is created instead of a normal TCP socket. we love an auto-detect moment. - Your application uses any other I/O strategy in this list but combines it with
BIO_f_buffer(3), for example usingBIO_push(3). Changes needed: Disable the usage ofBIO_f_buffer(3)when using QUIC. Usage of such a buffer is incompatible with QUIC as QUIC requires datagram semantics in its interaction with the network. not me crying in buffered bytes π - Your application uses a BIO pair to cause the SSL object to read and write network traffic to a memory buffer. Your application manages the transmission and reception of buffered data itself in a way unknown to libssl. Changes needed: Switch from using a conventional BIO pair to using
BIO_s_dgram_pair(3)instead, which has the necessary datagram semantics. You will need to modify your application to transmit and receive using a UDP socket and to use datagram semantics when interacting with theBIO_s_dgram_pair(3)instance. okay bestie, time to touch grass and also UDP. - Your application uses a custom BIO method to provide the SSL object with network access. Changes needed: The custom BIO must be re-architected to have datagram semantics.
BIO_sendmmsg(3)andBIO_recvmmsg(3)must be implemented. These calls must operate in a nonblocking fashion. Optionally, implement theBIO_get_rpoll_descriptor(3)andBIO_get_wpoll_descriptor(3)methods if desired. Implementing these methods is required if blocking semantics at the SSL API level are desired. custom BIO? more like custom glow-up.
- Your application uses
- An application must explicitly configure whether it wishes to use the SSL APIs in blocking mode or not. Traditionally, an SSL object has automatically operated in blocking or nonblocking mode based on whether the underlying network BIO operates in blocking or nonblocking mode. QUIC requires the use of a nonblocking network BIO, therefore the blocking mode at the application level can be explicitly configured by the application using the new
SSL_set_blocking_mode(3)API. The default mode is blocking. If an application wishes to use the SSL object APIs at application level in a nonblocking manner, it must add a call toSSL_set_blocking_mode(3)to disable blocking mode. she's explicit, she sets boundaries. - If your client application does not choose to use thread assisted mode, it must ensure that it calls an I/O function on the SSL object (for example,
SSL_read(3)orSSL_write(3)), or the new functionSSL_handle_events(3), regularly. If the SSL object is used in blocking mode, an ongoing blocking call to an I/O function satisfies this requirement. This is required to ensure that timer events required by QUIC are handled in a timely fashion. β°
Most applications will service the SSL object by calling SSL_read(3) or SSL_write(3) regularly. If an application does not do this, it should ensure that SSL_handle_events(3) is called regularly.
SSL_get_event_timeout(3) can be used to determine when SSL_handle_events(3) must next be called.
If the SSL object is being used with an underlying network BIO which is pollable (such as BIO_s_datagram(3)), the application can use SSL_get_rpoll_descriptor(3), SSL_get_wpoll_descriptor(3) to obtain resources which can be used to determine when SSL_handle_events(3) should be called due to network I/O.
Client applications which use thread assisted mode do not need to be concerned with this requirement, as the QUIC implementation ensures timeout events are handled in a timely manner. See THREAD ASSISTED MODE for details.
- Ensure that your usage of
SSL_want(3),SSL_want_read(3)andSSL_want_write(3)reflects the API changes described in CHANGES TO EXISTING APIS. In particular, you should use these APIs to determine the ability of a QUIC stream to receive or provide application data, not to determine if network I/O is required. don't use them for the wrong job, bestie. - Evaluate your application's use of
SSL_shutdown(3)in light of the changes discussed in CHANGES TO EXISTING APIS. Depending on whether your application wishes to prioritise RFC conformance or rapid shutdown, consider using the newSSL_shutdown_ex(3)API instead. See QUIC-SPECIFIC APIS for details. choose your fighter.
β¨ RECOMMENDED USAGE IN NEW APPLICATIONS β¨
The recommended usage in new applications varies depending on three independent design decisions:
- Whether the application will use blocking or nonblocking I/O at the application level (configured using
SSL_set_blocking_mode(3)).
If the application does nonblocking I/O at the application level it can choose to manage its own polling and event loop; see APPLICATION-DRIVEN EVENT LOOPS. main character event loop energy.
- Whether the application intends to give the QUIC implementation direct access to a network socket (e.g. via
BIO_s_datagram(3)) or whether it intends to buffer transmitted and received datagrams via aBIO_s_dgram_pair(3)or custom BIO.
The former is preferred where possible as it reduces latency to the network, which enables QUIC to achieve higher performance and more accurate connection round trip time (RTT) estimation. lowkey the direct socket is the move.
- Whether thread assisted mode will be used (see THREAD ASSISTED MODE).
Simple demos for QUIC usage under these various scenarios can be found at https://github.com/openssl/openssl/tree/master/doc/designs/ddd.
Applications which wish to implement QUIC-specific protocols should be aware of the APIs listed under QUIC-SPECIFIC APIS which provide access to QUIC-specific functionality. For example, SSL_stream_conclude(3) can be used to indicate the end of the sending part of a stream, and SSL_shutdown_ex(3) can be used to provide a QUIC application error code when closing a connection. iykyk.
Regardless of the design decisions chosen above, it is recommended that new applications avoid use of the default stream mode and use the multi-stream API by calling SSL_set_default_stream_mode(3); see the MODES OF OPERATION section for details. periodt.
β¨ QUIC-SPECIFIC APIS β¨
okay so this whole section is the new-API tour π Some of these are directly about QUIC, some are only indirectly related, but bestie β for the actual deep-dive on how each one behaves, go read the man page it references. iykyk.
π SSL APIs that are brand new but matter for BOTH QUIC and DTLS
π SSL_get_event_timeout(3)
Tells you when the QUIC implementation should next be woken up via a call to SSL_handle_events(3) (or via some other I/O function, like SSL_read(3) or SSL_write(3) β whatever you're vibing with). And like, if ever.
plot twist: this works with DTLS too, and for any new code it supersedes DTLSv1_get_timeout(3). π
π SSL_handle_events(3)
This one is a non-specific I/O operation, and it makes a best effort attempt to perform any pending I/O or timeout processing. In practice it can advance the QUIC state machine by processing incoming network traffic, generating outgoing network traffic, and handling any expired timeout events. π₯
Here's the thing though β most other I/O functions on an SSL object, like SSL_read(3) and SSL_write(3), implicitly do event handling on the SSL object already. So you only need to call this function if you're not going to call any other I/O function. no cap.
Also valid with DTLS, and it supersedes DTLSv1_handle_timeout(3) for new usage. β¨
π¦ SSL APIs that are specific to QUIC
π«Ά SSL_new_listener(3)
Creates a listener SSL object. It differs from an ordinary SSL object in that it's used to provide an abstraction for the acceptance of network connections in a protocol-agnostic manner. very demure, very versatile.
Currently, listener SSL objects are only supported for QUIC server usage or client-only usage. And, the listener interface may expand to support additional protocols in the future. π
π«Ά SSL_new_listener_from(3)
Creates a listener SSL object which is subordinate to a QUIC domain SSL object ssl. Need the lore on QUIC domain SSL objects? See SSL_new_domain(3) and openssl-quic-concurrency(7). π¦
π«Ά SSL_is_listener(3)
Returns 1 if and only if an SSL object is a listener SSL object. that's it, that's the whole function.
π«Ά SSL_get0_listener(3)
Returns an SSL object pointer (potentially to the same object on which it's called) or NULL.
π«Ά SSL_listen(3)
Begin listening after a listener has been created. honestly you ordinarily don't need to call this yourself, because it gets called automatically on the first call to SSL_accept_connection(3). let it cook. π
π«Ά SSL_accept_connection(3)
Accepts a new incoming connection for a listener SSL object. A brand-new SSL object representing the accepted connection is created and returned on success. π
But if no incoming connection is available and the listener SSL object is configured in nonblocking mode, you get NULL back. shrug.
π«Ά SSL_get_accept_connection_queue_len(3)
Returns an informational value listing the number of connections waiting to be popped from the queue via calls to SSL_accept_connection(). just a lil headcount of who's waiting. π
π«Ά SSL_new_from_listener(3)
Creates a client connection under a given listener SSL object. For QUIC, you can also use SSL_new_from_listener() together with a listener which does accept incoming connections (i.e., one that was not created using SSL_LISTENER_FLAG_NO_ACCEPT) β which gets you a UDP network endpoint that has both incoming and outgoing connections. main character energy, both directions. π
π«Ά SSL_new_domain(3)
Creates a new QUIC event domain, represented as an SSL object. This is known as a QUIC domain SSL object. The whole concept of a QUIC event domain is discussed in detail in openssl-quic-concurrency(7).
π«Ά SSL_is_domain(3)
Returns 1 if an SSL object is a QUIC domain SSL object.
π«Ά SSL_get0_domain(3)
SSL_get0_domain() obtains a pointer to the QUIC domain SSL object in an SSL object hierarchy (if any). β¨
π«Ά SSL_set_blocking_mode(3), SSL_get_blocking_mode(3)
Configures whether blocking semantics are used at the application level. This is literally what decides whether calls to functions like SSL_read(3) and SSL_write(3) will block. choose your fighter. π
π«Ά SSL_get_rpoll_descriptor(3), SSL_get_wpoll_descriptor(3)
These functions facilitate operation in nonblocking mode. π«
When an SSL object is being used with an underlying network read BIO which supports polling, SSL_get_rpoll_descriptor(3) outputs an OS resource which can be used to synchronise on network readability events which should result in a call to SSL_handle_events(3). SSL_get_wpoll_descriptor(3) works in an analogous fashion for the underlying network write BIO.
β οΈ okay important: the poll descriptors these functions provide should be used only whenSSL_net_read_desired(3)andSSL_net_write_desired(3)return1, respectively. don't skip that vibe check. π
π«Ά SSL_net_read_desired(3), SSL_net_write_desired(3)
These functions facilitate operation in nonblocking mode and are used in conjunction with SSL_get_rpoll_descriptor(3) and SSL_get_wpoll_descriptor(3) respectively. They determine whether the respective poll descriptor is currently relevant for the purposes of polling. basically: are we doing this or not. π₯
π«Ά SSL_set1_initial_peer_addr(3)
This function can be used to set the initial peer address for an outgoing QUIC connection. In the general case, you must use this function when creating an outgoing QUIC connection β however, the correct initial peer address can be autodetected in some cases. See SSL_set1_initial_peer_addr(3) for details. π¦
π«Ά SSL_shutdown_ex(3)
This augments SSL_shutdown(3) by allowing an application error code to be specified. It also lets an application decide how quickly it wants a shutdown to be performed β potentially by trading off strict RFC compliance. yes, you can choose speed. π
π«Ά SSL_stream_conclude(3)
This allows an application to indicate the normal end of the sending part of a QUIC stream. This corresponds to the FIN flag in the QUIC RFC. And the receiving part of a stream remains usable. π
π«Ά SSL_stream_reset(3)
This allows an application to indicate the non-normal termination of the sending part of a stream. This corresponds to the RESET_STREAM frame in the QUIC RFC. not a graceful exit, and that's the point. π
π«Ά SSL_get_stream_write_state(3) and SSL_get_stream_read_state(3)
This allows an application to determine the current stream states for the sending and receiving parts of a stream respectively. π
π«Ά SSL_get_stream_write_error_code(3) and SSL_get_stream_read_error_code(3)
This allows an application to determine the application error code which was signalled by a peer which has performed a non-normal stream termination of the respective sending or receiving part of a stream, if any. if any!! π
π«Ά SSL_get_conn_close_info(3)
This allows an application to determine the error code which was signalled when the local or remote endpoint terminated the QUIC connection. rip to that connection. π
π«Ά SSL_get0_connection(3)
Gets the QUIC connection SSL object from a QUIC stream SSL object. climbing up the family tree. πΈ
π«Ά SSL_is_connection(3)
Returns 1 if an SSL object is not a QUIC stream SSL object. yes, it's the "not a stream" test, that's the whole bit.
π«Ά SSL_get_stream_type(3)
Provides information on the kind of QUIC stream which is attached to the SSL object. β¨
π«Ά SSL_get_stream_id(3)
Returns the QUIC stream ID which the QUIC protocol has associated with a QUIC stream. π¦
π«Ά SSL_new_stream(3)
Creates a new QUIC stream SSL object representing a new, locally-initiated QUIC stream. you started it, you own it. π
π«Ά SSL_accept_stream(3)
Potentially yields a new QUIC stream SSL object representing a new remotely-initiated QUIC stream β and it blocks until one is available, if the connection is configured to do so. β³
π«Ά SSL_get_accept_stream_queue_len(3)
Provides information on the number of pending remotely-initiated streams. the queue said π
π«Ά SSL_set_incoming_stream_policy(3)
Configures how incoming, remotely-initiated streams are handled. The incoming stream policy can be used to automatically reject streams created by the peer, or allow them to be handled using SSL_accept_stream(3). your streams, your rules. π
π«Ά SSL_set_default_stream_mode(3)
Used to configure or disable default stream mode; see the MODES OF OPERATION section for details.
π«Ά SSL_get_peer_addr(3)
Used to obtain the peer address from a QUIC connection or stream. π¬
πΈ BIO APIs (not QUIC-specific, but added to serve QUIC's needs)
The following BIO APIs are not specific to QUIC, but they've been added to facilitate QUIC-specific requirements and are closely associated with its use:
π BIO_s_dgram_pair(3)
This is a new BIO method which is similar to a conventional BIO pair but provides datagram semantics. classic BIO pair, now with datagrams. π
π BIO_get_rpoll_descriptor(3), BIO_get_wpoll_descriptor(3)
This is a new BIO API which allows a BIO to expose a poll descriptor. This API is used to implement the corresponding SSL APIs SSL_get_rpoll_descriptor(3) and SSL_get_wpoll_descriptor(3). β¨
π BIO_sendmmsg(3), BIO_recvmmsg(3)
This is a new BIO API which can be implemented by BIOs which implement datagram semantics. It is implemented by BIO_s_datagram(3) and BIO_s_dgram_pair(3). It is used by the QUIC implementation to send and receive UDP datagrams. π₯
π BIO_dgram_set_no_trunc(3), BIO_dgram_get_no_trunc(3)
By default, BIO_s_dgram_pair(3) has semantics comparable to those of Berkeley sockets being used with datagram semantics. This API allows an alternative mode to be enabled in which datagrams will not be silently truncated if they are too large. no more sneaky chop. π
π BIO_dgram_set_caps(3), BIO_dgram_get_caps(3)
These functions are used to allow the user of one end of a BIO_s_dgram_pair(3) to indicate its capabilities to the other end of a BIO_s_dgram_pair(3). In particular, this allows an application to inform the QUIC implementation of whether it is prepared to handle local and/or peer addresses in transmitted datagrams, and to provide the applicable information in received datagrams. communicating your capabilities, as one does. π
π BIO_dgram_get_local_addr_cap(3), BIO_dgram_set_local_addr_enable(3), BIO_dgram_get_local_addr_enable(3)
Local addressing support refers to the ability of a BIO with datagram semantics to allow a source address to be specified on transmission and to report the destination address on reception. These functions can be used to determine if a BIO can support local addressing, and to enable local addressing support if it can. π¦
π BIO_err_is_non_fatal(3)
This is used to determine if an error while calling BIO_sendmmsg(3) or BIO_recvmmsg(3) is ephemeral in nature β like "would block" errors. basically: is this error a big deal or are we just being impatient. π
π§΅ THREAD ASSISTED MODE π§΅
okay bestie so there's this optional mode for clients and it's literally called thread assisted mode, and you turn it on with OSSL_QUIC_client_thread_method(3). the way it works: a background thread just... spawns itself, automatically, no notes. and then OpenSSL's QUIC implementation is the one taking responsibility for making sure timeout events get handled on time β even if your app hasn't called any SSL I/O function like SSL_read(3) or SSL_write(3) for like, an embarrassingly long time. it just handles it. main character energy fr. π
all the locking you'd ever need is handled automatically internally, so you don't gotta think about it. but β and this is the important part β the thread safety guarantees for the public SSL API are unchanged. so if your app wants to make concurrent use of the public SSL APIs, you still have to do your own locking. no skipping that. π
and because this whole method relies on threads, it's simply not available on platforms where threading support isn't available, or where OpenSSL doesn't support it. sad but true. the tradeoff tho? it does give you the simplest mode of usage for an application. pick your fighter. β¨
also, the implementation may or may not use a common thread or a thread pool to service multiple SSL objects inside the same SSL_CTX. no promises either way.
π APPLICATION-DRIVEN EVENT LOOPS π
so OpenSSL's QUIC implementation is designed to help apps that wanna use the SSL APIs in a blocking fashion β but it's also designed to help apps that wanna use the SSL APIs in a nonblocking fashion and manage their own event loops and polling directly. this is chef's kiss when you wanna host OpenSSL's QUIC implementation right on top of your app's existing nonblocking I/O infrastructure. π«Ά
this all works via the concept of poll descriptors β go read BIO_get_rpoll_descriptor(3) for the details. broadly (iykyk), a BIO_POLL_DESCRIPTOR is a structure that expresses some kind of OS resource you can use to synchronise on I/O events. the QUIC implementation provides a BIO_POLL_DESCRIPTOR based on the poll descriptor provided by the underlying network BIO. typically that's an OS socket handle, but custom BIOs could totally choose to implement their own custom poll descriptor format. slay, customise away.
so basically, if your app wants to manage its own event loop, here's the vibe-check checklist for interacting with the SSL object:
- It should provide read and write BIOs with nonblocking datagram semantics to the SSL object using
SSL_set0_rbio(3)andSSL_set0_wbio(3). this could be a BIO abstracting a network socket likeBIO_s_datagram(3), or a BIO abstracting some kind of memory buffer likeBIO_s_dgram_pair(3). using a custom BIO is also possible. options on options. π - It should configure the SSL object into nonblocking mode by calling
SSL_set_blocking_mode(3). - It should configure the SSL object as desired, set an initial peer as needed using
SSL_set1_initial_peer_addr(3), and trigger the connection process by callingSSL_connect(3). - If the network read and write BIOs provided were pollable (for example, a
BIO_s_datagram(3), or a custom BIO which implementsBIO_get_rpoll_descriptor(3)andBIO_get_wpoll_descriptor(3)), it should perform the following steps repeatedly:- The application should call
SSL_get_rpoll_descriptor(3)andSSL_get_wpoll_descriptor(3)to identify OS resources which can be used for synchronisation. - It should call
SSL_net_read_desired(3)andSSL_net_write_desired(3)to determine whether the QUIC implementation is currently interested in readability and writability events on the underlying network BIO which was provided, and callSSL_get_event_timeout(3)to determine if any timeout event will become applicable in the future. - It should wait until one of the following events occurs:
- The poll descriptor returned by
SSL_get_rpoll_descriptor(3)becomes readable (ifSSL_net_read_desired(3)returned 1); - The poll descriptor returned by
SSL_get_wpoll_descriptor(3)becomes writable (ifSSL_net_write_desired(3)returned 1); - The timeout returned by
SSL_get_event_timeout(3)(if any) expires.
- The poll descriptor returned by
- The application should call
Once any of these events occurs, SSL_handle_events(3) should be called.
- If the network read and write BIOs provided were not pollable (for example, in the case of
BIO_s_dgram_pair(3)), the application is responsible for managing and synchronising network I/O. It should callSSL_handle_events(3)after it writes data to aBIO_s_dgram_pair(3)or otherwise takes action so that the QUIC implementation can read new datagrams via a call toBIO_recvmmsg(3)on the underlying network BIO. The QUIC implementation may output datagrams via a call toBIO_sendmmsg(3)and the application is responsible for ensuring these are transmitted.
The application must call SSL_get_event_timeout(3) after every call to SSL_handle_events(3) (or another I/O function on the SSL object), and ensure that a call to SSL_handle_events(3) is performed after the specified timeout (if any).
β οΈ bestie. do not sleep on that last one. theSSL_get_event_timeout(3)-after-every-SSL_handle_events(3)thing is not a suggestion, it's a must. algorithmically scheduled, periodt.
πͺ WINDOWS APPLICATION NOTES πͺ
QUIC protocol uses UDP sockets, okay? and the recvfrom() function on Windows may fail with a "WSAECONNRESET" error β which causes the OpenSSL QUIC stack to enter permanent error, which prevents further communication over QUIC protocol. yes, permanently. the whole thing just stops. π Applications should disable SIO_UDP_CONNRESET and SIO_UDP_NETRESET error notification on UDP sockets they pass to the OpenSSL QUIC stack. more details can be found here: https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls#sio_udp_connreset-opcode-setting-i-t3
OpenSSL attempts to always disable SIO_UDP_CONNRESET and SIO_UDP_NETRESET on UDP sockets it receives from application, but no error is reported back if the respective WSAIoctl() calls fail. so a robust application should set those options itself so it can handle error notifications from WSAIoctl() properly. do it yourself if you want the receipts. π
π SEE ALSO π
SSL_handle_events(3)SSL_get_event_timeout(3)SSL_net_read_desired(3)SSL_net_write_desired(3)SSL_get_rpoll_descriptor(3)SSL_get_wpoll_descriptor(3)SSL_set_blocking_mode(3)SSL_shutdown_ex(3)SSL_set1_initial_peer_addr(3)SSL_stream_conclude(3)SSL_stream_reset(3)SSL_get_stream_read_state(3)SSL_get_stream_read_error_code(3)SSL_get_conn_close_info(3)SSL_get0_connection(3)SSL_get_stream_type(3)SSL_get_stream_id(3)SSL_new_stream(3)SSL_accept_stream(3)SSL_set_incoming_stream_policy(3)SSL_set_default_stream_mode(3)SSL_new_listener(3)SSL_new_listener_from(3)SSL_is_listener(3)SSL_get0_listener(3)SSL_listen(3)SSL_accept_connection(3)SSL_get_accept_connection_queue_len(3)SSL_new_domain(3)SSL_is_domain(3)SSL_get0_domain(3)
Β©οΈ COPYRIGHT Β©οΈ
Copyright 2022-2026 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, we did it πβ¨ the source is from OpenSSL and this version is 4.0.3, with a date of 2026-09-29 β OPENSSL-QUIC(7SSL), mwah. go forth and multiplex responsibly ππ¦πΈ
π¦ where this page comes from
- ubuntu stonking: openssl 4.0.3-1ubuntu1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms