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