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 */ 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 */ 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 */ 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 */ 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 */ 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 * actualy 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 | 413 SSL_SESS_CACHE_NO_INTERNAL_STORE | 414 SSL_SESS_CACHE_NO_AUTO_CLEAR)) 415 return rc; 416 417 /* open our cache file */ 418 session_bio = BIO_new_file(filename, "r+"); 419 if (session_bio == NULL) { 420 /* file might need to be created */ 421 session_bio = BIO_new_file(filename, "w+"); 422 if (session_bio == NULL) 423 return rc; 424 new_cache = 1; 425 } 426 427 if (new_cache == 0) { 428 /* read in our cached session */ 429 if (PEM_read_bio_SSL_SESSION(session_bio, &sess, NULL, NULL)) { 430 /* set our session */ 431 if (!SSL_set_session(ssl, sess)) 432 goto err; 433 } 434 } else { 435 /* Set the callback to store new sessions */ 436 SSL_CTX_sess_set_new_cb(ctx, cache_new_session); 437 } 438 439 rc = 1; 440 441 err: 442 if (rc == 0) 443 BIO_free(session_bio); 444 return rc; 445 } 446 447 /** 448 * @brief Pointer to a list of SSL polling items. 449 * 450 * This static variable holds the reference to a dynamically allocated list 451 * of SSL_POLL_ITEM structures used for SSL polling operations. It is 452 * initialized to NULL and will be populated as needed. 453 */ 454 static SSL_POLL_ITEM *poll_list = NULL; 455 456 /** 457 * @brief Pointer to an array of BIO objects for output. 458 * 459 * This static variable holds the reference to a dynamically allocated array 460 * of BIO structures, which are used for handling output in SSL operations. 461 * It is initialized to NULL and will be set when needed. This array holds 462 * the out bio's for all received data from GET requests 463 */ 464 static BIO **outbiolist = NULL; 465 466 /** 467 * @brief Pointer to an array of output names. 468 * 469 * This static variable holds the reference to a dynamically allocated array 470 * of strings, representing output names. It is initialized to NULL and 471 * populated as required during operation. This array holds the names of the 472 * output files from http GET requests. Indicies are correlated with the 473 * corresponding outbiolist and poll_list arrays 474 */ 475 static char **outnames = NULL; 476 477 /** 478 * @brief Counter for the number of poll items. 479 * 480 * This static variable holds the count of items in the poll_list. It is 481 * initialized to 0 and updated as items are added or removed from the list. 482 */ 483 static size_t poll_count = 0; 484 485 /** 486 * @brief Pointer to an array of request strings. 487 * 488 * This static variable holds the reference to a dynamically allocated array 489 * of strings, representing requests. It is initialized to NULL and populated 490 * as requests are added during execution. 491 */ 492 static char **req_array = NULL; 493 494 /** 495 * @brief Counter for the total number of requests. 496 * 497 * This static variable tracks the total number of parsed from reqfile. It is 498 * initialized to 0 and incremented as new requests are processed. 499 */ 500 static size_t total_requests = 0; 501 502 /** 503 * @brief Index for the current request in the request array. 504 * 505 * This static variable keeps track of the index of the current request being 506 * processed in the request array. It is initialized to 0 and updated as 507 * requests are handled. 508 */ 509 static size_t req_idx = 0; 510 511 /** 512 * @brief Builds and processes a set of SSL poll requests. 513 * 514 * This function creates a new set of SSL poll requests based on the current 515 * request array. It allocates and manages memory for poll lists, BIO output 516 * files, and associated request names. Each request sends an HTTP GET to the 517 * corresponding peer. The function processes the requests until a batch limit 518 * or error is encountered. 519 * 520 * @param ssl A pointer to the SSL object to use for creating new streams. 521 * 522 * @return The number of poll requests successfully built, or 0 on error. 523 */ 524 static size_t build_request_set(SSL *ssl) 525 { 526 size_t poll_idx; 527 char *req; 528 char outfilename[REQ_STRING_SZ]; 529 char req_string[REQ_STRING_SZ]; 530 SSL *new_stream; 531 size_t written; 532 unsigned long error; 533 size_t retry_count; 534 535 /* 536 * Free any previous poll list 537 */ 538 for (poll_idx = 0; poll_idx < poll_count; poll_idx++) { 539 (void)BIO_flush(outbiolist[poll_idx]); 540 BIO_free(outbiolist[poll_idx]); 541 SSL_free(poll_list[poll_idx].desc.value.ssl); 542 } 543 544 /* 545 * Reset out lists and poll_count 546 */ 547 OPENSSL_free(outbiolist); 548 OPENSSL_free(outnames); 549 OPENSSL_free(poll_list); 550 outnames = NULL; 551 poll_list = NULL; 552 outbiolist = NULL; 553 554 poll_count = 0; 555 556 /* 557 * Iterate through our parsed lists of requests 558 * note req_idx may start at a non-zero value if 559 * multiple calls to build_request_list are made 560 */ 561 while (req_idx < total_requests) { 562 req = req_array[req_idx]; 563 /* Up our poll count and set our poll_list index */ 564 poll_count++; 565 poll_idx = poll_count - 1; 566 567 /* 568 * Expand our poll_list, outbiolist, and outnames arrays 569 */ 570 poll_list = OPENSSL_realloc(poll_list, 571 sizeof(SSL_POLL_ITEM) * poll_count); 572 if (poll_list == NULL) { 573 fprintf(stderr, "Unable to realloc poll_list\n"); 574 goto err; 575 } 576 577 outbiolist = OPENSSL_realloc(outbiolist, 578 sizeof(BIO *) * poll_count); 579 if (outbiolist == NULL) { 580 fprintf(stderr, "Unable to realloc outbiolist\n"); 581 goto err; 582 } 583 584 outnames = OPENSSL_realloc(outnames, sizeof(char *) * poll_count); 585 if (outnames == NULL) { 586 fprintf(stderr, "Unable to realloc outnames\n"); 587 goto err; 588 } 589 590 /* set the output file name for this index */ 591 outnames[poll_idx] = req; 592 593 /* Format the http request */ 594 BIO_snprintf(req_string, REQ_STRING_SZ, "GET /%s\r\n", req); 595 596 /* build the outfile request path */ 597 memset(outfilename, 0, REQ_STRING_SZ); 598 BIO_snprintf(outfilename, REQ_STRING_SZ, "/downloads/%s", req); 599 600 /* open a bio to write the file */ 601 outbiolist[poll_idx] = BIO_new_file(outfilename, "w+"); 602 if (outbiolist[poll_idx] == NULL) { 603 fprintf(stderr, "Failed to open outfile %s\n", outfilename); 604 goto err; 605 } 606 607 /* create a request stream */ 608 new_stream = NULL; 609 610 /* 611 * NOTE: We are doing groups of 25 because thats 1/4 of the initial max 612 * stream count that most servers advertise. This gives the server an 613 * opportunity to send us updated MAX_STREAM frames to extend our stream 614 * allotment before we run out, which many servers defer doing. 615 */ 616 if (poll_count <= 25) { 617 for (retry_count = 0; retry_count < 10; retry_count++) { 618 ERR_clear_error(); 619 new_stream = SSL_new_stream(ssl, 0); 620 if (new_stream == NULL 621 && (error = ERR_get_error()) != 0 622 && ERR_GET_REASON(error) == SSL_R_STREAM_COUNT_LIMITED) { 623 /* 624 * Kick the SSL state machine in the hopes that 625 * the server has a MAX_STREAM frame for us to process 626 */ 627 fprintf(stderr, "Stream limit reached, retrying\n"); 628 SSL_handle_events(ssl); 629 continue; 630 } 631 break; 632 } 633 } 634 635 if (new_stream == NULL) { 636 /* 637 * We ran out of new streams to allocate 638 * return and process this batch before getting more 639 */ 640 poll_count--; 641 return poll_count; 642 } 643 644 /* 645 * Create a poll descriptor for this stream 646 */ 647 poll_list[poll_idx].desc = SSL_as_poll_descriptor(new_stream); 648 poll_list[poll_idx].revents = 0; 649 poll_list[poll_idx].events = SSL_POLL_EVENT_R; 650 651 /* Write an HTTP GET request to the peer */ 652 while (!SSL_write_ex2(poll_list[poll_idx].desc.value.ssl, 653 req_string, strlen(req_string), 654 SSL_WRITE_FLAG_CONCLUDE, &written)) { 655 if (handle_io_failure(poll_list[poll_idx].desc.value.ssl, 0) == 1) 656 continue; /* Retry */ 657 fprintf(stderr, "Failed to write start of HTTP request\n"); 658 goto err; /* Cannot retry: error */ 659 } 660 661 req_idx++; 662 } 663 return poll_count; 664 665 err: 666 for (poll_idx = 0; poll_idx < poll_count; poll_idx++) { 667 BIO_free(outbiolist[poll_idx]); 668 SSL_free(poll_list[poll_idx].desc.value.ssl); 669 } 670 OPENSSL_free(poll_list); 671 OPENSSL_free(outbiolist); 672 poll_list = NULL; 673 outbiolist = NULL; 674 poll_count = 0; 675 return poll_count; 676 } 677 678 /** 679 * @brief Static pointer to a BIO_ADDR structure representing the peer's address. 680 * 681 * This variable is used to store the address of a peer for network communication. 682 * It is statically allocated and should be initialized appropriately. 683 */ 684 static BIO_ADDR *peer_addr = NULL; 685 686 /** 687 * @brief Set up a TLS/QUIC connection to the specified hostname and port. 688 * 689 * This function creates and configures an SSL context for a client connection 690 * using the QUIC client method. It sets up the necessary certificates, 691 * performs host verification, configures ALPN, and establishes a non-blocking 692 * connection. 693 * 694 * @param hostname Hostname to connect to. 695 * @param port Port to connect to. 696 * @param ctx Pointer to an SSL_CTX object, which will be created. 697 * @param ssl Pointer to an SSL object, which will be created. 698 * 699 * @return Returns 0 on success, 1 on error. 700 */ 701 static int setup_connection(char *hostname, char *port, 702 SSL_CTX **ctx, SSL **ssl) 703 { 704 unsigned char alpn[] = {10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p'}; 705 int ret = 0; 706 BIO *bio = NULL; 707 708 /* 709 * Create an SSL_CTX which we can use to create SSL objects from. We 710 * want an SSL_CTX for creating clients so we use 711 * OSSL_QUIC_client_method() here. 712 */ 713 *ctx = SSL_CTX_new(OSSL_QUIC_client_method()); 714 if (*ctx == NULL) { 715 fprintf(stderr, "Failed to create the SSL_CTX\n"); 716 goto end; 717 } 718 719 /* 720 * Configure the client to abort the handshake if certificate 721 * verification fails. Virtually all clients should do this unless you 722 * really know what you are doing. 723 */ 724 SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL); 725 726 /* 727 * Use the default trusted certificate store 728 * Note: The store is read from SSL_CERT_DIR and SSL_CERT_FILE 729 * environment variables in the default case, so users can set those 730 * When running this application to direct where the store is loaded from 731 */ 732 if (!SSL_CTX_set_default_verify_paths(*ctx)) { 733 fprintf(stderr, "Failed to set the default trusted certificate store\n"); 734 goto end; 735 } 736 737 /* 738 * If the SSL_CIPHER_SUITES env variable is set, assign those 739 * ciphers to the context 740 */ 741 if (getenv("SSL_CIPHER_SUITES") != NULL) { 742 if (!SSL_CTX_set_ciphersuites(*ctx, getenv("SSL_CIPHER_SUITES"))) { 743 fprintf(stderr, "Failed to set cipher suites for connection\n"); 744 goto end; 745 } 746 } 747 748 /* Create an SSL object to represent the TLS connection */ 749 *ssl = SSL_new(*ctx); 750 if (*ssl == NULL) { 751 fprintf(stderr, "Failed to create the SSL object\n"); 752 goto end; 753 } 754 755 if (getenv("SSL_SESSION_FILE") != NULL) { 756 if (!setup_session_cache(*ssl, *ctx, getenv("SSL_SESSION_FILE"))) { 757 fprintf(stderr, "Unable to setup session cache\n"); 758 goto end; 759 } 760 } 761 762 /* 763 * Create the underlying transport socket/BIO and associate it with the 764 * connection. 765 */ 766 bio = create_socket_bio(hostname, port, &peer_addr); 767 if (bio == NULL) { 768 fprintf(stderr, "Failed to crete the BIO\n"); 769 goto end; 770 } 771 SSL_set_bio(*ssl, bio, bio); 772 773 /* 774 * Tell the server during the handshake which hostname we are attempting 775 * to connect to in case the server supports multiple hosts. 776 */ 777 if (!SSL_set_tlsext_host_name(*ssl, hostname)) { 778 fprintf(stderr, "Failed to set the SNI hostname\n"); 779 goto end; 780 } 781 782 /* 783 * Ensure we check during certificate verification that the server has 784 * supplied a certificate for the hostname that we were expecting. 785 * Virtually all clients should do this unless you really know what you 786 * are doing. 787 */ 788 if (!SSL_set1_host(*ssl, hostname)) { 789 fprintf(stderr, "Failed to set the certificate verification hostname"); 790 goto end; 791 } 792 793 /* SSL_set_alpn_protos returns 0 for success! */ 794 if (SSL_set_alpn_protos(*ssl, alpn, sizeof(alpn)) != 0) { 795 fprintf(stderr, "Failed to set the ALPN for the connection\n"); 796 goto end; 797 } 798 799 /* Set the IP address of the remote peer */ 800 if (!SSL_set1_initial_peer_addr(*ssl, peer_addr)) { 801 fprintf(stderr, "Failed to set the initial peer address\n"); 802 goto end; 803 } 804 805 /* 806 * The underlying socket is always nonblocking with QUIC, but the default 807 * behaviour of the SSL object is still to block. We set it for nonblocking 808 * mode in this demo. 809 */ 810 if (!SSL_set_blocking_mode(*ssl, 0)) { 811 fprintf(stderr, "Failed to turn off blocking mode\n"); 812 goto end; 813 } 814 815 /* Do the handshake with the server */ 816 while ((ret = SSL_connect(*ssl)) != 1) { 817 if (handle_io_failure(*ssl, ret) == 1) 818 continue; /* Retry */ 819 fprintf(stderr, "Failed to connect to server\n"); 820 goto end; /* Cannot retry: error */ 821 } 822 823 return 1; 824 end: 825 SSL_CTX_free(*ctx); 826 SSL_free(*ssl); 827 BIO_ADDR_free(peer_addr); 828 *ctx = NULL; 829 *ssl = NULL; 830 peer_addr = NULL; 831 return 0; 832 } 833 834 /** 835 * @brief Entry point for the QUIC hq-interop client demo application. 836 * 837 * This function sets up an SSL/TLS connection using QUIC, sends HTTP GET 838 * requests for files specified in the command-line arguments, and saves 839 * the responses to disk. It handles various configurations such as session 840 * caching, and key logging. 841 * 842 * @param argc The number of command-line arguments. 843 * @param argv The array of command-line arguments. The expected format is 844 * "hostname port file". 845 * @return EXIT_SUCCESS on success, or EXIT_FAILURE on error. 846 * 847 * @note The function performs the following main tasks: 848 * - Parses command-line arguments. 849 * - Reads the list of requests from the specified file. 850 * - Sets up the SSL context and configures certificate verification. 851 * - Optionally enables key logging and session caching. 852 * - Establishes a non-blocking QUIC connection to the server. 853 * - Sends an HTTP GET request for each file and writes the response 854 * to the corresponding output file. 855 * - Gracefully shuts down the SSL connection and frees resources. 856 * - Prints any OpenSSL error stack information on failure. 857 */ 858 int main(int argc, char *argv[]) 859 { 860 SSL_CTX *ctx = NULL; 861 SSL *ssl = NULL; 862 BIO *req_bio = NULL; 863 int res = EXIT_FAILURE; 864 int ret; 865 size_t readbytes = 0; 866 char buf[160]; 867 int eof = 0; 868 int argnext = 1; 869 char *reqfile = NULL; 870 char *reqnames = OPENSSL_zalloc(1025); 871 size_t read_offset = 0; 872 size_t bytes_read = 0; 873 size_t poll_idx = 0; 874 size_t poll_done = 0; 875 size_t result_count = 0; 876 struct timeval poll_timeout; 877 size_t this_poll_count = 0; 878 char *req = NULL; 879 char *hostname, *port; 880 881 if (argc < 4) { 882 fprintf(stderr, "Usage: quic-hq-interop hostname port reqfile\n"); 883 goto end; 884 } 885 886 hostname = argv[argnext++]; 887 port = argv[argnext++]; 888 reqfile = argv[argnext]; 889 890 req_bio = BIO_new_file(reqfile, "r"); 891 if (req_bio == NULL) { 892 fprintf(stderr, "Failed to open request file %s\n", reqfile); 893 goto end; 894 } 895 896 /* Get the list of requests */ 897 while (!BIO_eof(req_bio)) { 898 if (!BIO_read_ex(req_bio, &reqnames[read_offset], REQ_STRING_SZ, &bytes_read)) { 899 fprintf(stderr, "Failed to read some data from request file\n"); 900 goto end; 901 } 902 read_offset += bytes_read; 903 reqnames = OPENSSL_realloc(reqnames, read_offset + REQ_STRING_SZ); 904 if (reqnames == NULL) { 905 fprintf(stderr, "Realloc failure\n"); 906 goto end; 907 } 908 } 909 BIO_free(req_bio); 910 req_bio = NULL; 911 reqnames[read_offset + 1] = '\0'; 912 913 if (!setup_connection(hostname, port, &ctx, &ssl)) { 914 fprintf(stderr, "Unable to establish connection\n"); 915 goto end; 916 } 917 918 req = strtok(reqnames, " "); 919 920 while (req != NULL) { 921 total_requests++; 922 req_array = OPENSSL_realloc(req_array, sizeof(char *) * total_requests); 923 if (req_array == NULL) 924 goto end; 925 req_array[total_requests - 1] = req; 926 req = strtok(NULL, " "); 927 } 928 929 /* get a list of requests to poll */ 930 this_poll_count = build_request_set(ssl); 931 932 /* 933 * Now poll all our descriptors for events 934 */ 935 while (this_poll_count != 0 && poll_done < this_poll_count) { 936 result_count = 0; 937 poll_timeout.tv_sec = 0; 938 poll_timeout.tv_usec = 0; 939 if (!SSL_poll(poll_list, this_poll_count, sizeof(SSL_POLL_ITEM), 940 &poll_timeout, 0, &result_count)) { 941 fprintf(stderr, "Failed to poll\n"); 942 goto end; 943 } 944 945 /* Iterate over our poll array looking for ready SSL's */ 946 for (poll_idx = 0; poll_idx < this_poll_count; poll_idx++) { 947 /* 948 * If we have visited the number of SSL's that SSL_poll 949 * indicated were ready, we can go poll again 950 */ 951 if (result_count == 0) 952 break; 953 954 if (poll_list[poll_idx].revents == SSL_POLL_EVENT_R) { 955 /* 956 * We found an SSL that we can read, drop our result count 957 */ 958 result_count--; 959 960 /* And clear the revents for the next poll */ 961 poll_list[poll_idx].revents = 0; 962 963 /* 964 * Get up to sizeof(buf) bytes of the response. We keep reading until 965 * the server closes the connection. 966 */ 967 eof = 0; 968 969 /* Read our data, and handle any errors/eof conditions */ 970 if (!SSL_read_ex(poll_list[poll_idx].desc.value.ssl, buf, 971 sizeof(buf), &readbytes)) { 972 switch (handle_io_failure(poll_list[poll_idx].desc.value.ssl, 973 0)) { 974 case 1: 975 eof = 0; 976 break; /* Retry on next poll */ 977 case 0: 978 eof = 1; 979 break; 980 case -1: 981 default: 982 fprintf(stderr, "Failed reading remaining data\n"); 983 goto end; /* Cannot retry: error */ 984 } 985 } 986 987 /* 988 * If error handling indicated that this SSL is in an EOF state 989 * we mark the SSL as not needing any more polling, and up our 990 * poll_done count. Otherwise, just write to the outbio 991 */ 992 if (!eof) { 993 BIO_write(outbiolist[poll_idx], buf, readbytes); 994 } else { 995 fprintf(stderr, "completed %s\n", outnames[poll_idx]); 996 /* This file is done, take it out of polling contention */ 997 poll_list[poll_idx].events = 0; 998 poll_done++; 999 } 1000 } 1001 } 1002 1003 /* 1004 * If we've completed this poll set, try get another one 1005 */ 1006 if (poll_done == this_poll_count) { 1007 this_poll_count = build_request_set(ssl); 1008 poll_done = 0; 1009 } 1010 } 1011 1012 /* 1013 * Repeatedly call SSL_shutdown() until the connection is fully 1014 * closed. 1015 */ 1016 fprintf(stderr, "Shutting down\n"); 1017 while ((ret = SSL_shutdown(ssl)) != 1) { 1018 if (ret < 0 && handle_io_failure(ssl, ret) == 1) 1019 continue; /* Retry */ 1020 } 1021 1022 /* Success! */ 1023 res = EXIT_SUCCESS; 1024 end: 1025 /* 1026 * If something bad happened then we will dump the contents of the 1027 * OpenSSL error stack to stderr. There might be some useful diagnostic 1028 * information there. 1029 */ 1030 if (res == EXIT_FAILURE) 1031 ERR_print_errors_fp(stderr); 1032 1033 /* 1034 * Free the resources we allocated. We do not free the BIO object here 1035 * because ownership of it was immediately transferred to the SSL object 1036 * via SSL_set_bio(). The BIO will be freed when we free the SSL object. 1037 */ 1038 BIO_ADDR_free(peer_addr); 1039 OPENSSL_free(reqnames); 1040 BIO_free(session_bio); 1041 for (poll_idx = 0; poll_idx < poll_count; poll_idx++) { 1042 BIO_free(outbiolist[poll_idx]); 1043 SSL_free(poll_list[poll_idx].desc.value.ssl); 1044 } 1045 SSL_free(ssl); 1046 SSL_CTX_free(ctx); 1047 OPENSSL_free(outbiolist); 1048 OPENSSL_free(poll_list); 1049 return res; 1050 } 1051