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