]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add a guide for demo QUIC blocking server
authorAndrew Dinh <andrewd@openssl.org>
Mon, 23 Sep 2024 06:25:24 +0000 (23:25 -0700)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25361)

demos/guide/quic-server-block.c
doc/build.info
doc/man7/ossl-guide-introduction.pod
doc/man7/ossl-guide-quic-server-block.pod [new file with mode: 0644]

index f451ce7c6dcdf7077da4e36ee396cd274fc24d39..a424fbfddb386cb442ffd4099402cdad44c7550e 100644 (file)
@@ -231,7 +231,7 @@ static int run_quic_server(SSL_CTX *ctx, int fd)
         while (SSL_read_ex(conn, buf, sizeof(buf), &nread) > 0) {
             if (SSL_write_ex(conn, buf, nread, &nwritten) > 0
                 && nwritten == nread)
-                break;
+                continue;
             fprintf(stderr, "Error echoing client input");
             break;
         }
index 2ccdae3ad38efe101b1ec062c3e2c48b02c281d0..5141aac8b10884d300bcd0adef05f439a9f2dd21 100644 (file)
@@ -5027,6 +5027,10 @@ DEPEND[html/man7/ossl-guide-quic-multi-stream.html]=man7/ossl-guide-quic-multi-s
 GENERATE[html/man7/ossl-guide-quic-multi-stream.html]=man7/ossl-guide-quic-multi-stream.pod
 DEPEND[man/man7/ossl-guide-quic-multi-stream.7]=man7/ossl-guide-quic-multi-stream.pod
 GENERATE[man/man7/ossl-guide-quic-multi-stream.7]=man7/ossl-guide-quic-multi-stream.pod
+DEPEND[html/man7/ossl-guide-quic-server-block.html]=man7/ossl-guide-quic-server-block.pod
+GENERATE[html/man7/ossl-guide-quic-server-block.html]=man7/ossl-guide-quic-server-block.pod
+DEPEND[man/man7/ossl-guide-quic-server-block.7]=man7/ossl-guide-quic-server-block.pod
+GENERATE[man/man7/ossl-guide-quic-server-block.7]=man7/ossl-guide-quic-server-block.pod
 DEPEND[html/man7/ossl-guide-tls-client-block.html]=man7/ossl-guide-tls-client-block.pod
 GENERATE[html/man7/ossl-guide-tls-client-block.html]=man7/ossl-guide-tls-client-block.pod
 DEPEND[man/man7/ossl-guide-tls-client-block.7]=man7/ossl-guide-tls-client-block.pod
@@ -5267,6 +5271,7 @@ html/man7/ossl-guide-quic-client-block.html \
 html/man7/ossl-guide-quic-client-non-block.html \
 html/man7/ossl-guide-quic-introduction.html \
 html/man7/ossl-guide-quic-multi-stream.html \
+html/man7/ossl-guide-quic-server-block.html \
 html/man7/ossl-guide-tls-client-block.html \
 html/man7/ossl-guide-tls-client-non-block.html \
 html/man7/ossl-guide-tls-introduction.html \
@@ -5420,6 +5425,7 @@ man/man7/ossl-guide-quic-client-block.7 \
 man/man7/ossl-guide-quic-client-non-block.7 \
 man/man7/ossl-guide-quic-introduction.7 \
 man/man7/ossl-guide-quic-multi-stream.7 \
+man/man7/ossl-guide-quic-server-block.7 \
 man/man7/ossl-guide-tls-client-block.7 \
 man/man7/ossl-guide-tls-client-non-block.7 \
 man/man7/ossl-guide-tls-introduction.7 \
index 7ab41164da11f54904c4a00a3a2e8918015f42d2..9dc540e764cc28b921ebb10df235b365e75b97f3 100644 (file)
@@ -85,6 +85,8 @@ The pages in the guide are as follows:
 
 =item L<ossl-guide-quic-client-block(7)>: Writing a simple blocking QUIC client
 
