| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- /*
- * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- /**
- * @file quic-hq-interop-server.c
- * @brief Minimal QUIC HTTP/0.9 server implementation.
- *
- * This file implements a lightweight QUIC server supporting the HTTP/0.9
- * protocol for interoperability testing. It includes functions for setting
- * up a secure QUIC connection, handling ALPN negotiation, and serving client
- * requests. Intended for use with the quic-interop-runner
- * available at https://interop.seemann.io
- *
- * Key functionalities:
- * - Setting up SSL_CTX with QUIC support.
- * - Negotiating ALPN strings during the TLS handshake.
- * - Listening and accepting incoming QUIC connections.
- * - Handling client requests via HTTP/0.9 protocol.
- *
- * Usage:
- * <port> <server.crt> <server.key>
- * The server binds to the specified port and serves files using the given
- * certificate and private key.
- *
- * Environment variables:
- * - FILEPREFIX: Specifies the directory containing files to serve.
- * Defaults to "./downloads" if not set.
- * - SSLKEYLOGFILE: specifies that keylogging should be preformed on the server
- * should be set to a file name to record keylog data to
- * - NO_ADDR_VALIDATE: Disables server address validation of clients
- *
- */
- #include <string.h>
- /* Include the appropriate header file for SOCK_STREAM */
- #ifdef _WIN32
- # include <stdarg.h>
- # include <winsock2.h>
- # include <ws2tcpip.h>
- #else
- # include <sys/socket.h>
- # include <netinet/in.h>
- # include <unistd.h>
- #endif
- #include <openssl/bio.h>
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #include <openssl/quic.h>
- #define BUF_SIZE 4096
- /**
- * @brief ALPN (Application-Layer Protocol Negotiation) identifier for QUIC.
- *
- * This constant defines the ALPN string used during the TLS handshake
- * to negotiate the application-layer protocol between the client and
- * the server. It specifies "hq-interop" as the supported protocol.
- *
- * Format:
- * - The first byte represents the length of the ALPN string.
- * - Subsequent bytes represent the ASCII characters of the protocol name.
- *
- * Value:
- * - Protocol: "hq-interop"
- * - Length: 10 bytes
- *
- * Usage:
- * This is passed to the ALPN callback function to validate and
- * negotiate the desired protocol during the TLS handshake.
- */
- static const unsigned char alpn_ossltest[] = {
- 10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p',
- };
- /**
- * @brief Directory prefix for serving requested files.
- *
- * This variable specifies the directory path used as the base location
- * for serving files in response to client requests. It is used to construct
- * the full file path for requested resources.
- *
- * Default:
- * - If not set via the FILEPREFIX environment variable, it defaults to
- * "./downloads".
- *
- * Usage:
- * - Updated at runtime based on the FILEPREFIX environment variable.
- * - Used to locate and serve files during incoming requests.
- */
- static char *fileprefix = NULL;
- /**
- * @brief Callback for ALPN (Application-Layer Protocol Negotiation) selection.
- *
- * This function is invoked during the TLS handshake on the server side to
- * validate and negotiate the desired ALPN (Application-Layer Protocol
- * Negotiation) protocol with the client. It ensures that the negotiated
- * protocol matches the predefined "hq-interop" string.
- *
- * @param ssl Pointer to the SSL connection object.
- * @param[out] out Pointer to the negotiated ALPN protocol string.
- * @param[out] out_len Length of the negotiated ALPN protocol string.
- * @param in Pointer to the client-provided ALPN protocol list.
- * @param in_len Length of the client-provided ALPN protocol list.
- * @param arg Optional user-defined argument (unused in this context).
- *
- * @return SSL_TLSEXT_ERR_OK on successful ALPN negotiation,
- * SSL_TLSEXT_ERR_ALERT_FATAL otherwise.
- *
- * Usage:
- * - This function is set as the ALPN selection callback in the SSL_CTX
- * using `SSL_CTX_set_alpn_select_cb`.
- * - Ensures that only the predefined ALPN protocol is accepted.
- *
- * Note:
- * - The predefined protocol is specified in the `alpn_ossltest` array.
- */
- static int select_alpn(SSL *ssl, const unsigned char **out,
- unsigned char *out_len, const unsigned char *in,
- unsigned int in_len, void *arg)
- {
- /*
- * Use the next_proto helper function here.
- * This scans the list of alpns we support and matches against
- * what the client is requesting
- */
- 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;
- }
- /**
- * @brief Creates and configures an SSL_CTX for a QUIC server.
- *
- * This function initializes an SSL_CTX object with the QUIC server method
- * and configures it using the provided certificate and private key. The
- * context is prepared for handling secure QUIC connections and performing
- * ALPN (Application-Layer Protocol Negotiation).
- *
- * @param cert_path Path to the server's certificate chain file in PEM format.
- * The chain file must include the server's leaf certificate
- * followed by intermediate CA certificates.
- * @param key_path Path to the server's private key file in PEM format. The
- * private key must correspond to the leaf certificate in
- * the chain file.
- *
- * @return Pointer to the initialized SSL_CTX on success, or NULL on failure.
- *
- * Configuration:
- * - Loads the certificate chain and private key into the context.
- * - Disables client certificate verification (no mutual TLS).
- * - Sets up the ALPN selection callback for protocol negotiation.
- *
- * Error Handling:
- * - If any step fails (e.g., loading the certificate or key), the function
- * frees the SSL_CTX and returns NULL.
- *
- * Usage:
- * - Call this function to create an SSL_CTX before starting the QUIC server.
- * - Ensure valid paths for the certificate and private key are provided.
- *
- * Note:
- * - The ALPN callback only supports the predefined protocol defined in
- * `alpn_ossltest`.
- */
- static SSL_CTX *create_ctx(const char *cert_path, const char *key_path)
- {
- SSL_CTX *ctx;
- /*
- * An SSL_CTX holds shared configuration information for multiple
- * subsequent per-client connections. We specifically load a QUIC
- * server method here.
- */
- ctx = SSL_CTX_new(OSSL_QUIC_server_method());
- if (ctx == NULL)
- goto err;
- /*
- * Load the server's certificate *chain* file (PEM format), which includes
- * not only the leaf (end-entity) server certificate, but also any
- * intermediate issuer-CA certificates. The leaf certificate must be the
- * first certificate in the file.
- *
- * In advanced use-cases this can be called multiple times, once per public
- * key algorithm for which the server has a corresponding certificate.
- * However, the corresponding private key (see below) must be loaded first,
- * *before* moving on to the next chain file.
- *
- * The requisite files "chain.pem" and "pkey.pem" can be generated by running
- * "make chain" in this directory. If the server will be executed from some
- * other directory, move or copy the files there.
- */
- if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) {
- fprintf(stderr, "couldn't load certificate file: %s\n", cert_path);
- goto err;
- }
- /*
- * Load the corresponding private key, this also checks that the private
- * key matches the just loaded end-entity certificate. It does not check
- * whether the certificate chain is valid, the certificates could be
- * expired, or may otherwise fail to form a chain that a client can validate.
- */
- if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
- fprintf(stderr, "couldn't load key file: %s\n", key_path);
- goto err;
- }
- /*
- * Since we're not soliciting or processing client certificates, we don't
- * need to configure a trusted-certificate store, so no call to
- * SSL_CTX_set_default_verify_paths() is needed. The server's own
- * certificate chain is assumed valid.
- */
- SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
- /* Setup ALPN negotiation callback to decide which ALPN is accepted. */
- SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
- return ctx;
- err:
- SSL_CTX_free(ctx);
- return NULL;
- }
- /**
- * @brief Creates and binds a UDP socket to the specified port.
- *
- * This function initializes a new UDP socket, binds it to the specified
- * port on the local host, and returns the socket file descriptor for
- * further use.
- *
- * @param port The port number to which the UDP socket should be bound.
- *
- * @return On success, returns the BIO of the created socket.
- * On failure, returns NULL.
- *
- * Steps:
- * - Creates a new UDP socket using the `socket` system call.
- * - Configures the socket address structure to bind to the specified port
- * on the local host.
- * - Binds the socket to the port using the `bind` system call.
- *
- * Error Handling:
- * - If socket creation or binding fails, an error message is printed to
- * `stderr`, the socket (if created) is closed, and -1 is returned.
- *
- * Usage:
- * - Call this function to set up a socket for handling incoming QUIC
- * connections.
- *
- * Notes:
- * - This function assumes UDP (`SOCK_DGRAM`).
- * - This function accepts on both IPv4 and IPv6.
- * - The specified port is converted to network byte order using `htons`.
- */
- static BIO *create_socket(uint16_t port)
- {
- int fd = -1;
- BIO *sock = NULL;
- BIO_ADDR *addr = NULL;
- int opt = 0;
- #ifdef _WIN32
- struct in6_addr in6addr_any;
- memset(&in6addr_any, 0, sizeof(in6addr_any));
- #endif
- /* Retrieve the file descriptor for a new UDP socket */
- if ((fd = BIO_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) {
- fprintf(stderr, "cannot create socket");
- goto err;
- }
- /*
- * IPv6_V6ONLY is only available on some platforms. If it is defined,
- * disable it to accept both IPv4 and IPv6 connections. Otherwise, the
- * server will only accept IPv6 connections.
- */
- #ifdef IPV6_V6ONLY
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
- fprintf(stderr, "setsockopt IPV6_V6ONLY failed");
- goto err;
- }
- #endif
- /*
- * Create a new BIO_ADDR
- */
- addr = BIO_ADDR_new();
- if (addr == NULL) {
- fprintf(stderr, "Unable to create BIO_ADDR\n");
- goto err;
- }
- /*
- * Build an INADDR_ANY BIO_ADDR
- */
- if (!BIO_ADDR_rawmake(addr, AF_INET6, &in6addr_any, sizeof(in6addr_any), htons(port))) {
- fprintf(stderr, "unable to bind to port %d\n", port);
- goto err;
- }
- /* Bind to the new UDP socket */
- if (!BIO_bind(fd, addr, 0)) {
- fprintf(stderr, "cannot bind to %u\n", port);
- goto err;
- }
- /*
- * Create a new datagram socket
- */
- sock = BIO_new(BIO_s_datagram());
- if (sock == NULL) {
- fprintf(stderr, "cannot create dgram bio\n");
- goto err;
- }
- /*
- * associate the underlying socket with the dgram BIO
- */
- if (!BIO_set_fd(sock, fd, BIO_CLOSE)) {
- fprintf(stderr, "Unable to set fd of dgram sock\n");
- goto err;
- }
- /*
- * Free our allocated addr
- */
- BIO_ADDR_free(addr);
- return sock;
- err:
- BIO_ADDR_free(addr);
- BIO_free(sock);
- BIO_closesocket(fd);
- return NULL;
- }
- /**
- * @brief Handles I/O failures on an SSL stream based on the result code.
- *
- * This function processes the result of an SSL I/O operation and handles
- * different types of errors that may occur during the operation. It takes
- * appropriate actions such as retrying the operation, reporting errors, or
- * returning specific status codes based on the error type.
- *
- * @param ssl A pointer to the SSL object representing the stream.
- * @param res The result code from the SSL I/O operation.
- * @return An integer indicating the outcome:
- * - 0: EOF, indicating the stream has been closed.
- * - -1: A fatal error occurred or the stream has been reset.
- *
- *
- * @note If the failure is due to an SSL verification error, additional
- * information will be logged to stderr.
- */
- static int handle_io_failure(SSL *ssl, int res)
- {
- switch (SSL_get_error(ssl, res)) {
- case SSL_ERROR_ZERO_RETURN:
- /* EOF */
- return 0;
- case SSL_ERROR_SYSCALL:
- return -1;
- case SSL_ERROR_SSL:
- /*
- * Some stream fatal error occurred. This could be because of a
- * stream reset - or some failure occurred on the underlying
- * connection.
- */
- switch (SSL_get_stream_read_state(ssl)) {
- case SSL_STREAM_STATE_RESET_REMOTE:
- fprintf(stderr, "Stream reset occurred\n");
- /*
- * The stream has been reset but the connection is still
- * healthy.
- */
- break;
- case SSL_STREAM_STATE_CONN_CLOSED:
- fprintf(stderr, "Connection closed\n");
- /* Connection is already closed. */
- break;
- default:
- fprintf(stderr, "Unknown stream failure\n");
- break;
- }
- /*
- * If the failure is due to a verification error we can get more
- * information about it from SSL_get_verify_result().
- */
- if (SSL_get_verify_result(ssl) != X509_V_OK)
- fprintf(stderr, "Verify error: %s\n",
- X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
- return -1;
- default:
- return -1;
- }
- }
- /**
- * @brief Processes a new incoming QUIC stream for an HTTP/0.9 GET request.
- *
- * This function reads an HTTP/0.9 GET request from the provided QUIC stream,
- * retrieves the requested file from the server's file system, and sends the
- * file contents back to the client over the stream.
- *
- * @param Pointer to the SSL object representing the QUIC stream.
- *
- * Operation:
- * - Reads the HTTP/0.9 GET request from the client.
- * - Parses the request to extract the requested file name.
- * - Constructs the file path using the `fileprefix` directory.
- * - Reads the requested file in chunks and sends it to the client.
- * - Concludes the QUIC stream once the file is fully sent.
- *
- * Error Handling:
- * - If the request is invalid or the file cannot be opened, appropriate
- * error messages are logged, and the function exits without sending data.
- * - Errors during file reading or writing to the stream are handled, with
- * retries for buffer-related issues (e.g., full send buffer).
- *
- * Notes:
- * - The request is expected to be a valid HTTP/0.9 GET request.
- * - File paths are sanitized to prevent path traversal vulnerabilities.
- * - The function uses blocking operations for reading and writing data.
- *
- * Usage:
- * - Called for each accepted QUIC stream to handle client requests.
- */
- static void process_new_stream(SSL *stream)
- {
- unsigned char buf[BUF_SIZE];
- char path[BUF_SIZE];
- char *req = (char *)buf;
- char *reqname;
- char *creturn;
- size_t nread;
- BIO *readbio;
- size_t bytes_read = 0;
- size_t bytes_written = 0;
- size_t offset = 0;
- int rc;
- int ret;
- size_t total_read = 0;
- memset(buf, 0, BUF_SIZE);
- for (;;) {
- nread = 0;
- ret = SSL_read_ex(stream, &buf[total_read],
- sizeof(buf) - total_read - 1, &nread);
- total_read += nread;
- if (ret <= 0) {
- ret = handle_io_failure(stream, ret);
- if (ret == 0) {
- /* EOF condition, fin bit set, we got the whole request */
- break;
- } else {
- /* permanent failure, abort */
- fprintf(stderr, "Failure on stream\n");
- return;
- }
- }
- }
- /* We should have a valid http 0.9 GET request here */
- fprintf(stderr, "Request is %s\n", req);
- /* Look for the last '/' char in the request */
- reqname = strrchr(req, '/');
- if (reqname == NULL)
- return;
- reqname++;
- /* Requests have a trailing \r\n, eliminate them */
- creturn = strchr(reqname, '\r');
- if (creturn != NULL)
- *creturn = '\0';
- snprintf(path, BUF_SIZE, "%s/%s", fileprefix, reqname);
- fprintf(stderr, "Serving %s\n", path);
- readbio = BIO_new_file(path, "r");
- if (readbio == NULL) {
- fprintf(stderr, "Unable to open %s\n", path);
- ERR_print_errors_fp(stderr);
- goto done;
- }
- /* Read the readbio file into a buffer, and just send it to the requestor */
- while (BIO_eof(readbio) <= 0) {
- bytes_read = 0;
- if (!BIO_read_ex(readbio, buf, BUF_SIZE, &bytes_read)) {
- if (BIO_eof(readbio) <= 0) {
- fprintf(stderr, "Failed to read from %s\n", path);
- ERR_print_errors_fp(stderr);
- goto out;
- } else {
- break;
- }
- }
- offset = 0;
- for (;;) {
- bytes_written = 0;
- rc = SSL_write_ex(stream, &buf[offset], bytes_read, &bytes_written);
- if (rc <= 0) {
- rc = SSL_get_error(stream, rc);
- switch (rc) {
- case SSL_ERROR_WANT_WRITE:
- fprintf(stderr, "Send buffer full, retrying\n");
- continue;
- break;
- default:
- fprintf(stderr, "Unhandled error cause %d\n", rc);
- goto done;
- break;
- }
- }
- bytes_read -= bytes_written;
- offset += bytes_written;
- bytes_written = 0;
- if (bytes_read == 0)
- break;
- }
- }
- done:
- if (!SSL_stream_conclude(stream, 0))
- fprintf(stderr, "Failed to conclude stream\n");
- out:
- BIO_free(readbio);
- return;
- }
- /**
- * @brief Runs the QUIC server to accept and handle client connections.
- *
- * This function initializes a QUIC listener, binds it to the provided UDP
- * socket, and enters a loop to accept client connections and process incoming
- * QUIC streams. Each connection is handled until termination, and streams are
- * processed individually using the `process_new_stream` function.
- *
- * @param ctx Pointer to the SSL_CTX object configured for QUIC.
- * @param sock BIO of the bound UDP socket.
- *
- * @return Returns 0 on error; otherwise, the server runs indefinitely.
- *
- * Operation:
- * - Creates a QUIC listener using the provided SSL_CTX and associates it
- * with the specified UDP socket.
- * - Waits for incoming QUIC connections and accepts them.
- * - For each connection:
- * - Accepts incoming streams.
- * - Processes each stream using `process_new_stream`.
- * - Shuts down the connection upon completion.
- *
- * Error Handling:
- * - If listener creation or connection acceptance fails, the function logs
- * an error message and exits the loop.
- * - Cleans up allocated resources (e.g., listener, connection) on failure.
- *
- * Usage:
- * - Call this function in the main server loop after setting up the
- * SSL_CTX and binding a UDP socket.
- *
- * Notes:
- * - Uses blocking operations for listener, connection, and stream handling.
- * - Incoming streams are processed based on the configured stream policy.
- * - The server runs in an infinite loop unless a fatal error occurs.
- */
- static int run_quic_server(SSL_CTX *ctx, BIO *sock)
- {
- int ok = 0;
- SSL *listener, *conn, *stream;
- unsigned long errcode;
- uint64_t flags = 0;
- /*
- * If NO_ADDR_VALIDATE exists in our environment
- * then disable address validation on our listener
- */
- if (getenv("NO_ADDR_VALIDATE") != NULL)
- flags |= SSL_LISTENER_FLAG_NO_VALIDATE;
- /*
- * Create a new QUIC listener. Listeners, and other QUIC objects, default
- * to operating in blocking mode. The configured behaviour is inherited by
- * child objects.
- */
- if ((listener = SSL_new_listener(ctx, flags)) == NULL)
- goto err;
- /* Provide the listener with our UDP socket. */
- SSL_set_bio(listener, sock, sock);
- /* Begin listening. */
- if (!SSL_listen(listener))
- goto err;
- /*
- * Begin an infinite loop of listening for connections. We will only
- * exit this loop if we encounter an error.
- */
- for (;;) {
- /* Pristine error stack for each new connection */
- ERR_clear_error();
- /* Block while waiting for a client connection */
- printf("Waiting for connection\n");
- conn = SSL_accept_connection(listener, 0);
- if (conn == NULL) {
- fprintf(stderr, "error while accepting connection\n");
- goto err;
- }
- printf("Accepted new connection\n");
- /*
- * QUIC requires that we inform the connection that
- * we always want to accept new streams, rather than reject them
- * Additionally, while we don't make an explicit call here, we
- * are using the default stream mode, as would be specified by
- * a call to SSL_set_default_stream_mode
- */
- if (!SSL_set_incoming_stream_policy(conn,
- SSL_INCOMING_STREAM_POLICY_ACCEPT,
- 0)) {
- fprintf(stderr, "Failed to set incomming stream policy\n");
- goto close_conn;
- }
- /*
- * Until the connection is closed, accept incomming stream
- * requests and serve them
- */
- for (;;) {
- /*
- * Note that SSL_accept_stream is blocking here, as the
- * conn SSL object inherited the deafult blocking property
- * from its parent, the listener SSL object. As such there
- * is no need to handle retry failures here.
- */
- stream = SSL_accept_stream(conn, 0);
- if (stream == NULL) {
- /*
- * If we don't get a stream, either we
- * Hit a legitimate error, and should bail out
- * or
- * The Client closed the connection, and there are no
- * more incomming streams expected
- *
- * Filter on the shutdown error, and only print an error
- * message if the cause is not SHUTDOWN
- */
- ERR_print_errors_fp(stderr);
- errcode = ERR_get_error();
- if (ERR_GET_REASON(errcode) != SSL_R_PROTOCOL_IS_SHUTDOWN)
- fprintf(stderr, "Failure in accept stream, error %s\n",
- ERR_reason_error_string(errcode));
- break;
- }
- process_new_stream(stream);
- SSL_free(stream);
- }
- /*
- * Shut down the connection. We may need to call this multiple times
- * to ensure the connection is shutdown completely.
- */
- close_conn:
- while (SSL_shutdown(conn) != 1)
- continue;
- SSL_free(conn);
- }
- err:
- SSL_free(listener);
- return ok;
- }
- /**
- * @brief Entry point for the minimal QUIC HTTP/0.9 server.
- *
- * This function initializes the server, sets up a QUIC context, binds a UDP
- * socket to the specified port, and starts the main QUIC server loop to handle
- * client connections and requests.
- *
- * @param argc Number of command-line arguments.
- * @param argv Array of command-line arguments:
- * - argv[0]: Program name.
- * - argv[1]: Port number to bind the server.
- * - argv[2]: Path to the server's certificate file (PEM format).
- * - argv[3]: Path to the server's private key file (PEM format).
- *
- * @return Returns EXIT_SUCCESS on successful execution, or EXIT_FAILURE
- * on error.
- *
- * Operation:
- * - Validates the command-line arguments.
- * - Reads the FILEPREFIX environment variable to set the file prefix for
- * serving files (default is "./downloads").
- * - Creates an SSL_CTX with QUIC support using the provided certificate and
- * key files.
- * - Parses and validates the port number.
- * - Creates and binds a UDP socket to the specified port.
- * - Starts the server loop using `run_quic_server` to accept and process
- * client connections.
- *
- * Error Handling:
- * - If any initialization step fails (e.g., invalid arguments, socket
- * creation, context setup), appropriate error messages are logged, and
- * the program exits with EXIT_FAILURE.
- *
- * Usage:
- * - Run the program with the required arguments to start the server:
- * `./server <port> <server.crt> <server.key>`
- *
- * Notes:
- * - Ensure that the certificate and key files exist and are valid.
- * - The server serves files from the directory specified by FILEPREFIX.
- */
- int main(int argc, char *argv[])
- {
- int res = EXIT_FAILURE;
- SSL_CTX *ctx = NULL;
- BIO *sock = NULL;
- unsigned long port;
- if (argc != 4) {
- fprintf(stderr, "usage: %s <port> <server.crt> <server.key>\n", argv[0]);
- goto out;
- }
- fileprefix = getenv("FILEPREFIX");
- if (fileprefix == NULL)
- fileprefix = "./downloads";
- fprintf(stderr, "Fileprefix is %s\n", fileprefix);
- /* Create SSL_CTX that supports QUIC. */
- if ((ctx = create_ctx(argv[2], argv[3])) == NULL) {
- ERR_print_errors_fp(stderr);
- fprintf(stderr, "Failed to create context\n");
- goto out;
- }
- /* Parse port number from command line arguments. */
- port = strtoul(argv[1], NULL, 0);
- if (port == 0 || port > UINT16_MAX) {
- fprintf(stderr, "Failed to parse port number\n");
- goto out;
- }
- fprintf(stderr, "Binding to port %lu\n", port);
- /* Create and bind a UDP socket. */
- if ((sock = create_socket((uint16_t)port)) == NULL) {
- ERR_print_errors_fp(stderr);
- fprintf(stderr, "Failed to create socket\n");
- goto out;
- }
- /* QUIC server connection acceptance loop. */
- if (!run_quic_server(ctx, sock)) {
- ERR_print_errors_fp(stderr);
- fprintf(stderr, "Failed to run quic server\n");
- goto out;
- }
- res = EXIT_SUCCESS;
- out:
- /* Free resources. */
- SSL_CTX_free(ctx);
- BIO_free(sock);
- return res;
- }
|