diff options
Diffstat (limited to 'secure/lib/libcrypto/man/man7/ossl-guide-quic-server-non-block.7')
-rw-r--r-- | secure/lib/libcrypto/man/man7/ossl-guide-quic-server-non-block.7 | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/secure/lib/libcrypto/man/man7/ossl-guide-quic-server-non-block.7 b/secure/lib/libcrypto/man/man7/ossl-guide-quic-server-non-block.7 new file mode 100644 index 000000000000..7f3de1c8f7b8 --- /dev/null +++ b/secure/lib/libcrypto/man/man7/ossl-guide-quic-server-non-block.7 @@ -0,0 +1,447 @@ +.\" -*- mode: troff; coding: utf-8 -*- +.\" Automatically generated by Pod::Man 5.0102 (Pod::Simple 3.45) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. +.ie n \{\ +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "OSSL-GUIDE-QUIC-SERVER-NON-BLOCK 7ossl" +.TH OSSL-GUIDE-QUIC-SERVER-NON-BLOCK 7ossl 2025-09-16 3.5.3 OpenSSL +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH NAME +ossl\-guide\-quic\-server\-non\-block +\&\- OpenSSL Guide: Writing a simple nonblocking QUIC server +.SH "SIMPLE NONBLOCKING QUIC SERVER EXAMPLE" +.IX Header "SIMPLE NONBLOCKING QUIC SERVER EXAMPLE" +This page presents 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. +.PP +The server only accepts \f(CW\*(C`http/1.0\*(C'\fR and \f(CW\*(C`hq\-interop\*(C'\fR ALPN's and doesn't actually +implement HTTP but only does a simple echo. This is non-standard and will not +be supported by real world servers. This is for demonstration purposes only. +.PP +There are various methods to test this server: \fBquic\-client\-block.c\fR and +\&\fBquic\-client\-non\-block.c\fR will send a basic HTTP/1.0 request, which the server +will echo back. You can also test this server by running +\&\f(CW\*(C`openssl s_client \-connect localhost:4443 \-4 \-quic \-alpn http/1.0\*(C'\fR and entering +text that will be echoed back by the server. +.PP +Both the listening socket and connected socket are "nonblocking". However, +we use \fBselect()\fR to make the listening socket block when it cannot read/write. +Rather than stopping and waiting, your application may need to go and do other +tasks whilst the \fBSSL\fR object is unable to read/write. For example: updating a +GUI or performing operations on some other connection or stream. +.PP +The complete source code for this example nonblocking QUIC server is available +in the \fBdemos/guide\fR directory of the OpenSSL source distribution in the file +\&\fBquic\-server\-non\-block.c\fR. It is also available online at +<https://github.com/openssl/openssl/blob/master/demos/guide/quic\-server\-non\-block.c>. +.PP +We assume that you already have OpenSSL installed on your system; that you +already have some fundamental understanding of OpenSSL concepts and QUIC (see +\&\fBossl\-guide\-libraries\-introduction\fR\|(7) and \fBossl\-guide\-quic\-introduction\fR\|(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. +.SS "Creating the SSL_CTX and SSL objects" +.IX Subsection "Creating the SSL_CTX and SSL objects" +The first step is to create an \fBSSL_CTX\fR object for our server. We use the +\&\fBSSL_CTX_new\fR\|(3) function for this purpose. We pass as an argument the return +value of the function \fBOSSL_QUIC_server_method\fR\|(3). You should use this method +whenever you are writing a QUIC server. +.PP +.Vb 8 +\& /* +\& * 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; +.Ve +.PP +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. +.PP +.Vb 10 +\& /* +\& * Load the server\*(Aqs 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\*(Aqt load certificate file: %s\en", 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\*(Aqt load key file: %s\en", key_path); +\& goto err; +\& } +.Ve +.PP +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. +.PP +Though this is the default setting, we explicitly call the +\&\fBSSL_CTX_set_verify\fR\|(3) function and pass the \fBSSL_VERIFY_NONE\fR 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. +.PP +.Vb 12 +\& /* +\& * Clients rarely employ certificate\-based authentication, and so we don\*(Aqt +\& * require "mutual" TLS authentication (indeed there\*(Aqs no way to know +\& * whether or how the client authenticated the server, so the term "mutual" +\& * is potentially misleading). +\& * +\& * Since we\*(Aqre not soliciting or processing client certificates, we don\*(Aqt +\& * need to configure a trusted\-certificate store, so no call to +\& * SSL_CTX_set_default_verify_paths() is needed. The server\*(Aqs own +\& * certificate chain is assumed valid. +\& */ +\& SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); +.Ve +.PP +QUIC also dictates using Application-Layer Protocol Negotiation (ALPN) to select +an application protocol. We use \fBSSL_CTX_set_alpn_select_cb\fR\|(3) for this +purpose. We can pass a callback which will be called for each connection to +select an ALPN the server considers acceptable. +.PP +.Vb 2 +\& /* Setup ALPN negotiation callback to decide which ALPN is accepted. */ +\& SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); +.Ve +.PP +In this case, we only accept "http/1.0" and "hq-interop". +.PP +.Vb 8 +\& /* +\& * ALPN strings for TLS handshake. Only \*(Aqhttp/1.0\*(Aq and \*(Aqhq\-interop\*(Aq +\& * are accepted. +\& */ +\& static const unsigned char alpn_ossltest[] = { +\& 8, \*(Aqh\*(Aq, \*(Aqt\*(Aq, \*(Aqt\*(Aq, \*(Aqp\*(Aq, \*(Aq/\*(Aq, \*(Aq1\*(Aq, \*(Aq.\*(Aq, \*(Aq0\*(Aq, +\& 10, \*(Aqh\*(Aq, \*(Aqq\*(Aq, \*(Aq\-\*(Aq, \*(Aqi\*(Aq, \*(Aqn\*(Aq, \*(Aqt\*(Aq, \*(Aqe\*(Aq, \*(Aqr\*(Aq, \*(Aqo\*(Aq, \*(Aqp\*(Aq, +\& }; +\& +\& 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; +\& } +.Ve +.PP +That is all the setup that we need to do for the \fBSSL_CTX\fR. Next, we create a +UDP socket and bind to it on localhost. +.PP +.Vb 5 +\& /* Retrieve the file descriptor for a new UDP socket */ +\& if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { +\& fprintf(stderr, "cannot create socket"); +\& return \-1; +\& } +\& +\& 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\en", port); +\& BIO_closesocket(fd); +\& return \-1; +\& } +\& +\& /* Set port to nonblocking mode */ +\& if (BIO_socket_nbio(fd, 1) <= 0) { +\& fprintf(stderr, "Unable to set port to nonblocking mode"); +\& BIO_closesocket(fd); +\& return \-1; +\& } +.Ve +.PP +To run the QUIC server, we create an \fBSSL_LISTENER\fR to listen for incoming +connections. We provide it with the bound UDP port to then explicitly begin +listening for new connections. +.PP +.Vb 3 +\& /* Create a new QUIC listener */ +\& 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; +\& +\& /* Set the listener mode to nonblocking, which is inherited by +\& * child objects. +\& */ +\& if (!SSL_set_blocking_mode(listener, 0)) +\& goto err; +\& +\& /* +\& * Begin listening. Note that is not usually needed as SSL_accept_connection +\& * will implicitly start listening. It is only needed if a server wishes to +\& * ensure it has started to accept incoming connections but does not wish to +\& * actually call SSL_accept_connection yet. +\& */ +\& if (!SSL_listen(listener)) +\& goto err; +.Ve +.SS "Server loop" +.IX Subsection "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. +.PP +.Vb 2 +\& /* Pristine error stack for each new connection */ +\& ERR_clear_error(); +.Ve +.PP +We then wait until a connection is ready for reading. +It uses the select function to wait until the socket is either readable +or writable, depending on what the SSL connection requires. +.PP +We then accept a new connection in which the handshake will have already +occurred. However, since we are in nonblocking mode, \fBSSL_accept_connection\fR\|(3) +will return immediately. Therefore, we use a helper function to essentially +block until a connection is established. +.PP +.Vb 5 +\& printf("Waiting for connection\en"); +\& while ((conn = SSL_accept_connection(listener, 0)) == NULL) { +\& wait_for_activity(listener); +\& } +\& printf("Accepted new connection\en"); +.Ve +.PP +The helper function wait_for_activity uses \fBselect()\fR to block until the file +descriptor belonging to the passed SSL object is readable. As mentioned earlier, +a more real-world application would likely use this time to perform other tasks. +.PP +.Vb 3 +\& /* Initialize the fd_set structure */ +\& FD_ZERO(&read_fd); +\& FD_ZERO(&write_fd); +\& +\& /* +\& * Determine if we would like to write to the socket, read from it, or both. +\& */ +\& if (SSL_net_write_desired(ssl)) +\& FD_SET(sock, &write_fd); +\& if (SSL_net_read_desired(ssl)) +\& FD_SET(sock, &read_fd); +\& +\& /* +\& * Find out when OpenSSL would next like to be called, regardless of +\& * whether the state of the underlying socket has changed or not. +\& */ +\& if (SSL_get_event_timeout(ssl, &tv, &isinfinite) && !isinfinite) +\& tvp = &tv; +\& +\& /* +\& * Wait until the socket is writeable or readable. We use select here +\& * for the sake of simplicity and portability, but you could equally use +\& * poll/epoll or similar functions +\& * +\& * NOTE: For the purposes of this demonstration code this effectively +\& * makes this demo block until it has something more useful to do. In a +\& * real application you probably want to go and do other work here (e.g. +\& * update a GUI, or service other connections). +\& * +\& * Let\*(Aqs say for example that you want to update the progress counter on +\& * a GUI every 100ms. One way to do that would be to use the timeout in +\& * the last parameter to "select" below. If the tvp value is greater +\& * than 100ms then use 100ms instead. Then, when select returns, you +\& * check if it did so because of activity on the file descriptors or +\& * because of the timeout. If the 100ms GUI timeout has expired but the +\& * tvp timeout has not then go and update the GUI and then restart the +\& * "select" (with updated timeouts). +\& */ +\& +\& select(sock + 1, &read_fd, &write_fd, NULL, tvp); +.Ve +.PP +With the handshake complete, the server reads all the client input. +.PP +.Vb 10 +\& /* Read from client until the client sends a end of stream packet */ +\& while (!eof) { +\& ret = SSL_read_ex(conn, buf + total_read, sizeof(buf) \- total_read, +\& &nread); +\& total_read += nread; +\& if (total_read >= 8192) { +\& fprintf(stderr, "Could not fit all data into buffer\en"); +\& goto err; +\& } +\& switch (handle_io_failure(conn, ret)) { +\& case 1: +\& continue; /* Retry */ +\& case 0: +\& /* Reached end of stream */ +\& if (!SSL_has_pending(conn)) +\& eof = 1; +\& break; +\& default: +\& fprintf(stderr, "Failed reading remaining data\en"); +\& goto err; +\& } +\& } +.Ve +.PP +Finally, we echo the received data back to the client. We can use +\&\fBSSL_write_ex2\fR\|(3) to pass in a special flag SSL_WRITE_FLAG_CONCLUDE that will +send a FIN packet once the write has successfully finished writing all the data +to the peer. +.PP +.Vb 9 +\& /* Echo client input */ +\& while (!SSL_write_ex2(conn, buf, +\& total_read, +\& SSL_WRITE_FLAG_CONCLUDE, &total_written)) { +\& if (handle_io_failure(conn, 0) == 1) +\& continue; +\& fprintf(stderr, "Failed to write data\en"); +\& goto err; +\& } +.Ve +.PP +We then shut down the connection with \fBSSL_shutdown\fR\|(3), which may need +to be called multiple times to ensure the connection is shutdown completely. +.PP +.Vb 8 +\& /* +\& * Shut down the connection. We may need to call this multiple times +\& * to ensure the connection is shutdown completely. +\& */ +\& while ((ret = SSL_shutdown(conn)) != 1) { +\& if (ret < 0 && handle_io_failure(conn, ret) == 1) +\& continue; /* Retry */ +\& } +.Ve +.PP +Finally, we free the SSL connection, and the server is now ready to accept the +next client connection. +.PP +.Vb 1 +\& SSL_free(conn); +.Ve +.SS "Final clean up" +.IX Subsection "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 \fBSSL\fR. +.PP +.Vb 1 +\& SSL_free(listener); +.Ve +.PP +And in the main function, it would deallocate the constructed \fBSSL_CTX\fR. +.PP +.Vb 2 +\& SSL_CTX_free(ctx); +\& BIO_closesocket(fd); +.Ve +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fBossl\-guide\-introduction\fR\|(7), \fBossl\-guide\-libraries\-introduction\fR\|(7), +\&\fBossl\-guide\-libssl\-introduction\fR\|(7), \fBossl\-guide\-quic\-introduction\fR\|(7), +\&\fBossl\-guide\-quic\-client\-non\-block\fR\|(7), \fBossl\-guide\-quic\-client\-block\fR\|(7), +\&\fBossl\-guide\-tls\-server\-block\fR\|(7), \fBossl\-guide\-quic\-server\-block\fR\|(7) +.SH COPYRIGHT +.IX Header "COPYRIGHT" +Copyright 2024\-2025 The OpenSSL Project Authors. All Rights Reserved. +.PP +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>. |