OSSL-GUIDE-QUIC-SERVER-BLOCK(7ossl)
π NAME π
ossl-guide-quic-server-block(7ossl) β OpenSSL Guide: Writing a simple blocking QUIC server β¨
πΈ SIMPLE BLOCKING QUIC SERVER EXAMPLE πΈ
Okay so this page is literally gonna present various source code samples showing how to write a simple, non-concurrent, QUIC "echo" server application which accepts one client connection at a time, echoing input from the client back to the same client. Once the current client disconnects, the next client connection is accepted. Main character server energy, fr fr. π¦
Heads up, bestie: the server only accepts HTTP/1.0 requests, which is non-standard and will not be supported by real world servers. This is for demonstration purposes only. No cap.
Both the accepting socket and client connections are "blocking". A more typical server might use nonblocking sockets with an event loop and callbacks for I/O events. iykyk π
The complete source code for this example blocking QUIC server is available in the demos/guide directory of the OpenSSL source distribution in the file quic-server-block.c. It is also available online at https://github.com/openssl/openssl/blob/master/demos/guide/quic-server-block.c. Slay. π
We assume that you already have OpenSSL installed on your system; that you already have some fundamental understanding of OpenSSL concepts and QUIC (see ossl-guide-libraries-introduction(7) and ossl-guide-quic-introduction(7)); and that you know how to write and build C code and link it against the libcrypto and libssl libraries that are provided by OpenSSL. It also assumes that you have a basic understanding of UDP/IP and sockets. Lowkey important. πΈ
π Creating the SSL_CTX and SSL objects π
The first step is to create an SSL_CTX object for our server. We use the SSL_CTX_new(3) function for this purpose. We pass as an argument the return value of the function OSSL_QUIC_server_method(3). You should use this method whenever you are writing a QUIC server. Periodt. π
/*
* An SSL_CTX holds shared configuration information for multiple
* subsequent per-client SSL connections. We specifically load a QUIC
* server method here.
*/
ctx = SSL_CTX_new(OSSL_QUIC_server_method());
if (ctx == NULL)
goto err;
Servers need a private key and certificate. Intermediate issuer CA certificates are often required, and both the server (end-entity or EE) certificate and the issuer ("chain") certificates are most easily configured in a single "chain file". Below we load such a chain file (the EE certificate must appear first), and then load the corresponding private key, checking that it matches the server certificate. No checks are performed to check the integrity of the chain (CA signatures or certificate expiration dates, for example), but we do verify the consistency of the private key with the corresponding certificate. Read that twice, bestie. π«Ά
/*
* Load the server's certificate *chain* file (PEM format), which includes
* not only the leaf (end-entity) server certificate, but also any
* intermediate issuer-CA certificates. The leaf certificate must be the
* first certificate in the file.
*
* In advanced use-cases this can be called multiple times, once per public
* key algorithm for which the server has a corresponding certificate.
* However, the corresponding private key (see below) must be loaded first,
* *before* moving on to the next chain file.
*/
if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) {
fprintf(stderr, "couldn't load certificate file: %s\n", cert_path);
goto err;
}
/*
* Load the corresponding private key, this also checks that the private
* key matches the just loaded end-entity certificate. It does not check
* whether the certificate chain is valid, the certificates could be
* expired, or may otherwise fail to form a chain that a client can
* validate.
*/
if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
fprintf(stderr, "couldn't load key file: %s\n", key_path);
goto err;
}
Most servers, including this one, do not solicit client certificates. We therefore do not need a "trust store" and allow the handshake to complete even when the client does not present a certificate. Note: Even if a client did present a trusted certificate, for it to be useful, the server application would still need custom code to use the verified identity to grant nondefault access to that particular client. Some servers grant access to all clients with certificates from a private CA, this then requires processing of certificate revocation lists to deauthorise a client. It is often simpler and more secure to instead keep a list of authorised public keys. Secure vibes only. π₯
Though this is the default setting, we explicitly call the SSL_CTX_set_verify(3) function and pass the SSL_VERIFY_NONE value to it. The final argument to this function is a callback that you can optionally supply to override the default handling for certificate verification. Most applications do not need to do this so this can safely be set to NULL to get the default handling. Easy. β¨
/*
* Clients rarely employ certificate-based authentication, and so we don't
* require "mutual" TLS authentication (indeed there's no way to know
* whether or how the client authenticated the server, so the term "mutual"
* is potentially misleading).
*
* Since we're not soliciting or processing client certificates, we don't
* need to configure a trusted-certificate store, so no call to
* SSL_CTX_set_default_verify_paths() is needed. The server's own
* certificate chain is assumed valid.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
QUIC also dictates using Application-Layer Protocol Negotiation (ALPN) to select an application protocol. We use SSL_CTX_set_alpn_select_cb(3) for this purpose. We can pass a callback which will be called for each connection to select an ALPN the server considers acceptable. Obsessed with ALPN actually. π
/* Setup ALPN negotiation callback to decide which ALPN is accepted. */
SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
In this case, we only accept "http/1.0" and "hq-interop". Two choices, no drama. π¦
/*
* ALPN strings for TLS handshake. Only 'http/1.0' and 'hq-interop'
* are accepted.
*/
static const unsigned char alpn_ossltest[] = {
8, 'h', 't', 't', 'p', '/', '1', '.', '0',
10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p',
};
static int select_alpn(SSL *ssl, const unsigned char **out,
unsigned char *out_len, const unsigned char *in,
unsigned int in_len, void *arg)
{
if (SSL_select_next_proto((unsigned char **)out, out_len, alpn_ossltest,
sizeof(alpn_ossltest), in,
in_len) == OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_OK;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
That is all the setup that we need to do for the SSL_CTX. Next, we create a UDP socket and bind to it on localhost. Socket time. π§
/* Retrieve the file descriptor for a new UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
fprintf(stderr, "cannot create socket");
goto err;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
/* Bind to the new UDP socket on localhost */
if (bind(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) {
fprintf(stderr, "cannot bind to %u\n", port);
BIO_closesocket(fd);
goto err;
}
To run the QUIC server, we create an SSL_LISTENER to listen for incoming connections. We provide it with the bound UDP port to then explicitly begin listening for new connections. Listen up, literally. π
/*
* Create a new QUIC listener. Listeners, and other QUIC objects, default
* to operating in blocking mode. The configured behaviour is inherited by
* child objects.
*/
if ((listener = SSL_new_listener(ctx, 0)) == NULL) {
goto err;
}
/* Provide the listener with our UDP socket. */
if (!SSL_set_fd(listener, fd))
goto err;
/* Begin listening. */
if (!SSL_listen(listener))
goto err;
π Server loop π
The server now enters a "forever" loop, handling one client connection at a time. Before each connection, we clear the OpenSSL error stack so that any error reports are related to just the new connection. Fresh start, fresh stack. β¨
/* Pristine error stack for each new connection */
ERR_clear_error();
At this point, the server blocks to accept the next client. SSL_accept_connection(3) will return an accepted connection within a fresh SSL, in which the handshake will have already occurred. Blocking but make it iconic. π
/* Block while waiting for a client connection */
conn = SSL_accept_connection(listener, 0);
if (conn == NULL) {
fprintf(stderr, "error while accepting connection\n");
goto err;
}
With the handshake complete, the server echoes client input back to the client in a loop. Echo echo echo. π
while (SSL_read_ex(conn, buf, sizeof(buf), &nread) > 0) {
if (SSL_write_ex(conn, buf, nread, &nwritten) > 0 &&
nwritten == nread) {
continue;
}
fprintf(stderr, "Error echoing client input");
break;
}
Once the client closes its connection, we signal the end of the stream by using SSL_stream_conclude(3). This will send a final Finished packet to the client. Final packet energy. π
/* Signal the end of the stream. */
if (SSL_stream_conclude(conn, 0) != 1) {
fprintf(stderr, "Unable to conclude stream\n");
SSL_free(conn);
goto err;
}
We then shut down the connection with SSL_shutdown_ex(3), which may need to be called multiple times to ensure the connection is shutdown completely. Multiple times, bestie β don't skip. π
while (SSL_shutdown_ex(conn, 0, &shutdown_args,
sizeof(SSL_SHUTDOWN_EX_ARGS)) != 1) {
fprintf(stderr, "Re-attempting SSL shutdown\n");
}
Finally, we free the SSL connection, and the server is now ready to accept the next client connection. Clean. π§Ή
SSL_free(conn);
π§Ή Final clean up π§Ή
If the server somehow manages to break out of the infinite loop and be ready to exit, it would deallocate the constructed SSL. Cleanup arc. π«Ά
SSL_free(listener);
And in the main function, it would deallocate the constructed SSL_CTX. Last little cleanup slay. π
SSL_CTX_free(ctx);
BIO_closesocket(fd);
res = EXIT_SUCCESS;
return res;
β¨ SEE ALSO β¨
okay bestie, you ate that example, and now you're probably like "what else should I read??" β good news, there's a whole shelf of these, and they all lowkey slap π
ossl-guide-introduction(7)β the starting point, the origin story, the main character entrance πΈossl-guide-libraries-introduction(7)β meet the libraries themselves, they're iconic πossl-guide-libssl-introduction(7)β all aboutlibsslspecifically, the one doing the heavy lifting for you πͺossl-guide-quic-introduction(7)β QUIC 101, read this if you want the whole vibe explained properly πossl-guide-quic-client-non-block(7)β the client side, but make it async, no waiting around π¦ossl-guide-quic-client-block(7)β the same client energy, just blocking this time, very chill πossl-guide-tls-server-block(7)β classic TLS server, the OG, still slaying πossl-guide-quic-server-non-block(7)β the non-blocking sibling of the page you just read, if you're feeling spicy π₯
fr fr, if you only click one of these, make it ossl-guide-quic-introduction(7) β it's giving "prerequisite knowledge" and it will make everything else hit different β¨
π COPYRIGHT π
so like, legal stuff, but make it cute π here's the deal:
Copyright 2024-2025 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>.
ngl bestie, "except in compliance with the License" is doing a lot of work in that sentence β it's the polite lawyer way of saying read the license, don't just vibe π
and that's the whole page, babes π«Ά you made it to the end, which honestly deserves a little award π
this gorgeous thing is stamped with OpenSSL 3.5.0, with the date sitting at 2026-09-2β¦ in the footer (the terminal clearly got shy and cut it off, not me π). anyway β go build your blocking QUIC server, you're gonna be so good at it β¨π
π¦ where this page comes from
- opensuse-leap 16.0: openssl-3 3.5.0-160000.11.1 Β· Apache-2.0
- opensuse-leap 16.0: openssl-3 3.5.0-160000.11.1 Β· Apache-2.0
- opensuse-leap 16.0: openssl-3 3.5.0-160000.11.1 Β· Apache-2.0
- opensuse-leap 16.0: openssl-3 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