1 /* 2 * Copyright 2023-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 * NB: Changes to this file should also be reflected in 12 * doc/man7/ossl-guide-quic-client-block.pod 13 */ 14 15 #include <string.h> 16 17 /* Include the appropriate header file for SOCK_DGRAM */ 18 #ifdef _WIN32 /* Windows */ 19 # include <winsock2.h> 20 #else /* Linux/Unix */ 21 # include <sys/socket.h> 22 #endif 23 24 #include <openssl/bio.h> 25 #include <openssl/ssl.h> 26 #include <openssl/err.h> 27 28 /* Helper function to create a BIO connected to the server */ 29 static BIO *create_socket_bio(const char *hostname, const char *port, 30 int family, BIO_ADDR **peer_addr) 31 { 32 int sock = -1; 33 BIO_ADDRINFO *res; 34 const BIO_ADDRINFO *ai = NULL; 35 BIO *bio; 36 37 /* 38 * Lookup IP address info for the server. 39 */ 40 if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, 41 &res)) 42 return NULL; 43 44 /* 45 * Loop through all the possible addresses for the server and find one 46 * we can connect to. 47 */ 48 for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) { 49 /* 50 * Create a UDP socket. We could equally use non-OpenSSL calls such 51 * as "socket" here for this and the subsequent connect and close 52 * functions. But for portability reasons and also so that we get 53 * errors on the OpenSSL stack in the event of a failure we use 54 * OpenSSL's versions of these functions. 55 */ 56 sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0); 57 if (sock == -1) 58 continue; 59 60 /* Connect the socket to the server's address */ 61 if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) { 62 BIO_closesocket(sock); 63 sock = -1; 64 continue; 65 } 66 67 /* Set to nonblocking mode */ 68 if (!BIO_socket_nbio(sock, 1)) { 69 BIO_closesocket(sock); 70 sock = -1; 71 continue; 72 } 73 74 break; 75 } 76 77 if (sock != -1) { 78 *peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai)); 79 if (*peer_addr == NULL) { 80 BIO_closesocket(sock); 81 return NULL; 82 } 83 } 84 85 /* Free the address information resources we allocated earlier */ 86 BIO_ADDRINFO_free(res); 87 88 /* If sock is -1 then we've been unable to connect to the server */ 89 if (sock == -1) 90 return NULL; 91 92 /* Create a BIO to wrap the socket */ 93 bio = BIO_new(BIO_s_datagram()); 94 if (bio == NULL) { 95 BIO_closesocket(sock); 96 return NULL; 97 } 98 99 /* 100 * Associate the newly created BIO with the underlying socket. By 101 * passing BIO_CLOSE here the socket will be automatically closed when 102 * the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which 103 * case you must close the socket explicitly when it is no longer 104 * needed. 105 */ 106 BIO_set_fd(bio, sock, BIO_CLOSE); 107 108 return bio; 109 } 110 111 /* 112 * Simple application to send a basic HTTP/1.0 request to a server and 113 * print the response on the screen. Note that HTTP/1.0 over QUIC is 114 * non-standard and will not typically be supported by real world servers. This 115 * is for demonstration purposes only. 116 */ 117 int main(int argc, char *argv[]) 118 { 119 SSL_CTX *ctx = NULL; 120 SSL *ssl = NULL; 121 BIO *bio = NULL; 122 int res = EXIT_FAILURE; 123 int ret; 124 unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' }; 125 const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: "; 126 const char *request_end = "\r\n\r\n"; 127 size_t written, readbytes; 128 char buf[160]; 129 BIO_ADDR *peer_addr = NULL; 130 char *hostname, *port; 131 int argnext = 1; 132 int ipv6 = 0; 133 134 if (argc < 3) { 135 printf("Usage: quic-client-block [-6] hostname port\n"); 136 goto end; 137 } 138 139 if (!strcmp(argv[argnext], "-6")) { 140 if (argc < 4) { 141 printf("Usage: quic-client-block [-6] hostname port\n"); 142 goto end; 143 } 144 ipv6 = 1; 145 argnext++; 146 } 147 hostname = argv[argnext++]; 148 port = argv[argnext]; 149 150 /* 151 * Create an SSL_CTX which we can use to create SSL objects from. We 152 * want an SSL_CTX for creating clients so we use 153 * OSSL_QUIC_client_method() here. 154 */ 155 ctx = SSL_CTX_new(OSSL_QUIC_client_method()); 156 if (ctx == NULL) { 157 printf("Failed to create the SSL_CTX\n"); 158 goto end; 159 } 160 161 /* 162 * Configure the client to abort the handshake if certificate 163 * verification fails. Virtually all clients should do this unless you 164 * really know what you are doing. 165 */ 166 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 167 168 /* Use the default trusted certificate store */ 169 if (!SSL_CTX_set_default_verify_paths(ctx)) { 170 printf("Failed to set the default trusted certificate store\n"); 171 goto end; 172 } 173 174 /* Create an SSL object to represent the TLS connection */ 175 ssl = SSL_new(ctx); 176 if (ssl == NULL) { 177 printf("Failed to create the SSL object\n"); 178 goto end; 179 } 180 181 /* 182 * Create the underlying transport socket/BIO and associate it with the 183 * connection. 184 */ 185 bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, &peer_addr); 186 if (bio == NULL) { 187 printf("Failed to crete the BIO\n"); 188 goto end; 189 } 190 SSL_set_bio(ssl, bio, bio); 191 192 /* 193 * Tell the server during the handshake which hostname we are attempting 194 * to connect to in case the server supports multiple hosts. 195 */ 196 if (!SSL_set_tlsext_host_name(ssl, hostname)) { 197 printf("Failed to set the SNI hostname\n"); 198 goto end; 199 } 200 201 /* 202 * Ensure we check during certificate verification that the server has 203 * supplied a certificate for the hostname that we were expecting. 204 * Virtually all clients should do this unless you really know what you 205 * are doing. 206 */ 207 if (!SSL_set1_host(ssl, hostname)) { 208 printf("Failed to set the certificate verification hostname"); 209 goto end; 210 } 211 212 /* SSL_set_alpn_protos returns 0 for success! */ 213 if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) { 214 printf("Failed to set the ALPN for the connection\n"); 215 goto end; 216 } 217 218 /* Set the IP address of the remote peer */ 219 if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) { 220 printf("Failed to set the initial peer address\n"); 221 goto end; 222 } 223 224 /* Do the handshake with the server */ 225 if (SSL_connect(ssl) < 1) { 226 printf("Failed to connect to the server\n"); 227 /* 228 * If the failure is due to a verification error we can get more 229 * information about it from SSL_get_verify_result(). 230 */ 231 if (SSL_get_verify_result(ssl) != X509_V_OK) 232 printf("Verify error: %s\n", 233 X509_verify_cert_error_string(SSL_get_verify_result(ssl))); 234 goto end; 235 } 236 237 /* Write an HTTP GET request to the peer */ 238 if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) { 239 printf("Failed to write start of HTTP request\n"); 240 goto end; 241 } 242 if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) { 243 printf("Failed to write hostname in HTTP request\n"); 244 goto end; 245 } 246 if (!SSL_write_ex2(ssl, request_end, strlen(request_end), 247 SSL_WRITE_FLAG_CONCLUDE, &written)) { 248 printf("Failed to write end of HTTP request\n"); 249 goto end; 250 } 251 252 /* 253 * Get up to sizeof(buf) bytes of the response. We keep reading until the 254 * server closes the connection. 255 */ 256 while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) { 257 /* 258 * OpenSSL does not guarantee that the returned data is a string or 259 * that it is NUL terminated so we use fwrite() to write the exact 260 * number of bytes that we read. The data could be non-printable or 261 * have NUL characters in the middle of it. For this simple example 262 * we're going to print it to stdout anyway. 263 */ 264 fwrite(buf, 1, readbytes, stdout); 265 } 266 /* In case the response didn't finish with a newline we add one now */ 267 printf("\n"); 268 269 /* 270 * Check whether we finished the while loop above normally or as the 271 * result of an error. The 0 argument to SSL_get_error() is the return 272 * code we received from the SSL_read_ex() call. It must be 0 in order 273 * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In 274 * QUIC terms this means that the peer has sent FIN on the stream to 275 * indicate that no further data will be sent. 276 */ 277 switch (SSL_get_error(ssl, 0)) { 278 case SSL_ERROR_ZERO_RETURN: 279 /* Normal completion of the stream */ 280 break; 281 282 case SSL_ERROR_SSL: 283 /* 284 * Some stream fatal error occurred. This could be because of a stream 285 * reset - or some failure occurred on the underlying connection. 286 */ 287 switch (SSL_get_stream_read_state(ssl)) { 288 case SSL_STREAM_STATE_RESET_REMOTE: 289 printf("Stream reset occurred\n"); 290 /* The stream has been reset but the connection is still healthy. */ 291 break; 292 293 case SSL_STREAM_STATE_CONN_CLOSED: 294 printf("Connection closed\n"); 295 /* Connection is already closed. Skip SSL_shutdown() */ 296 goto end; 297 298 default: 299 printf("Unknown stream failure\n"); 300 break; 301 } 302 break; 303 304 default: 305 /* Some other unexpected error occurred */ 306 printf ("Failed reading remaining data\n"); 307 break; 308 } 309 310 /* 311 * Repeatedly call SSL_shutdown() until the connection is fully 312 * closed. 313 */ 314 do { 315 ret = SSL_shutdown(ssl); 316 if (ret < 0) { 317 printf("Error shutting down: %d\n", ret); 318 goto end; 319 } 320 } while (ret != 1); 321 322 /* Success! */ 323 res = EXIT_SUCCESS; 324 end: 325 /* 326 * If something bad happened then we will dump the contents of the 327 * OpenSSL error stack to stderr. There might be some useful diagnostic 328 * information there. 329 */ 330 if (res == EXIT_FAILURE) 331 ERR_print_errors_fp(stderr); 332 333 /* 334 * Free the resources we allocated. We do not free the BIO object here 335 * because ownership of it was immediately transferred to the SSL object 336 * via SSL_set_bio(). The BIO will be freed when we free the SSL object. 337 */ 338 SSL_free(ssl); 339 SSL_CTX_free(ctx); 340 BIO_ADDR_free(peer_addr); 341 return res; 342 } 343