OPENSSL-QUIC(7ossl)

πŸ’– openssl-quic(7ossl) β€” the OpenSSL QUIC page πŸ’–

🌸 NAME 🌸

openssl-quic β€” OpenSSL QUIC. That's the name, bestie, short and iconic. πŸ’…

✨ DESCRIPTION ✨

Okay so OpenSSL 3.2 and later has support for the QUIC transport protocol β€” no cap. You can use OpenSSL's QUIC capabilities for both client and server applications. This page is the how-to for letting applications use the QUIC protocol through the libssl API. πŸ¦‹

The QUIC protocol maps to the standard SSL API. A QUIC connection is represented by an SSL object in the same way that a TLS connection is. Existing applications that use the libssl API only need minimal changes to bring QUIC protocol support in. QUIC clients can use OSSL_QUIC_client_method(3) or OSSL_QUIC_client_thread_method(3) with SSL_CTX_new(3). See below for more details about the difference between the two. For servers, there is only one option: SSL method OSSL_QUIC_server_method(3) with SSL_CTX_new(3). πŸ‘‘

The remainder of this man page discusses, in order:

  • Default stream mode versus multi-stream mode for clients;
  • The changes to existing libssl APIs which are driven by QUIC-related implementation requirements, which existing applications should bear 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 πŸ¦‹

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. Whether the stream is client-initiated or server-initiated from a QUIC perspective depends on whether SSL_read(3) or SSL_write(3) is called first. 🌈

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. See the RECOMMENDATIONS FOR NEW APPLICATIONS section for more details. πŸŽ€

πŸŽ€ 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. 🫢

In default stream mode, a stream is implicitly created and bound to the QUIC connection SSL object; SSL_read(3) and SSL_write(3) calls to the QUIC connection SSL object work by default and are mapped to that stream. ✨

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, in which 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. 😭

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. 🧚

It is possible to use additional streams in default stream mode using SSL_new_stream(3) and SSL_accept_stream(3); note that 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. πŸ”₯

Calling SSL_new_stream(3) or SSL_accept_stream(3) before a default stream has been associated with the QUIC connection SSL object will inhibit future creation of a default stream. πŸ’€

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

To use multi-stream mode, call SSL_set_default_stream_mode(3) with an argument of SSL_DEFAULT_STREAM_MODE_NONE; this function must be called prior to initiating the connection. The default stream mode cannot be changed after initiating a connection. πŸ’œ

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. πŸ’€

πŸ’… CHANGES TO EXISTING APIS πŸ¦‹

okay so here's the tea: most of the SSL APIs you already know and love β€” like SSL_read(3) and SSL_write(3) β€” work exactly like they do for TLS connections, no cap. Same semantics, no notes. But there are some exceptions, and the changes to how these existing APIs behave go a lil something like this:

  • since QUIC rides on UDP, SSL_set_bio(3), SSL_set0_rbio(3) and SSL_set0_wbio(3) still function as before, but they must now receive a BIO with datagram semantics. There are broadly four options for apps to use as a network BIO:
    • BIO_s_datagram(3) β€” recommended for most applications, and it replaces BIO_s_socket(3) while providing a UDP socket. ✨
    • BIO_s_dgram_pair(3) β€” gives you BIO pair-like functionality but with datagram semantics, and it's the recommended pick for existing applications which 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, but with datagram semantics. Heads up tho: unlike BIO_s_dgram_pair(3), it is unidirectional. 🫢
    • an application may also choose to implement a custom BIO β€” but then the new BIO_sendmmsg(3) and BIO_recvmmsg(3) APIs must be supported. no exceptions bestie.
  • SSL_set_fd(3), SSL_set_rfd(3) and SSL_set_wfd(3) traditionally instantiate a BIO_s_socket(3). For QUIC, these functions instead instantiate a BIO_s_datagram(3). Which is equivalent to instantiating a BIO_s_datagram(3) and using SSL_set0_rbio(3) and SSL_set0_wbio(3). slay, less work for you πŸ’…
  • traditionally, whether the application-level I/O APIs (such as SSL_read(3) and SSL_write(3)) operated in a blocking fashion was directly correlated with whether the underlying network socket was configured in a blocking fashion. That is no longer the case 😭 β€” applications must explicitly configure the desired application-level blocking mode using SSL_set_blocking_mode(3). See SSL_set_blocking_mode(3) for the deets.
  • network-level I/O must always be performed in a nonblocking manner, periodt. The application can still enjoy blocking semantics for calls to application-level I/O functions such as SSL_read(3) and SSL_write(3), but the underlying network BIO provided to QUIC (such as a BIO_s_datagram(3)) must be configured in nonblocking mode. For application-level blocking functionality, go look at SSL_set_blocking_mode(3).
  • BIO_new_ssl_connect(3) has been changed to automatically use a BIO_s_datagram(3) when used with QUIC, so if your app uses this you literally don't need to change the BIO you use. iconic πŸ’–
  • BIO_new_buffer_ssl_connect(3) cannot be used with QUIC, and applications must change to use BIO_new_ssl_connect(3) instead. πŸ₯²
  • SSL_shutdown(3) has significant changes in relation to how QUIC connections must be shut down. In particular, applications should be advised that the full RFC-conformant QUIC shutdown process may take an extended amount of time. This may not be suitable for short-lived processes which should exit immediately after their usage of a QUIC connection is completed. A rapid shutdown mode is available for such applications. For details, see SSL_shutdown(3).