+=item L<ossl-guide-quic-server-block(7)>: Writing a simple blocking QUIC server
+
 =item L<ossl-guide-quic-multi-stream(7)>: Writing a simple multi-stream QUIC client
 
 =item L<ossl-guide-quic-client-non-block(7)>: Writing a simple nonblocking QUIC client
diff --git a/doc/man7/ossl-guide-quic-server-block.pod b/doc/man7/ossl-guide-quic-server-block.pod
new file mode 100644 (file)
index 0000000..7711cb0
--- /dev/null
@@ -0,0 +1,282 @@
+=pod
+
+=begin comment
+
+NB: Changes to the source code samples in this file should also be reflected in
+demos/guide/quic-server-block.c
+
+=end comment
+
+=head1 NAME
+
+ossl-guide-quic-server-block
+- OpenSSL Guide: Writing a simple blocking QUIC server
+
+=head1 SIMPLE BLOCKING QUIC SERVER EXAMPLE
+
+This page will present various source code samples demonstrating 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.
+
+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.
+
+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.
+
+The complete source code for this example blocking QUIC server is available in
+the B<demos/guide> directory of the OpenSSL source distribution in the file
+B<quic-server-block.c>.  It is also available online at
+L<https://github.com/openssl/openssl/blob/master/demos/guide/quic-server-block.c>.
+
+We assume that you already have OpenSSL installed on your system; that you
+already have some fundamental understanding of OpenSSL concepts and QUIC (see
+L<ossl-guide-libraries-introduction(7)> and L<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.
+
+=head2 Creating the SSL_CTX and SSL objects
+
+The first step is to create an B<SSL_CTX> object for our server. We use the
+L<SSL_CTX_new(3)> function for this purpose.  We pass as an argument the return
+value of the function L<OSSL_QUIC_server_method(3)>.  You should use this method
+whenever you are writing a QUIC server.
+
+    /*
+     * 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.
+
+    /*
+     * 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.
+
+Though this is the default setting, we explicitly call the
+L<SSL_CTX_set_verify(3)> function and pass the B<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.
+
+    /*
+     * 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 L<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.
+
+    /* 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".
+
+    /*
+    * 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 B<SSL_CTX>.  Next, we create a
+UDP socket and bind to it on localhost.
+
+    /* 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 B<SSL_LISTENER> to listen for incoming
+connections.  We provide it with the bound UDP port to then explicitly begin
+listening for new connections.
+
+    /*
+     * 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;
+
+=head2 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.
+
+    /* Pristine error stack for each new connection */
+    ERR_clear_error();
+
+At this point, the server blocks to accept the next client.
+L<SSL_accept_connection(3)> will return an accepted connection within a fresh
+SSL, in which the handshake will have already occurred.
+
+    /* 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.
+
+    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
+L<SSL_stream_conclude(3)>.  This will send a final Finished packet to the
+client.
+
+    /* 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 L<SSL_shutdown_ex(3)>, which may need
+to be called multiple times to ensure the connection is shutdown completely.
+
+    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.
+
+    SSL_free(conn);
+
+=head2 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 B<SSL>.
+
+    SSL_free(listener);
+
+And in the main function, it would deallocate the constructed B<SSL_CTX>.
+
+    SSL_CTX_free(ctx);
+    BIO_closesocket(fd);
+    res = EXIT_SUCCESS;
+    return res;
+
+=head1 SEE ALSO
+
+L<ossl-guide-introduction(7)>, L<ossl-guide-libraries-introduction(7)>,
+L<ossl-guide-libssl-introduction(7)>, L<ossl-guide-quic-introduction(7)>,
+L<ossl-guide-quic-client-non-block(7)>, L<ossl-guide-quic-client-block(7)>,
+L<ossl-guide-tls-server-block(7)>
+
+=head1 COPYRIGHT
+
+Copyright 2024 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
+L<https://www.openssl.org/source/license.html>.
+
+=cut