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