bestie please read that shutdown one twice. the full RFC-conformant path is not the vibe for a fire-and-exit process. πŸ•°οΈ
  • SSL_want(3), SSL_want_read(3) and SSL_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.

When used in nonblocking mode, SSL_ERROR_WANT_READ indicates that the receive part of a QUIC stream does not currently have any more data available to be read, and SSL_ERROR_WANT_WRITE indicates that the stream's internal buffer is full.

To determine if the QUIC implementation currently wishes to be informed of incoming network datagrams, use the new function SSL_net_read_desired(3); likewise, to determine if the QUIC implementation currently wishes to be informed when it is possible to transmit network datagrams, use the new function SSL_net_write_desired(3). Only applications which wish to manage their own event loops need to use these functions; see APPLICATION-DRIVEN EVENT LOOPS for further discussion. πŸ‘‘

  • the use of ALPN is mandatory when using QUIC. Attempts to connect without configuring ALPN will fail, fr fr. For information on how to configure ALPN, see SSL_set_alpn_protos(3).
  • whether QUIC operates in a client or server mode is determined by the SSL_METHOD used, rather than by calls to SSL_set_connect_state(3) or SSL_set_accept_state(3). It is not necessary to call either of SSL_set_connect_state(3) or SSL_set_accept_state(3) before connecting, but if either of these are called, the function called must be congruent with the SSL_METHOD being used. consistency is key πŸŽ€
  • the SSL_set_min_proto_version(3) and SSL_set_max_proto_version(3) APIs are not used and the values passed to them are ignored, as OpenSSL QUIC currently always uses TLS 1.3. so don't stress about those numbers, they're just vibes now πŸ™ˆ
  • the following libssl functionality 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.

The following libssl functionality is also not available when used with QUIC, but calls to the relevant functions are treated as no-ops β€” so they won't blow up, they just do absolutely nothing. main character energy of a function, truly πŸ’€

πŸ’… CONSIDERATIONS FOR EXISTING APPLICATIONS πŸ’…

