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 */
select_alpn(SSL * ssl,const unsigned char ** out,unsigned char * out_len,const unsigned char * in,unsigned int in_len,void * arg)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 */
create_ctx(const char * cert_path,const char * key_path)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 */
create_socket(uint16_t port)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 */
handle_io_failure(SSL * ssl,int res)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 */
process_new_stream(SSL * stream)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 */
run_quic_server(SSL_CTX * ctx,BIO * sock)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 */
main(int argc,char * argv[])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