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