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