xref: /freebsd/crypto/openssl/test/quic-openssl-docker/hq-interop/quic-hq-interop-server.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1*e7be843bSPierre Pronchery /*
2*e7be843bSPierre Pronchery  *  Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
3*e7be843bSPierre Pronchery  *
4*e7be843bSPierre Pronchery  *  Licensed under the Apache License 2.0 (the "License").  You may not use
5*e7be843bSPierre Pronchery  *  this file except in compliance with the License.  You can obtain a copy
6*e7be843bSPierre Pronchery  *  in the file LICENSE in the source distribution or at
7*e7be843bSPierre Pronchery  *  https://www.openssl.org/source/license.html
8*e7be843bSPierre Pronchery  */
9*e7be843bSPierre Pronchery 
10*e7be843bSPierre Pronchery /**
11*e7be843bSPierre Pronchery  * @file quic-hq-interop-server.c
12*e7be843bSPierre Pronchery  * @brief Minimal QUIC HTTP/0.9 server implementation.
13*e7be843bSPierre Pronchery  *
14*e7be843bSPierre Pronchery  * This file implements a lightweight QUIC server supporting the HTTP/0.9
15*e7be843bSPierre Pronchery  * protocol for interoperability testing. It includes functions for setting
16*e7be843bSPierre Pronchery  * up a secure QUIC connection, handling ALPN negotiation, and serving client
17*e7be843bSPierre Pronchery  * requests.  Intended for use with the quic-interop-runner
18*e7be843bSPierre Pronchery  * available at https://interop.seemann.io
19*e7be843bSPierre Pronchery  *
20*e7be843bSPierre Pronchery  * Key functionalities:
21*e7be843bSPierre Pronchery  * - Setting up SSL_CTX with QUIC support.
22*e7be843bSPierre Pronchery  * - Negotiating ALPN strings during the TLS handshake.
23*e7be843bSPierre Pronchery  * - Listening and accepting incoming QUIC connections.
24*e7be843bSPierre Pronchery  * - Handling client requests via HTTP/0.9 protocol.
25*e7be843bSPierre Pronchery  *
26*e7be843bSPierre Pronchery  * Usage:
27*e7be843bSPierre Pronchery  *   <port> <server.crt> <server.key>
28*e7be843bSPierre Pronchery  * The server binds to the specified port and serves files using the given
29*e7be843bSPierre Pronchery  * certificate and private key.
30*e7be843bSPierre Pronchery  *
31*e7be843bSPierre Pronchery  * Environment variables:
32*e7be843bSPierre Pronchery  * - FILEPREFIX: Specifies the directory containing files to serve.
33*e7be843bSPierre Pronchery  *   Defaults to "./downloads" if not set.
34*e7be843bSPierre Pronchery  * - SSLKEYLOGFILE: specifies that keylogging should be preformed on the server
35*e7be843bSPierre Pronchery  *   should be set to a file name to record keylog data to
36*e7be843bSPierre Pronchery  * - NO_ADDR_VALIDATE: Disables server address validation of clients
37*e7be843bSPierre Pronchery  *
38*e7be843bSPierre Pronchery  */
39*e7be843bSPierre Pronchery 
40*e7be843bSPierre Pronchery #include <string.h>
41*e7be843bSPierre Pronchery 
42*e7be843bSPierre Pronchery /* Include the appropriate header file for SOCK_STREAM */
43*e7be843bSPierre Pronchery #ifdef _WIN32
44*e7be843bSPierre Pronchery # include <stdarg.h>
45*e7be843bSPierre Pronchery # include <winsock2.h>
46*e7be843bSPierre Pronchery # include <ws2tcpip.h>
47*e7be843bSPierre Pronchery #else
48*e7be843bSPierre Pronchery # include <sys/socket.h>
49*e7be843bSPierre Pronchery # include <netinet/in.h>
50*e7be843bSPierre Pronchery # include <unistd.h>
51*e7be843bSPierre Pronchery #endif
52*e7be843bSPierre Pronchery 
53*e7be843bSPierre Pronchery #include <openssl/bio.h>
54*e7be843bSPierre Pronchery #include <openssl/ssl.h>
55*e7be843bSPierre Pronchery #include <openssl/err.h>
56*e7be843bSPierre Pronchery #include <openssl/quic.h>
57*e7be843bSPierre Pronchery 
58*e7be843bSPierre Pronchery #define BUF_SIZE 4096
59*e7be843bSPierre Pronchery 
60*e7be843bSPierre Pronchery /**
61*e7be843bSPierre Pronchery  * @brief ALPN (Application-Layer Protocol Negotiation) identifier for QUIC.
62*e7be843bSPierre Pronchery  *
63*e7be843bSPierre Pronchery  * This constant defines the ALPN string used during the TLS handshake
64*e7be843bSPierre Pronchery  * to negotiate the application-layer protocol between the client and
65*e7be843bSPierre Pronchery  * the server. It specifies "hq-interop" as the supported protocol.
66*e7be843bSPierre Pronchery  *
67*e7be843bSPierre Pronchery  * Format:
68*e7be843bSPierre Pronchery  * - The first byte represents the length of the ALPN string.
69*e7be843bSPierre Pronchery  * - Subsequent bytes represent the ASCII characters of the protocol name.
70*e7be843bSPierre Pronchery  *
71*e7be843bSPierre Pronchery  * Value:
72*e7be843bSPierre Pronchery  * - Protocol: "hq-interop"
73*e7be843bSPierre Pronchery  * - Length: 10 bytes
74*e7be843bSPierre Pronchery  *
75*e7be843bSPierre Pronchery  * Usage:
76*e7be843bSPierre Pronchery  * This is passed to the ALPN callback function to validate and
77*e7be843bSPierre Pronchery  * negotiate the desired protocol during the TLS handshake.
78*e7be843bSPierre Pronchery  */
79*e7be843bSPierre Pronchery static const unsigned char alpn_ossltest[] = {
80*e7be843bSPierre Pronchery     10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p',
81*e7be843bSPierre Pronchery };
82*e7be843bSPierre Pronchery 
83*e7be843bSPierre Pronchery /**
84*e7be843bSPierre Pronchery  * @brief Directory prefix for serving requested files.
85*e7be843bSPierre Pronchery  *
86*e7be843bSPierre Pronchery  * This variable specifies the directory path used as the base location
87*e7be843bSPierre Pronchery  * for serving files in response to client requests. It is used to construct
88*e7be843bSPierre Pronchery  * the full file path for requested resources.
89*e7be843bSPierre Pronchery  *
90*e7be843bSPierre Pronchery  * Default:
91*e7be843bSPierre Pronchery  * - If not set via the FILEPREFIX environment variable, it defaults to
92*e7be843bSPierre Pronchery  *   "./downloads".
93*e7be843bSPierre Pronchery  *
94*e7be843bSPierre Pronchery  * Usage:
95*e7be843bSPierre Pronchery  * - Updated at runtime based on the FILEPREFIX environment variable.
96*e7be843bSPierre Pronchery  * - Used to locate and serve files during incoming requests.
97*e7be843bSPierre Pronchery  */
98*e7be843bSPierre Pronchery static char *fileprefix = NULL;
99*e7be843bSPierre Pronchery 
100*e7be843bSPierre Pronchery /**
101*e7be843bSPierre Pronchery  * @brief Callback for ALPN (Application-Layer Protocol Negotiation) selection.
102*e7be843bSPierre Pronchery  *
103*e7be843bSPierre Pronchery  * This function is invoked during the TLS handshake on the server side to
104*e7be843bSPierre Pronchery  * validate and negotiate the desired ALPN (Application-Layer Protocol
105*e7be843bSPierre Pronchery  * Negotiation) protocol with the client. It ensures that the negotiated
106*e7be843bSPierre Pronchery  * protocol matches the predefined "hq-interop" string.
107*e7be843bSPierre Pronchery  *
108*e7be843bSPierre Pronchery  * @param ssl       Pointer to the SSL connection object.
109*e7be843bSPierre Pronchery  * @param[out] out  Pointer to the negotiated ALPN protocol string.
110*e7be843bSPierre Pronchery  * @param[out] out_len Length of the negotiated ALPN protocol string.
111*e7be843bSPierre Pronchery  * @param in        Pointer to the client-provided ALPN protocol list.
112*e7be843bSPierre Pronchery  * @param in_len    Length of the client-provided ALPN protocol list.
113*e7be843bSPierre Pronchery  * @param arg       Optional user-defined argument (unused in this context).
114*e7be843bSPierre Pronchery  *
115*e7be843bSPierre Pronchery  * @return SSL_TLSEXT_ERR_OK on successful ALPN negotiation,
116*e7be843bSPierre Pronchery  *         SSL_TLSEXT_ERR_ALERT_FATAL otherwise.
117*e7be843bSPierre Pronchery  *
118*e7be843bSPierre Pronchery  * Usage:
119*e7be843bSPierre Pronchery  * - This function is set as the ALPN selection callback in the SSL_CTX
120*e7be843bSPierre Pronchery  *   using `SSL_CTX_set_alpn_select_cb`.
121*e7be843bSPierre Pronchery  * - Ensures that only the predefined ALPN protocol is accepted.
122*e7be843bSPierre Pronchery  *
123*e7be843bSPierre Pronchery  * Note:
124*e7be843bSPierre Pronchery  * - The predefined protocol is specified in the `alpn_ossltest` array.
125*e7be843bSPierre Pronchery  */
select_alpn(SSL * ssl,const unsigned char ** out,unsigned char * out_len,const unsigned char * in,unsigned int in_len,void * arg)126*e7be843bSPierre Pronchery static int select_alpn(SSL *ssl, const unsigned char **out,
127*e7be843bSPierre Pronchery                        unsigned char *out_len, const unsigned char *in,
128*e7be843bSPierre Pronchery                        unsigned int in_len, void *arg)
129*e7be843bSPierre Pronchery {
130*e7be843bSPierre Pronchery     /*
131*e7be843bSPierre Pronchery      * Use the next_proto helper function here.
132*e7be843bSPierre Pronchery      * This scans the list of alpns we support and matches against
133*e7be843bSPierre Pronchery      * what the client is requesting
134*e7be843bSPierre Pronchery      */
135*e7be843bSPierre Pronchery     if (SSL_select_next_proto((unsigned char **)out, out_len, alpn_ossltest,
136*e7be843bSPierre Pronchery                               sizeof(alpn_ossltest), in,
137*e7be843bSPierre Pronchery                               in_len) == OPENSSL_NPN_NEGOTIATED)
138*e7be843bSPierre Pronchery         return SSL_TLSEXT_ERR_OK;
139*e7be843bSPierre Pronchery     return SSL_TLSEXT_ERR_ALERT_FATAL;
140*e7be843bSPierre Pronchery }
141*e7be843bSPierre Pronchery 
142*e7be843bSPierre Pronchery /**
143*e7be843bSPierre Pronchery  * @brief Creates and configures an SSL_CTX for a QUIC server.
144*e7be843bSPierre Pronchery  *
145*e7be843bSPierre Pronchery  * This function initializes an SSL_CTX object with the QUIC server method
146*e7be843bSPierre Pronchery  * and configures it using the provided certificate and private key. The
147*e7be843bSPierre Pronchery  * context is prepared for handling secure QUIC connections and performing
148*e7be843bSPierre Pronchery  * ALPN (Application-Layer Protocol Negotiation).
149*e7be843bSPierre Pronchery  *
150*e7be843bSPierre Pronchery  * @param cert_path Path to the server's certificate chain file in PEM format.
151*e7be843bSPierre Pronchery  *                  The chain file must include the server's leaf certificate
152*e7be843bSPierre Pronchery  *                  followed by intermediate CA certificates.
153*e7be843bSPierre Pronchery  * @param key_path  Path to the server's private key file in PEM format. The
154*e7be843bSPierre Pronchery  *                  private key must correspond to the leaf certificate in
155*e7be843bSPierre Pronchery  *                  the chain file.
156*e7be843bSPierre Pronchery  *
157*e7be843bSPierre Pronchery  * @return Pointer to the initialized SSL_CTX on success, or NULL on failure.
158*e7be843bSPierre Pronchery  *
159*e7be843bSPierre Pronchery  * Configuration:
160*e7be843bSPierre Pronchery  * - Loads the certificate chain and private key into the context.
161*e7be843bSPierre Pronchery  * - Disables client certificate verification (no mutual TLS).
162*e7be843bSPierre Pronchery  * - Sets up the ALPN selection callback for protocol negotiation.
163*e7be843bSPierre Pronchery  *
164*e7be843bSPierre Pronchery  * Error Handling:
165*e7be843bSPierre Pronchery  * - If any step fails (e.g., loading the certificate or key), the function
166*e7be843bSPierre Pronchery  *   frees the SSL_CTX and returns NULL.
167*e7be843bSPierre Pronchery  *
168*e7be843bSPierre Pronchery  * Usage:
169*e7be843bSPierre Pronchery  * - Call this function to create an SSL_CTX before starting the QUIC server.
170*e7be843bSPierre Pronchery  * - Ensure valid paths for the certificate and private key are provided.
171*e7be843bSPierre Pronchery  *
172*e7be843bSPierre Pronchery  * Note:
173*e7be843bSPierre Pronchery  * - The ALPN callback only supports the predefined protocol defined in
174*e7be843bSPierre Pronchery  *   `alpn_ossltest`.
175*e7be843bSPierre Pronchery  */
create_ctx(const char * cert_path,const char * key_path)176*e7be843bSPierre Pronchery static SSL_CTX *create_ctx(const char *cert_path, const char *key_path)
177*e7be843bSPierre Pronchery {
178*e7be843bSPierre Pronchery     SSL_CTX *ctx;
179*e7be843bSPierre Pronchery 
180*e7be843bSPierre Pronchery     /*
181*e7be843bSPierre Pronchery      * An SSL_CTX holds shared configuration information for multiple
182*e7be843bSPierre Pronchery      * subsequent per-client connections. We specifically load a QUIC
183*e7be843bSPierre Pronchery      * server method here.
184*e7be843bSPierre Pronchery      */
185*e7be843bSPierre Pronchery     ctx = SSL_CTX_new(OSSL_QUIC_server_method());
186*e7be843bSPierre Pronchery     if (ctx == NULL)
187*e7be843bSPierre Pronchery         goto err;
188*e7be843bSPierre Pronchery 
189*e7be843bSPierre Pronchery     /*
190*e7be843bSPierre Pronchery      * Load the server's certificate *chain* file (PEM format), which includes
191*e7be843bSPierre Pronchery      * not only the leaf (end-entity) server certificate, but also any
192*e7be843bSPierre Pronchery      * intermediate issuer-CA certificates.  The leaf certificate must be the
193*e7be843bSPierre Pronchery      * first certificate in the file.
194*e7be843bSPierre Pronchery      *
195*e7be843bSPierre Pronchery      * In advanced use-cases this can be called multiple times, once per public
196*e7be843bSPierre Pronchery      * key algorithm for which the server has a corresponding certificate.
197*e7be843bSPierre Pronchery      * However, the corresponding private key (see below) must be loaded first,
198*e7be843bSPierre Pronchery      * *before* moving on to the next chain file.
199*e7be843bSPierre Pronchery      *
200*e7be843bSPierre Pronchery      * The requisite files "chain.pem" and "pkey.pem" can be generated by running
201*e7be843bSPierre Pronchery      * "make chain" in this directory.  If the server will be executed from some
202*e7be843bSPierre Pronchery      * other directory, move or copy the files there.
203*e7be843bSPierre Pronchery      */
204*e7be843bSPierre Pronchery     if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) {
205*e7be843bSPierre Pronchery         fprintf(stderr, "couldn't load certificate file: %s\n", cert_path);
206*e7be843bSPierre Pronchery         goto err;
207*e7be843bSPierre Pronchery     }
208*e7be843bSPierre Pronchery 
209*e7be843bSPierre Pronchery     /*
210*e7be843bSPierre Pronchery      * Load the corresponding private key, this also checks that the private
211*e7be843bSPierre Pronchery      * key matches the just loaded end-entity certificate.  It does not check
212*e7be843bSPierre Pronchery      * whether the certificate chain is valid, the certificates could be
213*e7be843bSPierre Pronchery      * expired, or may otherwise fail to form a chain that a client can validate.
214*e7be843bSPierre Pronchery      */
215*e7be843bSPierre Pronchery     if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
216*e7be843bSPierre Pronchery         fprintf(stderr, "couldn't load key file: %s\n", key_path);
217*e7be843bSPierre Pronchery         goto err;
218*e7be843bSPierre Pronchery     }
219*e7be843bSPierre Pronchery 
220*e7be843bSPierre Pronchery     /*
221*e7be843bSPierre Pronchery      * Since we're not soliciting or processing client certificates, we don't
222*e7be843bSPierre Pronchery      * need to configure a trusted-certificate store, so no call to
223*e7be843bSPierre Pronchery      * SSL_CTX_set_default_verify_paths() is needed.  The server's own
224*e7be843bSPierre Pronchery      * certificate chain is assumed valid.
225*e7be843bSPierre Pronchery      */
226*e7be843bSPierre Pronchery     SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
227*e7be843bSPierre Pronchery 
228*e7be843bSPierre Pronchery     /* Setup ALPN negotiation callback to decide which ALPN is accepted. */
229*e7be843bSPierre Pronchery     SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
230*e7be843bSPierre Pronchery 
231*e7be843bSPierre Pronchery     return ctx;
232*e7be843bSPierre Pronchery 
233*e7be843bSPierre Pronchery err:
234*e7be843bSPierre Pronchery     SSL_CTX_free(ctx);
235*e7be843bSPierre Pronchery     return NULL;
236*e7be843bSPierre Pronchery }
237*e7be843bSPierre Pronchery 
238*e7be843bSPierre Pronchery /**
239*e7be843bSPierre Pronchery  * @brief Creates and binds a UDP socket to the specified port.
240*e7be843bSPierre Pronchery  *
241*e7be843bSPierre Pronchery  * This function initializes a new UDP socket, binds it to the specified
242*e7be843bSPierre Pronchery  * port on the local host, and returns the socket file descriptor for
243*e7be843bSPierre Pronchery  * further use.
244*e7be843bSPierre Pronchery  *
245*e7be843bSPierre Pronchery  * @param port The port number to which the UDP socket should be bound.
246*e7be843bSPierre Pronchery  *
247*e7be843bSPierre Pronchery  * @return On success, returns the BIO of the created socket.
248*e7be843bSPierre Pronchery  *         On failure, returns NULL.
249*e7be843bSPierre Pronchery  *
250*e7be843bSPierre Pronchery  * Steps:
251*e7be843bSPierre Pronchery  * - Creates a new UDP socket using the `socket` system call.
252*e7be843bSPierre Pronchery  * - Configures the socket address structure to bind to the specified port
253*e7be843bSPierre Pronchery  *   on the local host.
254*e7be843bSPierre Pronchery  * - Binds the socket to the port using the `bind` system call.
255*e7be843bSPierre Pronchery  *
256*e7be843bSPierre Pronchery  * Error Handling:
257*e7be843bSPierre Pronchery  * - If socket creation or binding fails, an error message is printed to
258*e7be843bSPierre Pronchery  *   `stderr`, the socket (if created) is closed, and -1 is returned.
259*e7be843bSPierre Pronchery  *
260*e7be843bSPierre Pronchery  * Usage:
261*e7be843bSPierre Pronchery  * - Call this function to set up a socket for handling incoming QUIC
262*e7be843bSPierre Pronchery  *   connections.
263*e7be843bSPierre Pronchery  *
264*e7be843bSPierre Pronchery  * Notes:
265*e7be843bSPierre Pronchery  * - This function assumes UDP (`SOCK_DGRAM`).
266*e7be843bSPierre Pronchery  * - This function accepts on both IPv4 and IPv6.
267*e7be843bSPierre Pronchery  * - The specified port is converted to network byte order using `htons`.
268*e7be843bSPierre Pronchery  */
create_socket(uint16_t port)269*e7be843bSPierre Pronchery static BIO *create_socket(uint16_t port)
270*e7be843bSPierre Pronchery {
271*e7be843bSPierre Pronchery     int fd = -1;
272*e7be843bSPierre Pronchery     BIO *sock = NULL;
273*e7be843bSPierre Pronchery     BIO_ADDR *addr = NULL;
274*e7be843bSPierre Pronchery     int opt = 0;
275*e7be843bSPierre Pronchery #ifdef _WIN32
276*e7be843bSPierre Pronchery     struct in6_addr in6addr_any;
277*e7be843bSPierre Pronchery 
278*e7be843bSPierre Pronchery     memset(&in6addr_any, 0, sizeof(in6addr_any));
279*e7be843bSPierre Pronchery #endif
280*e7be843bSPierre Pronchery 
281*e7be843bSPierre Pronchery     /* Retrieve the file descriptor for a new UDP socket */
282*e7be843bSPierre Pronchery     if ((fd = BIO_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) {
283*e7be843bSPierre Pronchery         fprintf(stderr, "cannot create socket");
284*e7be843bSPierre Pronchery         goto err;
285*e7be843bSPierre Pronchery     }
286*e7be843bSPierre Pronchery 
287*e7be843bSPierre Pronchery     /*
288*e7be843bSPierre Pronchery      * IPv6_V6ONLY is only available on some platforms. If it is defined,
289*e7be843bSPierre Pronchery      * disable it to accept both IPv4 and IPv6 connections. Otherwise, the
290*e7be843bSPierre Pronchery      * server will only accept IPv6 connections.
291*e7be843bSPierre Pronchery      */
292*e7be843bSPierre Pronchery #ifdef IPV6_V6ONLY
293*e7be843bSPierre Pronchery     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
294*e7be843bSPierre Pronchery         fprintf(stderr, "setsockopt IPV6_V6ONLY failed");
295*e7be843bSPierre Pronchery         goto err;
296*e7be843bSPierre Pronchery     }
297*e7be843bSPierre Pronchery #endif
298*e7be843bSPierre Pronchery 
299*e7be843bSPierre Pronchery     /*
300*e7be843bSPierre Pronchery      * Create a new BIO_ADDR
301*e7be843bSPierre Pronchery      */
302*e7be843bSPierre Pronchery     addr = BIO_ADDR_new();
303*e7be843bSPierre Pronchery     if (addr == NULL) {
304*e7be843bSPierre Pronchery         fprintf(stderr, "Unable to create BIO_ADDR\n");
305*e7be843bSPierre Pronchery         goto err;
306*e7be843bSPierre Pronchery     }
307*e7be843bSPierre Pronchery 
308*e7be843bSPierre Pronchery     /*
309*e7be843bSPierre Pronchery      * Build an INADDR_ANY BIO_ADDR
310*e7be843bSPierre Pronchery      */
311*e7be843bSPierre Pronchery     if (!BIO_ADDR_rawmake(addr, AF_INET6, &in6addr_any, sizeof(in6addr_any), htons(port))) {
312*e7be843bSPierre Pronchery         fprintf(stderr, "unable to bind to port %d\n", port);
313*e7be843bSPierre Pronchery         goto err;
314*e7be843bSPierre Pronchery     }
315*e7be843bSPierre Pronchery 
316*e7be843bSPierre Pronchery     /* Bind to the new UDP socket */
317*e7be843bSPierre Pronchery     if (!BIO_bind(fd, addr, 0)) {
318*e7be843bSPierre Pronchery         fprintf(stderr, "cannot bind to %u\n", port);
319*e7be843bSPierre Pronchery         goto err;
320*e7be843bSPierre Pronchery     }
321*e7be843bSPierre Pronchery 
322*e7be843bSPierre Pronchery     /*
323*e7be843bSPierre Pronchery      * Create a new datagram socket
324*e7be843bSPierre Pronchery      */
325*e7be843bSPierre Pronchery     sock = BIO_new(BIO_s_datagram());
326*e7be843bSPierre Pronchery     if (sock == NULL) {
327*e7be843bSPierre Pronchery         fprintf(stderr, "cannot create dgram bio\n");
328*e7be843bSPierre Pronchery         goto err;
329*e7be843bSPierre Pronchery     }
330*e7be843bSPierre Pronchery 
331*e7be843bSPierre Pronchery     /*
332*e7be843bSPierre Pronchery      * associate the underlying socket with the dgram BIO
333*e7be843bSPierre Pronchery      */
334*e7be843bSPierre Pronchery     if (!BIO_set_fd(sock, fd, BIO_CLOSE)) {
335*e7be843bSPierre Pronchery         fprintf(stderr, "Unable to set fd of dgram sock\n");
336*e7be843bSPierre Pronchery         goto err;
337*e7be843bSPierre Pronchery     }
338*e7be843bSPierre Pronchery 
339*e7be843bSPierre Pronchery     /*
340*e7be843bSPierre Pronchery      * Free our allocated addr
341*e7be843bSPierre Pronchery      */
342*e7be843bSPierre Pronchery     BIO_ADDR_free(addr);
343*e7be843bSPierre Pronchery     return sock;
344*e7be843bSPierre Pronchery 
345*e7be843bSPierre Pronchery err:
346*e7be843bSPierre Pronchery     BIO_ADDR_free(addr);
347*e7be843bSPierre Pronchery     BIO_free(sock);
348*e7be843bSPierre Pronchery     BIO_closesocket(fd);
349*e7be843bSPierre Pronchery     return NULL;
350*e7be843bSPierre Pronchery }
351*e7be843bSPierre Pronchery 
352*e7be843bSPierre Pronchery /**
353*e7be843bSPierre Pronchery  * @brief Handles I/O failures on an SSL stream based on the result code.
354*e7be843bSPierre Pronchery  *
355*e7be843bSPierre Pronchery  * This function processes the result of an SSL I/O operation and handles
356*e7be843bSPierre Pronchery  * different types of errors that may occur during the operation. It takes
357*e7be843bSPierre Pronchery  * appropriate actions such as retrying the operation, reporting errors, or
358*e7be843bSPierre Pronchery  * returning specific status codes based on the error type.
359*e7be843bSPierre Pronchery  *
360*e7be843bSPierre Pronchery  * @param ssl A pointer to the SSL object representing the stream.
361*e7be843bSPierre Pronchery  * @param res The result code from the SSL I/O operation.
362*e7be843bSPierre Pronchery  * @return An integer indicating the outcome:
363*e7be843bSPierre Pronchery  *         - 0: EOF, indicating the stream has been closed.
364*e7be843bSPierre Pronchery  *         - -1: A fatal error occurred or the stream has been reset.
365*e7be843bSPierre Pronchery  *
366*e7be843bSPierre Pronchery  *
367*e7be843bSPierre Pronchery  * @note If the failure is due to an SSL verification error, additional
368*e7be843bSPierre Pronchery  * information will be logged to stderr.
369*e7be843bSPierre Pronchery  */
handle_io_failure(SSL * ssl,int res)370*e7be843bSPierre Pronchery static int handle_io_failure(SSL *ssl, int res)
371*e7be843bSPierre Pronchery {
372*e7be843bSPierre Pronchery     switch (SSL_get_error(ssl, res)) {
373*e7be843bSPierre Pronchery     case SSL_ERROR_ZERO_RETURN:
374*e7be843bSPierre Pronchery         /* EOF */
375*e7be843bSPierre Pronchery         return 0;
376*e7be843bSPierre Pronchery 
377*e7be843bSPierre Pronchery     case SSL_ERROR_SYSCALL:
378*e7be843bSPierre Pronchery         return -1;
379*e7be843bSPierre Pronchery 
380*e7be843bSPierre Pronchery     case SSL_ERROR_SSL:
381*e7be843bSPierre Pronchery         /*
382*e7be843bSPierre Pronchery          * Some stream fatal error occurred. This could be because of a
383*e7be843bSPierre Pronchery          * stream reset - or some failure occurred on the underlying
384*e7be843bSPierre Pronchery          * connection.
385*e7be843bSPierre Pronchery          */
386*e7be843bSPierre Pronchery         switch (SSL_get_stream_read_state(ssl)) {
387*e7be843bSPierre Pronchery         case SSL_STREAM_STATE_RESET_REMOTE:
388*e7be843bSPierre Pronchery             fprintf(stderr, "Stream reset occurred\n");
389*e7be843bSPierre Pronchery             /*
390*e7be843bSPierre Pronchery              * The stream has been reset but the connection is still
391*e7be843bSPierre Pronchery              * healthy.
392*e7be843bSPierre Pronchery              */
393*e7be843bSPierre Pronchery             break;
394*e7be843bSPierre Pronchery 
395*e7be843bSPierre Pronchery         case SSL_STREAM_STATE_CONN_CLOSED:
396*e7be843bSPierre Pronchery             fprintf(stderr, "Connection closed\n");
397*e7be843bSPierre Pronchery             /* Connection is already closed. */
398*e7be843bSPierre Pronchery             break;
399*e7be843bSPierre Pronchery 
400*e7be843bSPierre Pronchery         default:
401*e7be843bSPierre Pronchery             fprintf(stderr, "Unknown stream failure\n");
402*e7be843bSPierre Pronchery             break;
403*e7be843bSPierre Pronchery         }
404*e7be843bSPierre Pronchery         /*
405*e7be843bSPierre Pronchery          * If the failure is due to a verification error we can get more
406*e7be843bSPierre Pronchery          * information about it from SSL_get_verify_result().
407*e7be843bSPierre Pronchery          */
408*e7be843bSPierre Pronchery         if (SSL_get_verify_result(ssl) != X509_V_OK)
409*e7be843bSPierre Pronchery             fprintf(stderr, "Verify error: %s\n",
410*e7be843bSPierre Pronchery                     X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
411*e7be843bSPierre Pronchery         return -1;
412*e7be843bSPierre Pronchery 
413*e7be843bSPierre Pronchery     default:
414*e7be843bSPierre Pronchery         return -1;
415*e7be843bSPierre Pronchery     }
416*e7be843bSPierre Pronchery }
417*e7be843bSPierre Pronchery 
418*e7be843bSPierre Pronchery /**
419*e7be843bSPierre Pronchery  * @brief Processes a new incoming QUIC stream for an HTTP/0.9 GET request.
420*e7be843bSPierre Pronchery  *
421*e7be843bSPierre Pronchery  * This function reads an HTTP/0.9 GET request from the provided QUIC stream,
422*e7be843bSPierre Pronchery  * retrieves the requested file from the server's file system, and sends the
423*e7be843bSPierre Pronchery  * file contents back to the client over the stream.
424*e7be843bSPierre Pronchery  *
425*e7be843bSPierre Pronchery  * @param Pointer to the SSL object representing the QUIC stream.
426*e7be843bSPierre Pronchery  *
427*e7be843bSPierre Pronchery  * Operation:
428*e7be843bSPierre Pronchery  * - Reads the HTTP/0.9 GET request from the client.
429*e7be843bSPierre Pronchery  * - Parses the request to extract the requested file name.
430*e7be843bSPierre Pronchery  * - Constructs the file path using the `fileprefix` directory.
431*e7be843bSPierre Pronchery  * - Reads the requested file in chunks and sends it to the client.
432*e7be843bSPierre Pronchery  * - Concludes the QUIC stream once the file is fully sent.
433*e7be843bSPierre Pronchery  *
434*e7be843bSPierre Pronchery  * Error Handling:
435*e7be843bSPierre Pronchery  * - If the request is invalid or the file cannot be opened, appropriate
436*e7be843bSPierre Pronchery  *   error messages are logged, and the function exits without sending data.
437*e7be843bSPierre Pronchery  * - Errors during file reading or writing to the stream are handled, with
438*e7be843bSPierre Pronchery  *   retries for buffer-related issues (e.g., full send buffer).
439*e7be843bSPierre Pronchery  *
440*e7be843bSPierre Pronchery  * Notes:
441*e7be843bSPierre Pronchery  * - The request is expected to be a valid HTTP/0.9 GET request.
442*e7be843bSPierre Pronchery  * - File paths are sanitized to prevent path traversal vulnerabilities.
443*e7be843bSPierre Pronchery  * - The function uses blocking operations for reading and writing data.
444*e7be843bSPierre Pronchery  *
445*e7be843bSPierre Pronchery  * Usage:
446*e7be843bSPierre Pronchery  * - Called for each accepted QUIC stream to handle client requests.
447*e7be843bSPierre Pronchery  */
process_new_stream(SSL * stream)448*e7be843bSPierre Pronchery static void process_new_stream(SSL *stream)
449*e7be843bSPierre Pronchery {
450*e7be843bSPierre Pronchery     unsigned char buf[BUF_SIZE];
451*e7be843bSPierre Pronchery     char path[BUF_SIZE];
452*e7be843bSPierre Pronchery     char *req = (char *)buf;
453*e7be843bSPierre Pronchery     char *reqname;
454*e7be843bSPierre Pronchery     char *creturn;
455*e7be843bSPierre Pronchery     size_t nread;
456*e7be843bSPierre Pronchery     BIO *readbio;
457*e7be843bSPierre Pronchery     size_t bytes_read = 0;
458*e7be843bSPierre Pronchery     size_t bytes_written = 0;
459*e7be843bSPierre Pronchery     size_t offset = 0;
460*e7be843bSPierre Pronchery     int rc;
461*e7be843bSPierre Pronchery     int ret;
462*e7be843bSPierre Pronchery     size_t total_read = 0;
463*e7be843bSPierre Pronchery 
464*e7be843bSPierre Pronchery     memset(buf, 0, BUF_SIZE);
465*e7be843bSPierre Pronchery     for (;;) {
466*e7be843bSPierre Pronchery         nread = 0;
467*e7be843bSPierre Pronchery         ret = SSL_read_ex(stream, &buf[total_read],
468*e7be843bSPierre Pronchery                           sizeof(buf) - total_read - 1, &nread);
469*e7be843bSPierre Pronchery         total_read += nread;
470*e7be843bSPierre Pronchery         if (ret <= 0) {
471*e7be843bSPierre Pronchery             ret = handle_io_failure(stream, ret);
472*e7be843bSPierre Pronchery             if (ret == 0) {
473*e7be843bSPierre Pronchery                 /* EOF condition, fin bit set, we got the whole request */
474*e7be843bSPierre Pronchery                 break;
475*e7be843bSPierre Pronchery             } else {
476*e7be843bSPierre Pronchery                 /* permanent failure, abort */
477*e7be843bSPierre Pronchery                 fprintf(stderr, "Failure on stream\n");
478*e7be843bSPierre Pronchery                 return;
479*e7be843bSPierre Pronchery             }
480*e7be843bSPierre Pronchery         }
481*e7be843bSPierre Pronchery     }
482*e7be843bSPierre Pronchery 
483*e7be843bSPierre Pronchery     /* We should have a valid http 0.9 GET request here */
484*e7be843bSPierre Pronchery     fprintf(stderr, "Request is %s\n", req);
485*e7be843bSPierre Pronchery 
486*e7be843bSPierre Pronchery     /* Look for the last '/' char in the request */
487*e7be843bSPierre Pronchery     reqname = strrchr(req, '/');
488*e7be843bSPierre Pronchery     if (reqname == NULL)
489*e7be843bSPierre Pronchery         return;
490*e7be843bSPierre Pronchery     reqname++;
491*e7be843bSPierre Pronchery 
492*e7be843bSPierre Pronchery     /* Requests have a trailing \r\n, eliminate them */
493*e7be843bSPierre Pronchery     creturn = strchr(reqname, '\r');
494*e7be843bSPierre Pronchery     if (creturn != NULL)
495*e7be843bSPierre Pronchery         *creturn = '\0';
496*e7be843bSPierre Pronchery 
497*e7be843bSPierre Pronchery     snprintf(path, BUF_SIZE, "%s/%s", fileprefix, reqname);
498*e7be843bSPierre Pronchery 
499*e7be843bSPierre Pronchery     fprintf(stderr, "Serving %s\n", path);
500*e7be843bSPierre Pronchery     readbio = BIO_new_file(path, "r");
501*e7be843bSPierre Pronchery     if (readbio == NULL) {
502*e7be843bSPierre Pronchery         fprintf(stderr, "Unable to open %s\n", path);
503*e7be843bSPierre Pronchery         ERR_print_errors_fp(stderr);
504*e7be843bSPierre Pronchery         goto done;
505*e7be843bSPierre Pronchery     }
506*e7be843bSPierre Pronchery 
507*e7be843bSPierre Pronchery     /* Read the readbio file into a buffer, and just send it to the requestor */
508*e7be843bSPierre Pronchery     while (BIO_eof(readbio) <= 0) {
509*e7be843bSPierre Pronchery         bytes_read = 0;
510*e7be843bSPierre Pronchery         if (!BIO_read_ex(readbio, buf, BUF_SIZE, &bytes_read)) {
511*e7be843bSPierre Pronchery             if (BIO_eof(readbio) <= 0) {
512*e7be843bSPierre Pronchery                 fprintf(stderr, "Failed to read from %s\n", path);
513*e7be843bSPierre Pronchery                 ERR_print_errors_fp(stderr);
514*e7be843bSPierre Pronchery                 goto out;
515*e7be843bSPierre Pronchery             } else {
516*e7be843bSPierre Pronchery                 break;
517*e7be843bSPierre Pronchery             }
518*e7be843bSPierre Pronchery         }
519*e7be843bSPierre Pronchery 
520*e7be843bSPierre Pronchery         offset = 0;
521*e7be843bSPierre Pronchery         for (;;) {
522*e7be843bSPierre Pronchery             bytes_written = 0;
523*e7be843bSPierre Pronchery             rc = SSL_write_ex(stream, &buf[offset], bytes_read, &bytes_written);
524*e7be843bSPierre Pronchery             if (rc <= 0) {
525*e7be843bSPierre Pronchery                 rc = SSL_get_error(stream, rc);
526*e7be843bSPierre Pronchery                 switch (rc) {
527*e7be843bSPierre Pronchery                 case SSL_ERROR_WANT_WRITE:
528*e7be843bSPierre Pronchery                     fprintf(stderr, "Send buffer full, retrying\n");
529*e7be843bSPierre Pronchery                     continue;
530*e7be843bSPierre Pronchery                     break;
531*e7be843bSPierre Pronchery                 default:
532*e7be843bSPierre Pronchery                     fprintf(stderr, "Unhandled error cause %d\n", rc);
533*e7be843bSPierre Pronchery                     goto done;
534*e7be843bSPierre Pronchery                     break;
535*e7be843bSPierre Pronchery                 }
536*e7be843bSPierre Pronchery             }
537*e7be843bSPierre Pronchery             bytes_read -= bytes_written;
538*e7be843bSPierre Pronchery             offset += bytes_written;
539*e7be843bSPierre Pronchery             bytes_written = 0;
540*e7be843bSPierre Pronchery             if (bytes_read == 0)
541*e7be843bSPierre Pronchery                 break;
542*e7be843bSPierre Pronchery         }
543*e7be843bSPierre Pronchery     }
544*e7be843bSPierre Pronchery 
545*e7be843bSPierre Pronchery done:
546*e7be843bSPierre Pronchery     if (!SSL_stream_conclude(stream, 0))
547*e7be843bSPierre Pronchery         fprintf(stderr, "Failed to conclude stream\n");
548*e7be843bSPierre Pronchery 
549*e7be843bSPierre Pronchery out:
550*e7be843bSPierre Pronchery     BIO_free(readbio);
551*e7be843bSPierre Pronchery     return;
552*e7be843bSPierre Pronchery }
553*e7be843bSPierre Pronchery 
554*e7be843bSPierre Pronchery /**
555*e7be843bSPierre Pronchery  * @brief Runs the QUIC server to accept and handle client connections.
556*e7be843bSPierre Pronchery  *
557*e7be843bSPierre Pronchery  * This function initializes a QUIC listener, binds it to the provided UDP
558*e7be843bSPierre Pronchery  * socket, and enters a loop to accept client connections and process incoming
559*e7be843bSPierre Pronchery  * QUIC streams. Each connection is handled until termination, and streams are
560*e7be843bSPierre Pronchery  * processed individually using the `process_new_stream` function.
561*e7be843bSPierre Pronchery  *
562*e7be843bSPierre Pronchery  * @param ctx Pointer to the SSL_CTX object configured for QUIC.
563*e7be843bSPierre Pronchery  * @param sock  BIO of the bound UDP socket.
564*e7be843bSPierre Pronchery  *
565*e7be843bSPierre Pronchery  * @return Returns 0 on error; otherwise, the server runs indefinitely.
566*e7be843bSPierre Pronchery  *
567*e7be843bSPierre Pronchery  * Operation:
568*e7be843bSPierre Pronchery  * - Creates a QUIC listener using the provided SSL_CTX and associates it
569*e7be843bSPierre Pronchery  *   with the specified UDP socket.
570*e7be843bSPierre Pronchery  * - Waits for incoming QUIC connections and accepts them.
571*e7be843bSPierre Pronchery  * - For each connection:
572*e7be843bSPierre Pronchery  *   - Accepts incoming streams.
573*e7be843bSPierre Pronchery  *   - Processes each stream using `process_new_stream`.
574*e7be843bSPierre Pronchery  *   - Shuts down the connection upon completion.
575*e7be843bSPierre Pronchery  *
576*e7be843bSPierre Pronchery  * Error Handling:
577*e7be843bSPierre Pronchery  * - If listener creation or connection acceptance fails, the function logs
578*e7be843bSPierre Pronchery  *   an error message and exits the loop.
579*e7be843bSPierre Pronchery  * - Cleans up allocated resources (e.g., listener, connection) on failure.
580*e7be843bSPierre Pronchery  *
581*e7be843bSPierre Pronchery  * Usage:
582*e7be843bSPierre Pronchery  * - Call this function in the main server loop after setting up the
583*e7be843bSPierre Pronchery  *   SSL_CTX and binding a UDP socket.
584*e7be843bSPierre Pronchery  *
585*e7be843bSPierre Pronchery  * Notes:
586*e7be843bSPierre Pronchery  * - Uses blocking operations for listener, connection, and stream handling.
587*e7be843bSPierre Pronchery  * - Incoming streams are processed based on the configured stream policy.
588*e7be843bSPierre Pronchery  * - The server runs in an infinite loop unless a fatal error occurs.
589*e7be843bSPierre Pronchery  */
run_quic_server(SSL_CTX * ctx,BIO * sock)590*e7be843bSPierre Pronchery static int run_quic_server(SSL_CTX *ctx, BIO *sock)
591*e7be843bSPierre Pronchery {
592*e7be843bSPierre Pronchery     int ok = 0;
593*e7be843bSPierre Pronchery     SSL *listener, *conn, *stream;
594*e7be843bSPierre Pronchery     unsigned long errcode;
595*e7be843bSPierre Pronchery     uint64_t flags = 0;
596*e7be843bSPierre Pronchery 
597*e7be843bSPierre Pronchery     /*
598*e7be843bSPierre Pronchery      * If NO_ADDR_VALIDATE exists in our environment
599*e7be843bSPierre Pronchery      * then disable address validation on our listener
600*e7be843bSPierre Pronchery      */
601*e7be843bSPierre Pronchery     if (getenv("NO_ADDR_VALIDATE") != NULL)
602*e7be843bSPierre Pronchery         flags |= SSL_LISTENER_FLAG_NO_VALIDATE;
603*e7be843bSPierre Pronchery 
604*e7be843bSPierre Pronchery     /*
605*e7be843bSPierre Pronchery      * Create a new QUIC listener. Listeners, and other QUIC objects, default
606*e7be843bSPierre Pronchery      * to operating in blocking mode. The configured behaviour is inherited by
607*e7be843bSPierre Pronchery      * child objects.
608*e7be843bSPierre Pronchery      */
609*e7be843bSPierre Pronchery     if ((listener = SSL_new_listener(ctx, flags)) == NULL)
610*e7be843bSPierre Pronchery         goto err;
611*e7be843bSPierre Pronchery 
612*e7be843bSPierre Pronchery     /* Provide the listener with our UDP socket. */
613*e7be843bSPierre Pronchery     SSL_set_bio(listener, sock, sock);
614*e7be843bSPierre Pronchery 
615*e7be843bSPierre Pronchery     /* Begin listening. */
616*e7be843bSPierre Pronchery     if (!SSL_listen(listener))
617*e7be843bSPierre Pronchery         goto err;
618*e7be843bSPierre Pronchery 
619*e7be843bSPierre Pronchery     /*
620*e7be843bSPierre Pronchery      * Begin an infinite loop of listening for connections. We will only
621*e7be843bSPierre Pronchery      * exit this loop if we encounter an error.
622*e7be843bSPierre Pronchery      */
623*e7be843bSPierre Pronchery     for (;;) {
624*e7be843bSPierre Pronchery         /* Pristine error stack for each new connection */
625*e7be843bSPierre Pronchery         ERR_clear_error();
626*e7be843bSPierre Pronchery 
627*e7be843bSPierre Pronchery         /* Block while waiting for a client connection */
628*e7be843bSPierre Pronchery         printf("Waiting for connection\n");
629*e7be843bSPierre Pronchery         conn = SSL_accept_connection(listener, 0);
630*e7be843bSPierre Pronchery         if (conn == NULL) {
631*e7be843bSPierre Pronchery             fprintf(stderr, "error while accepting connection\n");
632*e7be843bSPierre Pronchery             goto err;
633*e7be843bSPierre Pronchery         }
634*e7be843bSPierre Pronchery         printf("Accepted new connection\n");
635*e7be843bSPierre Pronchery 
636*e7be843bSPierre Pronchery         /*
637*e7be843bSPierre Pronchery          * QUIC requires that we inform the connection that
638*e7be843bSPierre Pronchery          * we always want to accept new streams, rather than reject them
639*e7be843bSPierre Pronchery          * Additionally, while we don't make an explicit call here, we
640*e7be843bSPierre Pronchery          * are using the default stream mode, as would be specified by
641*e7be843bSPierre Pronchery          * a call to SSL_set_default_stream_mode
642*e7be843bSPierre Pronchery          */
643*e7be843bSPierre Pronchery         if (!SSL_set_incoming_stream_policy(conn,
644*e7be843bSPierre Pronchery                                             SSL_INCOMING_STREAM_POLICY_ACCEPT,
645*e7be843bSPierre Pronchery                                             0)) {
646*e7be843bSPierre Pronchery             fprintf(stderr, "Failed to set incomming stream policy\n");
647*e7be843bSPierre Pronchery             goto close_conn;
648*e7be843bSPierre Pronchery         }
649*e7be843bSPierre Pronchery 
650*e7be843bSPierre Pronchery         /*
651*e7be843bSPierre Pronchery          * Until the connection is closed, accept incomming stream
652*e7be843bSPierre Pronchery          * requests and serve them
653*e7be843bSPierre Pronchery          */
654*e7be843bSPierre Pronchery         for (;;) {
655*e7be843bSPierre Pronchery             /*
656*e7be843bSPierre Pronchery              * Note that SSL_accept_stream is blocking here, as the
657*e7be843bSPierre Pronchery              * conn SSL object inherited the deafult blocking property
658*e7be843bSPierre Pronchery              * from its parent, the listener SSL object.  As such there
659*e7be843bSPierre Pronchery              * is no need to handle retry failures here.
660*e7be843bSPierre Pronchery              */
661*e7be843bSPierre Pronchery             stream = SSL_accept_stream(conn, 0);
662*e7be843bSPierre Pronchery             if (stream == NULL) {
663*e7be843bSPierre Pronchery                 /*
664*e7be843bSPierre Pronchery                  * If we don't get a stream, either we
665*e7be843bSPierre Pronchery                  * Hit a legitimate error, and should bail out
666*e7be843bSPierre Pronchery                  * or
667*e7be843bSPierre Pronchery                  * The Client closed the connection, and there are no
668*e7be843bSPierre Pronchery                  * more incomming streams expected
669*e7be843bSPierre Pronchery                  *
670*e7be843bSPierre Pronchery                  * Filter on the shutdown error, and only print an error
671*e7be843bSPierre Pronchery                  * message if the cause is not SHUTDOWN
672*e7be843bSPierre Pronchery                  */
673*e7be843bSPierre Pronchery                 ERR_print_errors_fp(stderr);
674*e7be843bSPierre Pronchery                 errcode = ERR_get_error();
675*e7be843bSPierre Pronchery                 if (ERR_GET_REASON(errcode) != SSL_R_PROTOCOL_IS_SHUTDOWN)
676*e7be843bSPierre Pronchery                     fprintf(stderr, "Failure in accept stream, error %s\n",
677*e7be843bSPierre Pronchery                             ERR_reason_error_string(errcode));
678*e7be843bSPierre Pronchery                 break;
679*e7be843bSPierre Pronchery             }
680*e7be843bSPierre Pronchery             process_new_stream(stream);
681*e7be843bSPierre Pronchery             SSL_free(stream);
682*e7be843bSPierre Pronchery         }
683*e7be843bSPierre Pronchery 
684*e7be843bSPierre Pronchery         /*
685*e7be843bSPierre Pronchery          * Shut down the connection. We may need to call this multiple times
686*e7be843bSPierre Pronchery          * to ensure the connection is shutdown completely.
687*e7be843bSPierre Pronchery          */
688*e7be843bSPierre Pronchery close_conn:
689*e7be843bSPierre Pronchery         while (SSL_shutdown(conn) != 1)
690*e7be843bSPierre Pronchery             continue;
691*e7be843bSPierre Pronchery 
692*e7be843bSPierre Pronchery         SSL_free(conn);
693*e7be843bSPierre Pronchery     }
694*e7be843bSPierre Pronchery 
695*e7be843bSPierre Pronchery err:
696*e7be843bSPierre Pronchery     SSL_free(listener);
697*e7be843bSPierre Pronchery     return ok;
698*e7be843bSPierre Pronchery }
699*e7be843bSPierre Pronchery 
700*e7be843bSPierre Pronchery /**
701*e7be843bSPierre Pronchery  * @brief Entry point for the minimal QUIC HTTP/0.9 server.
702*e7be843bSPierre Pronchery  *
703*e7be843bSPierre Pronchery  * This function initializes the server, sets up a QUIC context, binds a UDP
704*e7be843bSPierre Pronchery  * socket to the specified port, and starts the main QUIC server loop to handle
705*e7be843bSPierre Pronchery  * client connections and requests.
706*e7be843bSPierre Pronchery  *
707*e7be843bSPierre Pronchery  * @param argc Number of command-line arguments.
708*e7be843bSPierre Pronchery  * @param argv Array of command-line arguments:
709*e7be843bSPierre Pronchery  *             - argv[0]: Program name.
710*e7be843bSPierre Pronchery  *             - argv[1]: Port number to bind the server.
711*e7be843bSPierre Pronchery  *             - argv[2]: Path to the server's certificate file (PEM format).
712*e7be843bSPierre Pronchery  *             - argv[3]: Path to the server's private key file (PEM format).
713*e7be843bSPierre Pronchery  *
714*e7be843bSPierre Pronchery  * @return Returns EXIT_SUCCESS on successful execution, or EXIT_FAILURE
715*e7be843bSPierre Pronchery  *         on error.
716*e7be843bSPierre Pronchery  *
717*e7be843bSPierre Pronchery  * Operation:
718*e7be843bSPierre Pronchery  * - Validates the command-line arguments.
719*e7be843bSPierre Pronchery  * - Reads the FILEPREFIX environment variable to set the file prefix for
720*e7be843bSPierre Pronchery  *   serving files (default is "./downloads").
721*e7be843bSPierre Pronchery  * - Creates an SSL_CTX with QUIC support using the provided certificate and
722*e7be843bSPierre Pronchery  *   key files.
723*e7be843bSPierre Pronchery  * - Parses and validates the port number.
724*e7be843bSPierre Pronchery  * - Creates and binds a UDP socket to the specified port.
725*e7be843bSPierre Pronchery  * - Starts the server loop using `run_quic_server` to accept and process
726*e7be843bSPierre Pronchery  *   client connections.
727*e7be843bSPierre Pronchery  *
728*e7be843bSPierre Pronchery  * Error Handling:
729*e7be843bSPierre Pronchery  * - If any initialization step fails (e.g., invalid arguments, socket
730*e7be843bSPierre Pronchery  *   creation, context setup), appropriate error messages are logged, and
731*e7be843bSPierre Pronchery  *   the program exits with EXIT_FAILURE.
732*e7be843bSPierre Pronchery  *
733*e7be843bSPierre Pronchery  * Usage:
734*e7be843bSPierre Pronchery  * - Run the program with the required arguments to start the server:
735*e7be843bSPierre Pronchery  *   `./server <port> <server.crt> <server.key>`
736*e7be843bSPierre Pronchery  *
737*e7be843bSPierre Pronchery  * Notes:
738*e7be843bSPierre Pronchery  * - Ensure that the certificate and key files exist and are valid.
739*e7be843bSPierre Pronchery  * - The server serves files from the directory specified by FILEPREFIX.
740*e7be843bSPierre Pronchery  */
main(int argc,char * argv[])741*e7be843bSPierre Pronchery int main(int argc, char *argv[])
742*e7be843bSPierre Pronchery {
743*e7be843bSPierre Pronchery     int res = EXIT_FAILURE;
744*e7be843bSPierre Pronchery     SSL_CTX *ctx = NULL;
745*e7be843bSPierre Pronchery     BIO *sock = NULL;
746*e7be843bSPierre Pronchery     unsigned long port;
747*e7be843bSPierre Pronchery 
748*e7be843bSPierre Pronchery     if (argc != 4) {
749*e7be843bSPierre Pronchery         fprintf(stderr, "usage: %s <port> <server.crt> <server.key>\n", argv[0]);
750*e7be843bSPierre Pronchery         goto out;
751*e7be843bSPierre Pronchery     }
752*e7be843bSPierre Pronchery 
753*e7be843bSPierre Pronchery     fileprefix = getenv("FILEPREFIX");
754*e7be843bSPierre Pronchery     if (fileprefix == NULL)
755*e7be843bSPierre Pronchery         fileprefix = "./downloads";
756*e7be843bSPierre Pronchery 
757*e7be843bSPierre Pronchery     fprintf(stderr, "Fileprefix is %s\n", fileprefix);
758*e7be843bSPierre Pronchery 
759*e7be843bSPierre Pronchery     /* Create SSL_CTX that supports QUIC. */
760*e7be843bSPierre Pronchery     if ((ctx = create_ctx(argv[2], argv[3])) == NULL) {
761*e7be843bSPierre Pronchery         ERR_print_errors_fp(stderr);
762*e7be843bSPierre Pronchery         fprintf(stderr, "Failed to create context\n");
763*e7be843bSPierre Pronchery         goto out;
764*e7be843bSPierre Pronchery     }
765*e7be843bSPierre Pronchery 
766*e7be843bSPierre Pronchery     /* Parse port number from command line arguments. */
767*e7be843bSPierre Pronchery     port = strtoul(argv[1], NULL, 0);
768*e7be843bSPierre Pronchery     if (port == 0 || port > UINT16_MAX) {
769*e7be843bSPierre Pronchery         fprintf(stderr, "Failed to parse port number\n");
770*e7be843bSPierre Pronchery         goto out;
771*e7be843bSPierre Pronchery     }
772*e7be843bSPierre Pronchery     fprintf(stderr, "Binding to port %lu\n", port);
773*e7be843bSPierre Pronchery 
774*e7be843bSPierre Pronchery     /* Create and bind a UDP socket. */
775*e7be843bSPierre Pronchery     if ((sock = create_socket((uint16_t)port)) == NULL) {
776*e7be843bSPierre Pronchery         ERR_print_errors_fp(stderr);
777*e7be843bSPierre Pronchery         fprintf(stderr, "Failed to create socket\n");
778*e7be843bSPierre Pronchery         goto out;
779*e7be843bSPierre Pronchery     }
780*e7be843bSPierre Pronchery 
781*e7be843bSPierre Pronchery     /* QUIC server connection acceptance loop. */
782*e7be843bSPierre Pronchery     if (!run_quic_server(ctx, sock)) {
783*e7be843bSPierre Pronchery         ERR_print_errors_fp(stderr);
784*e7be843bSPierre Pronchery         fprintf(stderr, "Failed to run quic server\n");
785*e7be843bSPierre Pronchery         goto out;
786*e7be843bSPierre Pronchery     }
787*e7be843bSPierre Pronchery 
788*e7be843bSPierre Pronchery     res = EXIT_SUCCESS;
789*e7be843bSPierre Pronchery out:
790*e7be843bSPierre Pronchery     /* Free resources. */
791*e7be843bSPierre Pronchery     SSL_CTX_free(ctx);
792*e7be843bSPierre Pronchery     BIO_free(sock);
793*e7be843bSPierre Pronchery     return res;
794*e7be843bSPierre Pronchery }
795