Okay so if you've already got an app and you're thinking β€œcan I make this QUIC?” β€” yes bestie, but run through this checklist first. Here's the whole vibe check for what changes you might need to make:

  • A client app that wants to use QUIC must use OSSL_QUIC_client_method(3) or OSSL_QUIC_client_thread_method(3) as its SSL method. For the tea on how those two methods differ, see THREAD ASSISTED MODE.
  • A server app that wants to use QUIC must use OSSL_QUIC_server_method(3). Then the server can accept new connections with SSL_accept_connection(3). Slay.
  • Figure out how you're giving QUIC network access. Which of these is your app doing?
    • Your app uses BIO_s_socket(3) to construct a BIO that gets passed to the SSL object to provide it with network access.
      • Changes needed: switch your app to use BIO_s_datagram(3) instead when using QUIC. The socket must be configured in nonblocking mode. You may or may not need to use SSL_set1_initial_peer_addr(3) to set the initial peer address β€” see the QUIC-SPECIFIC APIS section for the details.
    • Your app uses BIO_new_ssl_connect(3) to construct a BIO that gets 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. Iconic.
    • Your app uses any other I/O strategy in this list but combines it with a BIO_f_buffer(3), for example using BIO_push(3).
      • Changes needed: disable the usage of BIO_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.
    • Your app uses a BIO pair to cause the SSL object to read and write network traffic to a memory buffer. Your app 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 app to transmit and receive using a UDP socket and to use datagram semantics when interacting with the BIO_s_dgram_pair(3) instance.
    • Your app uses a custom BIO method to provide the SSL object with network access.
  • 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 to SSL_set_blocking_mode(3) to disable blocking mode.
  • 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) or SSL_write(3)), or the new function SSL_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.
  • Ensure that your usage of SSL_want(3), SSL_want_read(3) and SSL_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.
  • 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 new SSL_shutdown_ex(3) API instead. See QUIC-SPECIFIC APIS for details.

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.
  • 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 a BIO_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.
  • 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.

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.

✨ QUIC-SPECIFIC APIS ✨

Okay bestie, this is the section where we meet the new APIs β€” some are directly about QUIC, some are more "related by vibes." For the full deets on how each one actually behaves, go read the man page it points to, mmkay? πŸ’…

🌸 The SSL APIs that are new but relevant to BOTH QUIC and DTLS

  • SSL_get_event_timeout(3) β€” figures out when the QUIC implementation should next be woken up, if ever β€” via a call to SSL_handle_events(3) or some other I/O function like SSL_read(3) or SSL_write(3). It also works with DTLS, and for new usage it supersedes DTLSv1_get_timeout(3). ⏰
  • SSL_handle_events(3) β€” a non-specific I/O operation that makes a best effort attempt to do any pending I/O or timeout processing. Use it to advance the QUIC state machine: process incoming network traffic, generate outgoing network traffic, and handle any expired timeout events. 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 one if no other I/O function is going to be called. It also works with DTLS, and for new usage it supersedes DTLSv1_handle_timeout(3). 🫢

