mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
	
	
		
			283 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			283 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)>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								=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
							 |