xref: /src/crypto/openssl/test/quic-openssl-docker/hq-interop/quic-hq-interop.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
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.c
12  * @brief QUIC client interoperability demo using OpenSSL.
13  *
14  * This file contains the implementation of a QUIC client that demonstrates
15  * interoperability with hq-interop servers. It handles connection setup,
16  * session caching, key logging, and sending HTTP GET requests over QUIC.
17  *
18  * The file includes functions for setting up SSL/TLS contexts, managing session
19  * caches, and handling I/O failures. It supports both IPv4 and IPv6 connections
20  * and uses non-blocking mode for QUIC operations.
21  *
22  * The client sends HTTP/1.0 GET requests for specified files and saves the
23  * responses to disk. It demonstrates OpenSSL's QUIC API, including ALPN protocol
24  * settings, peer address management, and SSL handshake and shutdown processes.
25  *
26  * @note This client is intended for demonstration purposes and may require
27  * additional error handling and robustness improvements for production use.
28  *
29  * USAGE
30  * quic-hq-interop <host> <port> <reqfile>
31  * host - The hostname of the server to contact
32  * port - The port that the server is listening on
33  * reqfile - a text file containing a space separated list of paths to fetch
34  *           via http 1.0 GET requests
35  *
36  * ENVIRONMENT VARIABLES
37  * SSLKEYLOGFILE - set to a file path to record keylog exchange with server
38  * SSL_SESSION_FILE - set to a file path to record ssl sessions and restore
39  *                    said sessions on next invocation
40  * SSL_CERT_FILE - The ca file to use when validating a server
41  * SSL_CIPHER_SUITES - The list of cipher suites to use (see openssl-ciphers)
42  */
43 #include <string.h>
44 
45 /* Include the appropriate header file for SOCK_DGRAM */
46 #ifdef _WIN32 /* Windows */
47 #include <winsock2.h>
48 #else /* Linux/Unix */
49 #include <sys/socket.h>
50 #include <sys/select.h>
51 #endif
52 
53 #include <openssl/bio.h>
54 #include <openssl/ssl.h>
55 #include <openssl/err.h>
56 
57 static int handle_io_failure(SSL *ssl, int res);
58 
59 #define REQ_STRING_SZ 1024
60 
61 /**
62  * @brief A static pointer to a BIO object representing the session's BIO.
63  *
64  * This variable holds a reference to a BIO object used for network
65  * communication during the session. It is initialized to NULL and should
66  * be assigned a valid BIO object before use. The BIO object pointed to by
67  * this variable may be used throughout the session for reading and writing
68  * data.
69  *
70  * @note This variable is static, meaning it is only accessible within the
71  * file in which it is declared.
72  */
73 static BIO *session_bio = NULL;
74 
75 /**
76  * @brief Creates a BIO object for a UDP socket connection to a server.
77  *
78  * This function attempts to create a UDP socket and connect it to the server
79  * specified by the given hostname and port. The socket is wrapped in a BIO
80  * object, which allows for network communication via OpenSSL's BIO API.
81  * The function also returns the address of the connected peer.
82  *
83  * @param hostname The hostname of the server to connect to.
84  * @param port The port number of the server to connect to.
85  * @param peer_addr A pointer to a BIO_ADDR pointer that will hold the address
86  *                  of the connected peer on success. The caller is responsible
87  *                  for freeing this memory using BIO_ADDR_free().
88  * @return A pointer to a BIO object on success, or NULL on failure.
89  *         The returned BIO object will be associated with the connected socket.
90  *         If the BIO object is successfully created, it will take ownership of
91  *         the socket and automatically close it when the BIO is freed.
92  *
93  * @note The function uses OpenSSL's socket-related functions (e.g., BIO_socket,
94  *       BIO_connect) or portability and to integrate with OpenSSL's error
95  *       handling mechanisms.
96  * @note If no valid connection is established, the function returns NULL and
97  *       ensures that any resources allocated during the process are properly
98  *       freed.
99  */
create_socket_bio(const char * hostname,const char * port,BIO_ADDR ** peer_addr)100 static BIO *create_socket_bio(const char *hostname, const char *port,
101     BIO_ADDR **peer_addr)
102 {
103     int sock = -1;
104     BIO_ADDRINFO *res;
105     const BIO_ADDRINFO *ai = NULL;
106     BIO *bio;
107 
108     /*
109      * Lookup IP address info for the server.
110      */
111     if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, AF_UNSPEC, SOCK_DGRAM,
112             0, &res))
113         return NULL;
114 
115     /*
116      * Loop through all the possible addresses for the server and find one
117      * we can connect to.
118      */
119     for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
120         /*
121          * Create a UDP socket. We could equally use non-OpenSSL calls such
122          * as "socket" here for this and the subsequent connect and close
123          * functions. But for portability reasons and also so that we get
124          * errors on the OpenSSL stack in the event of a failure we use
125          * OpenSSL's versions of these functions.
126          */
127         sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0);
128         if (sock == -1)
129             continue;
130 
131         /* Connect the socket to the server's address */
132         if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) {
133             BIO_closesocket(sock);
134             sock = -1;
135             continue;
136         }
137 
138         /*
139          * Set to nonblocking mode
140          * Note: This function returns a range of errors
141          * <= 0 if something goes wrong, so catch them all here
142          */
143         if (BIO_socket_nbio(sock, 1) <= 0) {
144             BIO_closesocket(sock);
145             sock = -1;
146             continue;
147         }
148 
149         break;
150     }
151 
152     if (sock != -1) {
153         *peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai));
154         if (*peer_addr == NULL) {
155             BIO_closesocket(sock);
156             return NULL;
157         }
158     }
159 
160     /* Free the address information resources we allocated earlier */
161     BIO_ADDRINFO_free(res);
162 
163     /* If sock is -1 then we've been unable to connect to the server */
164     if (sock == -1)
165         return NULL;
166 
167     /* Create a BIO to wrap the socket */
168     bio = BIO_new(BIO_s_datagram());
169     if (bio == NULL) {
170         BIO_closesocket(sock);
171         return NULL;
172     }
173 
174     /*
175      * Associate the newly created BIO with the underlying socket. By
176      * passing BIO_CLOSE here the socket will be automatically closed when
177      * the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
178      * case you must close the socket explicitly when it is no longer
179      * needed.
180      */
181     if (BIO_set_fd(bio, sock, BIO_CLOSE) <= 0) {
182         BIO_closesocket(sock);
183         BIO_free(bio);
184         return NULL;
185     }
186 
187     return bio;
188 }
189 
190 /**
191  * @brief Waits for activity on the SSL socket, either for reading or writing.
192  *
193  * This function monitors the underlying file descriptor of the given SSL
194  * connection to determine when it is ready for reading or writing, or both.
195  * It uses the select function to wait until the socket is either readable
196  * or writable, depending on what the SSL connection requires.
197  *
198  * @param ssl A pointer to the SSL object representing the connection.
199  *
200  * @note This function blocks until there is activity on the socket or
201  * until the timeout specified by OpenSSL is reached. In a real application,
202  * you might want to perform other tasks while waiting, such as updating a
203  * GUI or handling other connections.
204  *
205  * @note This function uses select for simplicity and portability. Depending
206  * on your application's requirements, you might consider using other
207  * mechanisms like poll or epoll for handling multiple file descriptors.
208  */
wait_for_activity(SSL * ssl)209 static void wait_for_activity(SSL *ssl)
210 {
211     fd_set wfds, rfds;
212     int width, sock, isinfinite;
213     struct timeval tv;
214     struct timeval *tvp = NULL;
215 
216     /* Get hold of the underlying file descriptor for the socket */
217     sock = SSL_get_fd(ssl);
218 
219     FD_ZERO(&wfds);
220     FD_ZERO(&rfds);
221 
222     /*
223      * Find out if we would like to write to the socket, or read from it (or
224      * both)
225      */
226     if (SSL_net_write_desired(ssl))
227         FD_SET(sock, &wfds);
228     if (SSL_net_read_desired(ssl))
229         FD_SET(sock, &rfds);
230     width = sock + 1;
231 
232     /*
233      * Find out when OpenSSL would next like to be called, regardless of
234      * whether the state of the underlying socket has changed or not.
235      */
236     if (SSL_get_event_timeout(ssl, &tv, &isinfinite) && !isinfinite)
237         tvp = &tv;
238 
239     /*
240      * Wait until the socket is writeable or readable. We use select here
241      * for the sake of simplicity and portability, but you could equally use
242      * poll/epoll or similar functions
243      *
244      * NOTE: For the purposes of this demonstration code this effectively
245      * makes this demo block until it has something more useful to do. In a
246      * real application you probably want to go and do other work here (e.g.
247      * update a GUI, or service other connections).
248      *
249      * Let's say for example that you want to update the progress counter on
250      * a GUI every 100ms. One way to do that would be to use the timeout in
251      * the last parameter to "select" below. If the tvp value is greater
252      * than 100ms then use 100ms instead. Then, when select returns, you
253      * check if it did so because of activity on the file descriptors or
254      * because of the timeout. If the 100ms GUI timeout has expired but the
255      * tvp timeout has not then go and update the GUI and then restart the
256      * "select" (with updated timeouts).
257      */
258 
259     select(width, &rfds, &wfds, NULL, tvp);
260 }
261 
262 /**
263  * @brief Handles I/O failures on an SSL connection based on the result code.
264  *
265  * This function processes the result of an SSL I/O operation and handles
266  * different types of errors that may occur during the operation. It takes
267  * appropriate actions such as retrying the operation, reporting errors, or
268  * returning specific status codes based on the error type.
269  *
270  * @param ssl A pointer to the SSL object representing the connection.
271  * @param res The result code from the SSL I/O operation.
272  * @return An integer indicating the outcome:
273  *         - 1: Temporary failure, the operation should be retried.
274  *         - 0: EOF, indicating the connection has been closed.
275  *         - -1: A fatal error occurred or the connection has been reset.
276  *
277  * @note This function may block if a temporary failure occurs and
278  * wait_for_activity() is called.
279  *
280  * @note If the failure is due to an SSL verification error, additional
281  * information will be logged to stderr.
282  */
handle_io_failure(SSL * ssl,int res)283 static int handle_io_failure(SSL *ssl, int res)
284 {
285     switch (SSL_get_error(ssl, res)) {
286     case SSL_ERROR_WANT_READ:
287     case SSL_ERROR_WANT_WRITE:
288         /* Temporary failure. Wait until we can read/write and try again */
289         wait_for_activity(ssl);
290         return 1;
291 
292     case SSL_ERROR_ZERO_RETURN:
293         /* EOF */
294         return 0;
295 
296     case SSL_ERROR_SYSCALL:
297         return -1;
298 
299     case SSL_ERROR_SSL:
300         /*
301          * Some stream fatal error occurred. This could be because of a
302          * stream reset - or some failure occurred on the underlying
303          * connection.
304          */
305         switch (SSL_get_stream_read_state(ssl)) {
306         case SSL_STREAM_STATE_RESET_REMOTE:
307             fprintf(stderr, "Stream reset occurred\n");
308             /*
309              * The stream has been reset but the connection is still
310              * healthy.
311              */
312             break;
313 
314         case SSL_STREAM_STATE_CONN_CLOSED:
315             fprintf(stderr, "Connection closed\n");
316             /* Connection is already closed. */
317             break;
318 
319         default:
320             fprintf(stderr, "Unknown stream failure\n");
321             break;
322         }
323         /*
324          * If the failure is due to a verification error we can get more
325          * information about it from SSL_get_verify_result().
326          */
327         if (SSL_get_verify_result(ssl) != X509_V_OK)
328             fprintf(stderr, "Verify error: %s\n",
329                 X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
330         return -1;
331 
332     default:
333         return -1;
334     }
335 }
336 
337 /**
338  * @brief A static integer indicating whether the session is cached.
339  *
340  * This variable is used to track the state of session caching. It is
341  * initialized to 0, meaning no session is cached. The value may be updated
342  * to indicate that a session has been successfully cached.
343  *
344  * @note This variable is static, meaning it is only accessible within the
345  * file in which it is declared.
346  */
347 static int session_cached = 0;
348 
349 /**
350  * @brief Caches a new SSL session if one is not already cached.
351  *
352  * This function writes a new SSL session to the session BIO and caches it.
353  * It ensures that only one session is cached at a time by checking the
354  * `session_cached` flag. If a session is already cached, the function
355  * returns without caching the new session.
356  *
357  * @param ssl A pointer to the SSL object associated with the session.
358  * @param sess A pointer to the SSL_SESSION object to be cached.
359  * @return 1 if the session is successfully cached, 0 otherwise.
360  *
361  * @note This function only allows one session to be cached. Subsequent
362  * sessions will not be cached unless `session_cached` is reset.
363  */
cache_new_session(struct ssl_st * ssl,SSL_SESSION * sess)364 static int cache_new_session(struct ssl_st *ssl, SSL_SESSION *sess)
365 {
366 
367     if (session_cached == 1)
368         return 0;
369 
370     /* Just write the new session to our bio */
371     if (!PEM_write_bio_SSL_SESSION(session_bio, sess))
372         return 0;
373 
374     (void)BIO_flush(session_bio);
375     /* only cache one session */
376     session_cached = 1;
377     return 1;
378 }
379 
380 /**
381  * @brief Sets up the session cache for the SSL connection.
382  *
383  * This function configures session caching for the given SSL connection
384  * and context. It attempts to load a session from the specified cache file
385  * or creates a new one if the file does not exist. It also configures the
386  * session cache mode and disables stateless session tickets.
387  *
388  * @param ssl A pointer to the SSL object for the connection.
389  * @param ctx A pointer to the SSL_CTX object representing the context.
390  * @param filename The name of the file used to store the session cache.
391  * @return 1 on success, 0 on failure.
392  *
393  * @note If the cache file does not exist, a new file is created and the
394  * session cache is initialized. If a session is successfully loaded from
395  * the file, it is added to the context and set for the SSL connection.
396  * If an error occurs during setup, the session BIO is freed.
397  */
setup_session_cache(SSL * ssl,SSL_CTX * ctx,const char * filename)398 static int setup_session_cache(SSL *ssl, SSL_CTX *ctx, const char *filename)
399 {
400     SSL_SESSION *sess = NULL;
401     int rc = 0;
402     int new_cache = 0;
403 
404     /*
405      * Because we cache sessions to a file in this client, we don't
406      * actually need to internally store sessions, because we restore them
407      * from the file with SSL_set_session below, but we want to ensure
408      * that caching is enabled so that the session cache callbacks get called
409      * properly.  The documentation is a bit unclear under what conditions
410      * the callback is made, so play it safe here, by enforcing enablement
411      */
412     if (!SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE | SSL_SESS_CACHE_NO_AUTO_CLEAR))
413         return rc;
414 
415     /* open our cache file */
416     session_bio = BIO_new_file(filename, "r+");
417     if (session_bio == NULL) {
418         /* file might need to be created */
419         session_bio = BIO_new_file(filename, "w+");
420         if (session_bio == NULL)
421             return rc;
422         new_cache = 1;
423     }
424 
425     if (new_cache == 0) {
426         /* read in our cached session */
427         if (PEM_read_bio_SSL_SESSION(session_bio, &sess, NULL, NULL)) {
428             /* set our session */
429             if (!SSL_set_session(ssl, sess))
430                 goto err;
431         }
432     } else {
433         /* Set the callback to store new sessions */
434         SSL_CTX_sess_set_new_cb(ctx, cache_new_session);
435     }
436 
437     rc = 1;
438 
439 err:
440     if (rc == 0)
441         BIO_free(session_bio);
442     return rc;
443 }
444 
445 /**
446  * @brief Pointer to a list of SSL polling items.
447  *
448  * This static variable holds the reference to a dynamically allocated list
449  * of SSL_POLL_ITEM structures used for SSL polling operations. It is
450  * initialized to NULL and will be populated as needed.
451  */
452 static SSL_POLL_ITEM *poll_list = NULL;
453 
454 /**
455  * @brief Pointer to an array of BIO objects for output.
456  *
457  * This static variable holds the reference to a dynamically allocated array
458  * of BIO structures, which are used for handling output in SSL operations.
459  * It is initialized to NULL and will be set when needed.  This array holds
460  * the out bio's for all received data from GET requests
461  */
462 static BIO **outbiolist = NULL;
463 
464 /**
465  * @brief Pointer to an array of output names.
466  *
467  * This static variable holds the reference to a dynamically allocated array
468  * of strings, representing output names. It is initialized to NULL and
469  * populated as required during operation.  This array holds the names of the
470  * output files from http GET requests.  Indices are correlated with the
471  * corresponding outbiolist and poll_list arrays
472  */
473 static char **outnames = NULL;
474 
475 /**
476  * @brief Counter for the number of poll items.
477  *
478  * This static variable holds the count of items in the poll_list. It is
479  * initialized to 0 and updated as items are added or removed from the list.
480  */
481 static size_t poll_count = 0;
482 
483 /**
484  * @brief Pointer to an array of request strings.
485  *
486  * This static variable holds the reference to a dynamically allocated array
487  * of strings, representing requests. It is initialized to NULL and populated
488  * as requests are added during execution.
489  */
490 static char **req_array = NULL;
491 
492 /**
493  * @brief Counter for the total number of requests.
494  *
495  * This static variable tracks the total number of parsed from reqfile. It is
496  * initialized to 0 and incremented as new requests are processed.
497  */
498 static size_t total_requests = 0;
499 
500 /**
501  * @brief Index for the current request in the request array.
502  *
503  * This static variable keeps track of the index of the current request being
504  * processed in the request array. It is initialized to 0 and updated as
505  * requests are handled.
506  */
507 static size_t req_idx = 0;
508 
509 /**
510  * @brief Builds and processes a set of SSL poll requests.
511  *
512  * This function creates a new set of SSL poll requests based on the current
513  * request array. It allocates and manages memory for poll lists, BIO output
514  * files, and associated request names. Each request sends an HTTP GET to the
515  * corresponding peer. The function processes the requests until a batch limit
516  * or error is encountered.
517  *
518  * @param ssl A pointer to the SSL object to use for creating new streams.
519  *
520  * @return The number of poll requests successfully built, or 0 on error.
521  */
build_request_set(SSL * ssl)522 static size_t build_request_set(SSL *ssl)
523 {
524     size_t poll_idx;
525     char *req;
526     char outfilename[REQ_STRING_SZ];
527     char req_string[REQ_STRING_SZ];
528     SSL *new_stream;
529     size_t written;
530     unsigned long error;
531     size_t retry_count;
532 
533     /*
534      * Free any previous poll list
535      */
536     for (poll_idx = 0; poll_idx < poll_count; poll_idx++) {
537         (void)BIO_flush(outbiolist[poll_idx]);
538         BIO_free(outbiolist[poll_idx]);
539         SSL_free(poll_list[poll_idx].desc.value.ssl);
540     }
541 
542     /*
543      * Reset out lists and poll_count
544      */
545     OPENSSL_free(outbiolist);
546     OPENSSL_free(outnames);
547     OPENSSL_free(poll_list);
548     outnames = NULL;
549     poll_list = NULL;
550     outbiolist = NULL;
551 
552     poll_count = 0;
553 
554     /*
555      * Iterate through our parsed lists of requests
556      * note req_idx may start at a non-zero value if
557      * multiple calls to build_request_list are made
558      */
559     while (req_idx < total_requests) {
560         req = req_array[req_idx];
561         /* Up our poll count and set our poll_list index */
562         poll_count++;
563         poll_idx = poll_count - 1;
564 
565         /*
566          * Expand our poll_list, outbiolist, and outnames arrays
567          */
568         poll_list = OPENSSL_realloc(poll_list,
569             sizeof(SSL_POLL_ITEM) * poll_count);
570         if (poll_list == NULL) {
571             fprintf(stderr, "Unable to realloc poll_list\n");
572             goto err;
573         }
574 
575         outbiolist = OPENSSL_realloc(outbiolist,
576             sizeof(BIO *) * poll_count);
577         if (outbiolist == NULL) {
578             fprintf(stderr, "Unable to realloc outbiolist\n");
579             goto err;
580         }
581 
582         outnames = OPENSSL_realloc(outnames, sizeof(char *) * poll_count);
583         if (outnames == NULL) {
584             fprintf(stderr, "Unable to realloc outnames\n");
585             goto err;
586         }
587 
588         /* set the output file name for this index */
589         outnames[poll_idx] = req;
590 
591         /* Format the http request */
592         BIO_snprintf(req_string, REQ_STRING_SZ, "GET /%s\r\n", req);
593 
594         /* build the outfile request path */
595         memset(outfilename, 0, REQ_STRING_SZ);
596         BIO_snprintf(outfilename, REQ_STRING_SZ, "/downloads/%s", req);
597 
598         /* open a bio to write the file */
599         outbiolist[poll_idx] = BIO_new_file(outfilename, "w+");
600         if (outbiolist[poll_idx] == NULL) {
601             fprintf(stderr, "Failed to open outfile %s\n", outfilename);
602             goto err;
603         }
604 
605         /* create a request stream */
606         new_stream = NULL;
607 
608         /*
609          * NOTE: We are doing groups of 25 because that's 1/4 of the initial max
610          * stream count that most servers advertise.  This gives the server an
611          * opportunity to send us updated MAX_STREAM frames to extend our stream
612          * allotment before we run out, which many servers defer doing.
613          */
614         if (poll_count <= 25) {
615             for (retry_count = 0; retry_count < 10; retry_count++) {
616                 ERR_clear_error();
617                 new_stream = SSL_new_stream(ssl, 0);
618                 if (new_stream == NULL
619                     && (error = ERR_get_error()) != 0
620                     && ERR_GET_REASON(error) == SSL_R_STREAM_COUNT_LIMITED) {
621                     /*
622                      * Kick the SSL state machine in the hopes that
623                      * the server has a MAX_STREAM frame for us to process
624                      */
625                     fprintf(stderr, "Stream limit reached, retrying\n");
626                     SSL_handle_events(ssl);
627                     continue;
628                 }
629                 break;
630             }
631         }
632 
633         if (new_stream == NULL) {
634             /*
635              * We ran out of new streams to allocate
636              * return and process this batch before getting more
637              */
638             poll_count--;
639             return poll_count;
640         }
641 
642         /*
643          * Create a poll descriptor for this stream
644          */
645         poll_list[poll_idx].desc = SSL_as_poll_descriptor(new_stream);
646         poll_list[poll_idx].revents = 0;
647         poll_list[poll_idx].events = SSL_POLL_EVENT_R;
648 
649         /* Write an HTTP GET request to the peer */
650         while (!SSL_write_ex2(poll_list[poll_idx].desc.value.ssl,
651             req_string, strlen(req_string),
652             SSL_WRITE_FLAG_CONCLUDE, &written)) {
653             if (handle_io_failure(poll_list[poll_idx].desc.value.ssl, 0) == 1)
654                 continue; /* Retry */
655             fprintf(stderr, "Failed to write start of HTTP request\n");
656             goto err; /* Cannot retry: error */
657         }
658 
659         req_idx++;
660     }
661     return poll_count;
662 
663 err:
664     for (poll_idx = 0; poll_idx < poll_count; poll_idx++) {
665         BIO_free(outbiolist[poll_idx]);
666         SSL_free(poll_list[poll_idx].desc.value.ssl);
667     }
668     OPENSSL_free(poll_list);
669     OPENSSL_free(outbiolist);
670     poll_list = NULL;
671     outbiolist = NULL;
672     poll_count = 0;
673     return poll_count;
674 }
675 
676 /**
677  * @brief Static pointer to a BIO_ADDR structure representing the peer's address.
678  *
679  * This variable is used to store the address of a peer for network communication.
680  * It is statically allocated and should be initialized appropriately.
681  */
682 static BIO_ADDR *peer_addr = NULL;
683 
684 /**
685  * @brief Set up a TLS/QUIC connection to the specified hostname and port.
686  *
687  * This function creates and configures an SSL context for a client connection
688  * using the QUIC client method. It sets up the necessary certificates,
689  * performs host verification, configures ALPN, and establishes a non-blocking
690  * connection.
691  *
692  * @param hostname Hostname to connect to.
693  * @param port Port to connect to.
694  * @param ctx Pointer to an SSL_CTX object, which will be created.
695  * @param ssl Pointer to an SSL object, which will be created.
696  *
697  * @return Returns 0 on success, 1 on error.
698  */
setup_connection(char * hostname,char * port,SSL_CTX ** ctx,SSL ** ssl)699 static int setup_connection(char *hostname, char *port,
700     SSL_CTX **ctx, SSL **ssl)
701 {
702     unsigned char alpn[] = { 10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p' };
703     int ret = 0;
704     BIO *bio = NULL;
705 
706     /*
707      * Create an SSL_CTX which we can use to create SSL objects from. We
708      * want an SSL_CTX for creating clients so we use
709      * OSSL_QUIC_client_method() here.
710      */
711     *ctx = SSL_CTX_new(OSSL_QUIC_client_method());
712     if (*ctx == NULL) {
713         fprintf(stderr, "Failed to create the SSL_CTX\n");
714         goto end;
715     }
716 
717     /*
718      * Configure the client to abort the handshake if certificate
719      * verification fails. Virtually all clients should do this unless you
720      * really know what you are doing.
721      */
722     SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL);
723 
724     /*
725      * Use the default trusted certificate store
726      * Note: The store is read from SSL_CERT_DIR and SSL_CERT_FILE
727      * environment variables in the default case, so users can set those
728      * When running this application to direct where the store is loaded from
729      */
730     if (!SSL_CTX_set_default_verify_paths(*ctx)) {
731         fprintf(stderr, "Failed to set the default trusted certificate store\n");
732         goto end;
733     }
734 
735     /*
736      * If the SSL_CIPHER_SUITES env variable is set, assign those
737      * ciphers to the context
738      */
739     if (getenv("SSL_CIPHER_SUITES") != NULL) {
740         if (!SSL_CTX_set_ciphersuites(*ctx, getenv("SSL_CIPHER_SUITES"))) {
741             fprintf(stderr, "Failed to set cipher suites for connection\n");
742             goto end;
743         }
744     }
745 
746     /* Create an SSL object to represent the TLS connection */
747     *ssl = SSL_new(*ctx);
748     if (*ssl == NULL) {
749         fprintf(stderr, "Failed to create the SSL object\n");
750         goto end;
751     }
752 
753     if (getenv("SSL_SESSION_FILE") != NULL) {
754         if (!setup_session_cache(*ssl, *ctx, getenv("SSL_SESSION_FILE"))) {
755             fprintf(stderr, "Unable to setup session cache\n");
756             goto end;
757         }
758     }
759 
760     /*
761      * Create the underlying transport socket/BIO and associate it with the
762      * connection.
763      */
764     bio = create_socket_bio(hostname, port, &peer_addr);
765     if (bio == NULL) {
766         fprintf(stderr, "Failed to crete the BIO\n");
767         goto end;
768     }
769     SSL_set_bio(*ssl, bio, bio);
770 
771     /*
772      * Tell the server during the handshake which hostname we are attempting
773      * to connect to in case the server supports multiple hosts.
774      */
775     if (!SSL_set_tlsext_host_name(*ssl, hostname)) {
776         fprintf(stderr, "Failed to set the SNI hostname\n");
777         goto end;
778     }
779 
780     /*
781      * Ensure we check during certificate verification that the server has
782      * supplied a certificate for the hostname that we were expecting.
783      * Virtually all clients should do this unless you really know what you
784      * are doing.
785      */
786     if (!SSL_set1_host(*ssl, hostname)) {
787         fprintf(stderr, "Failed to set the certificate verification hostname");
788         goto end;
789     }
790 
791     /* SSL_set_alpn_protos returns 0 for success! */
792     if (SSL_set_alpn_protos(*ssl, alpn, sizeof(alpn)) != 0) {
793         fprintf(stderr, "Failed to set the ALPN for the connection\n");
794         goto end;
795     }
796 
797     /* Set the IP address of the remote peer */
798     if (!SSL_set1_initial_peer_addr(*ssl, peer_addr)) {
799         fprintf(stderr, "Failed to set the initial peer address\n");
800         goto end;
801     }
802 
803     /*
804      * The underlying socket is always nonblocking with QUIC, but the default
805      * behaviour of the SSL object is still to block. We set it for nonblocking
806      * mode in this demo.
807      */
808     if (!SSL_set_blocking_mode(*ssl, 0)) {
809         fprintf(stderr, "Failed to turn off blocking mode\n");
810         goto end;
811     }
812 
813     /* Do the handshake with the server */
814     while ((ret = SSL_connect(*ssl)) != 1) {
815         if (handle_io_failure(*ssl, ret) == 1)
816             continue; /* Retry */
817         fprintf(stderr, "Failed to connect to server\n");
818         goto end; /* Cannot retry: error */
819     }
820 
821     return 1;
822 end:
823     SSL_CTX_free(*ctx);
824     SSL_free(*ssl);
825     BIO_ADDR_free(peer_addr);
826     *ctx = NULL;
827     *ssl = NULL;
828     peer_addr = NULL;
829     return 0;
830 }
831 
832 /**
833  * @brief Entry point for the QUIC hq-interop client demo application.
834  *
835  * This function sets up an SSL/TLS connection using QUIC, sends HTTP GET
836  * requests for files specified in the command-line arguments, and saves
837  * the responses to disk. It handles various configurations such as session
838  * caching, and key logging.
839  *
840  * @param argc The number of command-line arguments.
841  * @param argv The array of command-line arguments. The expected format is
842  *             "hostname port file".
843  * @return EXIT_SUCCESS on success, or EXIT_FAILURE on error.
844  *
845  * @note The function performs the following main tasks:
846  *       - Parses command-line arguments.
847  *       - Reads the list of requests from the specified file.
848  *       - Sets up the SSL context and configures certificate verification.
849  *       - Optionally enables key logging and session caching.
850  *       - Establishes a non-blocking QUIC connection to the server.
851  *       - Sends an HTTP GET request for each file and writes the response
852  *         to the corresponding output file.
853  *       - Gracefully shuts down the SSL connection and frees resources.
854  *       - Prints any OpenSSL error stack information on failure.
855  */
main(int argc,char * argv[])856 int main(int argc, char *argv[])
857 {
858     SSL_CTX *ctx = NULL;
859     SSL *ssl = NULL;
860     BIO *req_bio = NULL;
861     int res = EXIT_FAILURE;
862     int ret;
863     size_t readbytes = 0;
864     char buf[160];
865     int eof = 0;
866     int argnext = 1;
867     char *reqfile = NULL;
868     char *reqnames = OPENSSL_zalloc(1025);
869     size_t read_offset = 0;
870     size_t bytes_read = 0;
871     size_t poll_idx = 0;
872     size_t poll_done = 0;
873     size_t result_count = 0;
874     struct timeval poll_timeout;
875     size_t this_poll_count = 0;
876     char *req = NULL;
877     char *hostname, *port;
878 
879     if (argc < 4) {
880         fprintf(stderr, "Usage: quic-hq-interop hostname port reqfile\n");
881         goto end;
882     }
883 
884     hostname = argv[argnext++];
885     port = argv[argnext++];
886     reqfile = argv[argnext];
887 
888     req_bio = BIO_new_file(reqfile, "r");
889     if (req_bio == NULL) {
890         fprintf(stderr, "Failed to open request file %s\n", reqfile);
891         goto end;
892     }
893 
894     /* Get the list of requests */
895     while (!BIO_eof(req_bio)) {
896         if (!BIO_read_ex(req_bio, &reqnames[read_offset], REQ_STRING_SZ, &bytes_read)) {
897             fprintf(stderr, "Failed to read some data from request file\n");
898             goto end;
899         }
900         read_offset += bytes_read;
901         reqnames = OPENSSL_realloc(reqnames, read_offset + REQ_STRING_SZ);
902         if (reqnames == NULL) {
903             fprintf(stderr, "Realloc failure\n");
904             goto end;
905         }
906     }
907     reqnames[read_offset + 1] = '\0';
908 
909     if (!setup_connection(hostname, port, &ctx, &ssl)) {
910         fprintf(stderr, "Unable to establish connection\n");
911         goto end;
912     }
913 
914     req = strtok(reqnames, " ");
915 
916     while (req != NULL) {
917         total_requests++;
918         req_array = OPENSSL_realloc(req_array, sizeof(char *) * total_requests);
919         if (req_array == NULL)
920             goto end;
921         req_array[total_requests - 1] = req;
922         req = strtok(NULL, " ");
923     }
924 
925     /* get a list of requests to poll */
926     this_poll_count = build_request_set(ssl);
927 
928     /*
929      * Now poll all our descriptors for events
930      */
931     while (this_poll_count != 0 && poll_done < this_poll_count) {
932         result_count = 0;
933         poll_timeout.tv_sec = 0;
934         poll_timeout.tv_usec = 0;
935         if (!SSL_poll(poll_list, this_poll_count, sizeof(SSL_POLL_ITEM),
936                 &poll_timeout, 0, &result_count)) {
937             fprintf(stderr, "Failed to poll\n");
938             goto end;
939         }
940 
941         /* Iterate over our poll array looking for ready SSL's */
942         for (poll_idx = 0; poll_idx < this_poll_count; poll_idx++) {
943             /*
944              * If we have visited the number of SSL's that SSL_poll
945              * indicated were ready, we can go poll again
946              */
947             if (result_count == 0)
948                 break;
949 
950             if (poll_list[poll_idx].revents == SSL_POLL_EVENT_R) {
951                 /*
952                  * We found an SSL that we can read, drop our result count
953                  */
954                 result_count--;
955 
956                 /* And clear the revents for the next poll */
957                 poll_list[poll_idx].revents = 0;
958 
959                 /*
960                  * Get up to sizeof(buf) bytes of the response. We keep reading until
961                  * the server closes the connection.
962                  */
963                 eof = 0;
964 
965                 /* Read our data, and handle any errors/eof conditions */
966                 if (!SSL_read_ex(poll_list[poll_idx].desc.value.ssl, buf,
967                         sizeof(buf), &readbytes)) {
968                     switch (handle_io_failure(poll_list[poll_idx].desc.value.ssl,
969                         0)) {
970                     case 1:
971                         eof = 0;
972                         break; /* Retry on next poll */
973                     case 0:
974                         eof = 1;
975                         break;
976                     case -1:
977                     default:
978                         fprintf(stderr, "Failed reading remaining data\n");
979                         goto end; /* Cannot retry: error */
980                     }
981                 }
982 
983                 /*
984                  * If error handling indicated that this SSL is in an EOF state
985                  * we mark the SSL as not needing any more polling, and up our
986                  * poll_done count.  Otherwise, just write to the outbio
987                  */
988                 if (!eof) {
989                     BIO_write(outbiolist[poll_idx], buf, readbytes);
990                 } else {
991                     fprintf(stderr, "completed %s\n", outnames[poll_idx]);
992                     /* This file is done, take it out of polling contention */
993                     poll_list[poll_idx].events = 0;
994                     poll_done++;
995                 }
996             }
997         }
998 
999         /*
1000          * If we've completed this poll set, try get another one
1001          */
1002         if (poll_done == this_poll_count) {
1003             this_poll_count = build_request_set(ssl);
1004             poll_done = 0;
1005         }
1006     }
1007 
1008     /*
1009      * Repeatedly call SSL_shutdown() until the connection is fully
1010      * closed.
1011      */
1012     fprintf(stderr, "Shutting down\n");
1013     while ((ret = SSL_shutdown(ssl)) != 1) {
1014         if (ret < 0 && handle_io_failure(ssl, ret) == 1)
1015             continue; /* Retry */
1016     }
1017 
1018     /* Success! */
1019     res = EXIT_SUCCESS;
1020 end:
1021     /*
1022      * If something bad happened then we will dump the contents of the
1023      * OpenSSL error stack to stderr. There might be some useful diagnostic
1024      * information there.
1025      */
1026     if (res == EXIT_FAILURE)
1027         ERR_print_errors_fp(stderr);
1028 
1029     /*
1030      * Free the resources we allocated. We do not free the BIO object here
1031      * because ownership of it was immediately transferred to the SSL object
1032      * via SSL_set_bio(). The BIO will be freed when we free the SSL object.
1033      */
1034     BIO_ADDR_free(peer_addr);
1035     OPENSSL_free(reqnames);
1036     BIO_free(req_bio);
1037     BIO_free(session_bio);
1038     for (poll_idx = 0; poll_idx < poll_count; poll_idx++) {
1039         BIO_free(outbiolist[poll_idx]);
1040         SSL_free(poll_list[poll_idx].desc.value.ssl);
1041     }
1042     SSL_free(ssl);
1043     SSL_CTX_free(ctx);
1044     OPENSSL_free(outbiolist);
1045     OPENSSL_free(poll_list);
1046     return res;
1047 }
1048