πŸ¦‹ The SSL APIs that are specific to QUIC

  • SSL_new_listener(3) β€” creates a listener SSL object. It's not like an ordinary SSL object: it's the abstraction for accepting network connections in a protocol-agnostic way. Right now, listener SSL objects are only supported for QUIC server usage or client-only usage. The listener interface may expand to support more protocols later, so watch this space. πŸ‘€
  • SSL_new_listener_from(3) β€” creates a listener SSL object that is subordinate to a QUIC domain SSL object ssl. Wanna know about 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 job. βœ…
  • SSL_get0_listener(3) β€” returns an SSL object pointer (potentially to the same object it was called on) or NULL.
  • SSL_listen(3) β€” starts listening after a listener has been created. You ordinarily don't need to call this, because it gets called automatically on the first call to SSL_accept_connection(3). Iconic lazy-loader behaviour, honestly. ✨
  • SSL_accept_connection(3) β€” accepts a new incoming connection for a listener SSL object. On success, a brand new SSL object representing the accepted connection is created and returned. If there's no incoming connection available and the listener SSL object is in nonblocking mode, you get NULL back. πŸŽ€
  • SSL_get_accept_connection_queue_len(3) β€” an informational value: how many connections are chilling in the queue waiting to be popped off via calls to SSL_accept_connection(). πŸ“
  • 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() with a listener that does accept incoming connections (i.e. one that wasn't created using SSL_LISTENER_FLAG_NO_ACCEPT) β€” which gives you a UDP network endpoint with both incoming and outgoing connections. Best of both worlds, fr fr. πŸ”₯
  • SSL_new_domain(3) β€” creates a new QUIC event domain, represented as an SSL object. Yep, that's what a QUIC domain SSL object is. The whole concept of a QUIC event domain is explained 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 there is one. πŸ”
  • SSL_set_blocking_mode(3), SSL_get_blocking_mode(3) β€” configure whether blocking semantics are used at the application level. This decides whether calls to functions like SSL_read(3) and SSL_write(3) will block. 😴
  • SSL_get_rpoll_descriptor(3), SSL_get_wpoll_descriptor(3) β€” these facilitate operation in nonblocking mode. When an SSL object is used with an underlying network read BIO that supports polling, SSL_get_rpoll_descriptor(3) outputs an OS resource you can synchronise on for network readability events that should result in a call to SSL_handle_events(3). SSL_get_wpoll_descriptor(3) does the analogous thing for the underlying network write BIO.
Heads up bestie: only use the poll descriptors these functions give you when SSL_net_read_desired(3) and SSL_net_write_desired(3) return 1, respectively. Don't be out here using them out of turn. πŸ™ˆ
  • SSL_net_read_desired(3), SSL_net_write_desired(3) β€” these also facilitate operation in nonblocking mode, and they're used together with SSL_get_rpoll_descriptor(3) and SSL_get_wpoll_descriptor(3) respectively. They tell you whether the respective poll descriptor is currently relevant for polling purposes.
  • SSL_set1_initial_peer_addr(3) β€” sets the initial peer address for an outgoing QUIC connection. In the general case you must use this when creating an outgoing QUIC connection β€” but in some cases the correct initial peer address can be autodetected. See SSL_set1_initial_peer_addr(3) for the details. πŸ“
  • SSL_shutdown_ex(3) β€” this is SSL_shutdown(3) but with a glow-up: it lets an application error code be specified. It also lets an application decide how quickly it wants the shutdown performed, potentially by trading off strict RFC compliance. Speed vs. rules, your pick. ⚑
  • SSL_stream_conclude(3) β€” lets an application indicate the normal end of the sending part of a QUIC stream. This corresponds to the FIN flag in the QUIC RFC. Good news: the receiving part of the stream stays usable. πŸ’–
  • SSL_stream_reset(3) β€” lets an application indicate the non-normal termination of the sending part of a stream. This corresponds to the RESET_STREAM frame in the QUIC RFC. πŸ’€
  • SSL_get_stream_write_state(3) and SSL_get_stream_read_state(3) β€” let an application 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) β€” let an application determine the application error code signalled by a peer that performed a non-normal stream termination of the respective sending or receiving part of a stream, if any.
  • SSL_get_conn_close_info(3) β€” lets an application determine the error code that was signalled when the local or remote endpoint terminated the QUIC connection. πŸšͺ
  • SSL_get0_connection(3) β€” gets the QUIC connection SSL object from a QUIC stream SSL object.
  • SSL_is_connection(3) β€” returns 1 if an SSL object is not a QUIC stream SSL object.
  • SSL_get_stream_type(3) β€” gives you info on the kind of QUIC stream attached to the SSL object. 🏷️
  • SSL_get_stream_id(3) β€” returns the QUIC stream ID that 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. 🌱
  • SSL_accept_stream(3) β€” potentially yields a new QUIC stream SSL object representing a new remotely-initiated QUIC stream β€” blocking until one is available if the connection is configured to do so. ⏳
  • SSL_get_accept_stream_queue_len(3) β€” info on the number of pending remotely-initiated streams. πŸ“Š
  • SSL_set_incoming_stream_policy(3) β€” configures how incoming, remotely-initiated streams are handled. The incoming stream policy can auto-reject streams created by the peer, or allow them to be handled using SSL_accept_stream(3). βœ‹
  • SSL_set_default_stream_mode(3) β€” used to configure or disable default stream mode; see the MODES OF OPERATION section for the deets.

🧚 The BIO APIs (not QUIC-specific, but added for QUIC reasons)

