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