mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
				
	
	
		
			282 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| =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)>, L<ossl-guide-quic-server-non-block(7)>
 | |
| 
 | |
| =head1 COPYRIGHT
 | |
| 
 | |
| 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
 | |
| L<https://www.openssl.org/source/license.html>.
 | |
| 
 | |
| =cut
 |