These aren't specific to QUIC, but they were added to facilitate QUIC-specific requirements and are closely associated with its use. 🫢

🧡 THREAD ASSISTED MODE 🧡

ok so bestie, if you're a client and you want the laziest possible life, this is your moment. The optional thread assisted mode is the one you unlock with OSSL_QUIC_client_thread_method(3). In this mode a background thread just... spawns itself automatically. no cap. And then OpenSSL's QUIC implementation takes on the job of making sure timeout events get handled on time β€” even if your app hasn't called an SSL I/O function like SSL_read(3) or SSL_write(3) in literal ages. it's giving personal assistant energy πŸ’…

All the locking you need is handled automatically, internally, by OpenSSL itself. BUT β€” and this is the important part, don't come for me β€” the thread safety guarantees for the public SSL API are completely unchanged. So if your application wants to make concurrent use of the public SSL APIs, you still have to do your own locking. yes, still. sorry. 😭

And because this whole method relies on threads, it is not available on platforms where threading support isn't available, or where OpenSSL doesn't support it. So do a vibe check on your platform first. That said, it does give you the simplest mode of usage for an application. slay.

One little detail, ngl: the implementation may or may not use a common thread or a thread pool to service multiple SSL objects in the same SSL_CTX. So don't count on one or the other.


πŸ”„ APPLICATION-DRIVEN EVENT LOOPS πŸ”„

Okay so OpenSSL's QUIC implementation was designed to make two very different kinds of apps happy:

  • the ones that want to use the SSL APIs in a blocking fashion (super chill, no event loop of their own), and
  • the ones that want to use the SSL APIs in a nonblocking fashion and manage their own event loops and polling directly.

That second one is chef's kiss if you want to host OpenSSL's QUIC implementation on top of nonblocking I/O infrastructure you already have. you know, the stuff that already works. iconic.

This is supported via the concept of poll descriptors β€” see BIO_get_rpoll_descriptor(3) for the details. Broadly, a BIO_POLL_DESCRIPTOR is a structure that expresses some kind of OS resource which can be used to synchronise on I/O events. The QUIC implementation hands you a BIO_POLL_DESCRIPTOR based on the poll descriptor provided by the underlying network BIO. Typically that's an OS socket handle, though custom BIOs could choose to implement their own custom poll descriptor format. do your thing, custom BIOs ✨

So broadly, an application that wishes to manage its own event loop should interact with the SSL object like this:

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 call SSL_handle_events(3) after it writes data to a BIO_s_dgram_pair(3) or otherwise takes action so that the QUIC implementation can read new datagrams via a call to BIO_recvmmsg(3) on the underlying network BIO. The QUIC implementation may output datagrams via a call to BIO_sendmmsg(3) and the application is responsible for ensuring these are transmitted. you're the courier now, bestie 🫢

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).

like, genuinely, "after every call" β€” that's not a suggestion, that's the whole assignment. don't skip it or the timeouts get sad 😭

πŸͺŸ WINDOWS APPLICATION NOTES πŸͺŸ

Windows besties, this one's for you specifically. The QUIC protocol uses UDP sockets, and the recvfrom() function on Windows may fail with a "WSAECONNRESET" error. When that happens it can cause the OpenSSL QUIC stack to enter a permanent error state, which prevents any further communication over the QUIC protocol. like, permanently permanent. πŸ’€

So 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 the application β€” but no error is reported back if the respective WSAIoctl() calls fail. Sneaky, right? So a robust application should set those options itself, so it can handle error notifications from WSAIoctl() properly. be your own hero πŸ’…


πŸ‘€ 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)

(yes, that's the whole friend group. go say hi. πŸ¦‹)


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>.


okay that's the whole page, we ate and left no crumbs πŸ‘‘ this lil number is version 3.5.9, dated 2026-09-29, and it lives at OPENSSL-QUIC(7ossl) β€” go forth and QUIC responsibly, bestie πŸ’œπŸŒˆ

πŸ“¦ where this page comes from