1*e7be843bSPierre Pronchery=pod 2*e7be843bSPierre Pronchery 3*e7be843bSPierre Pronchery=begin comment 4*e7be843bSPierre Pronchery 5*e7be843bSPierre ProncheryNB: Changes to the source code samples in this file should also be reflected in 6*e7be843bSPierre Proncherydemos/guide/quic-client-block.c 7*e7be843bSPierre Pronchery 8*e7be843bSPierre Pronchery=end comment 9*e7be843bSPierre Pronchery 10*e7be843bSPierre Pronchery=head1 NAME 11*e7be843bSPierre Pronchery 12*e7be843bSPierre Proncheryossl-guide-quic-client-block 13*e7be843bSPierre Pronchery- OpenSSL Guide: Writing a simple blocking QUIC client 14*e7be843bSPierre Pronchery 15*e7be843bSPierre Pronchery=head1 SIMPLE BLOCKING QUIC CLIENT EXAMPLE 16*e7be843bSPierre Pronchery 17*e7be843bSPierre ProncheryThis page will present various source code samples demonstrating how to write 18*e7be843bSPierre Proncherya simple blocking QUIC client application which connects to a server, sends an 19*e7be843bSPierre ProncheryHTTP/1.0 request to it, and reads back the response. Note that HTTP/1.0 over 20*e7be843bSPierre ProncheryQUIC is non-standard and will not be supported by real world servers. This is 21*e7be843bSPierre Proncheryfor demonstration purposes only. 22*e7be843bSPierre Pronchery 23*e7be843bSPierre ProncheryWe assume that you already have OpenSSL installed on your system; that you 24*e7be843bSPierre Proncheryalready have some fundamental understanding of OpenSSL concepts, TLS and QUIC 25*e7be843bSPierre Pronchery(see L<ossl-guide-libraries-introduction(7)>, L<ossl-guide-tls-introduction(7)> 26*e7be843bSPierre Proncheryand L<ossl-guide-quic-introduction(7)>); and that you know how to 27*e7be843bSPierre Proncherywrite and build C code and link it against the libcrypto and libssl libraries 28*e7be843bSPierre Proncherythat are provided by OpenSSL. It also assumes that you have a basic 29*e7be843bSPierre Proncheryunderstanding of UDP/IP and sockets. The example code that we build in this 30*e7be843bSPierre Proncherytutorial will amend the blocking TLS client example that is covered in 31*e7be843bSPierre ProncheryL<ossl-guide-tls-client-block(7)>. Only the differences between that client and 32*e7be843bSPierre Proncherythis one will be discussed so we also assume that you have run through and 33*e7be843bSPierre Proncheryunderstand that tutorial. 34*e7be843bSPierre Pronchery 35*e7be843bSPierre ProncheryFor this tutorial our client will be using a single QUIC stream. A subsequent 36*e7be843bSPierre Proncherytutorial will discuss how to write a multi-stream client (see 37*e7be843bSPierre ProncheryL<ossl-guide-quic-multi-stream(7)>). 38*e7be843bSPierre Pronchery 39*e7be843bSPierre ProncheryThe complete source code for this example blocking QUIC client is available in 40*e7be843bSPierre Proncherythe C<demos/guide> directory of the OpenSSL source distribution in the file 41*e7be843bSPierre ProncheryC<quic-client-block.c>. It is also available online at 42*e7be843bSPierre ProncheryL<https://github.com/openssl/openssl/blob/master/demos/guide/quic-client-block.c>. 43*e7be843bSPierre Pronchery 44*e7be843bSPierre Pronchery=head2 Creating the SSL_CTX and SSL objects 45*e7be843bSPierre Pronchery 46*e7be843bSPierre ProncheryIn the TLS tutorial (L<ossl-guide-tls-client-block(7)>) we created an B<SSL_CTX> 47*e7be843bSPierre Proncheryobject for our client and used it to create an B<SSL> object to represent the 48*e7be843bSPierre ProncheryTLS connection. A QUIC connection works in exactly the same way. We first create 49*e7be843bSPierre Proncheryan B<SSL_CTX> object and then use it to create an B<SSL> object to represent the 50*e7be843bSPierre ProncheryQUIC connection. 51*e7be843bSPierre Pronchery 52*e7be843bSPierre ProncheryAs in the TLS example the first step is to create an B<SSL_CTX> object for our 53*e7be843bSPierre Proncheryclient. This is done in the same way as before except that we use a different 54*e7be843bSPierre Pronchery"method". OpenSSL offers two different QUIC client methods, i.e. 55*e7be843bSPierre ProncheryL<OSSL_QUIC_client_method(3)> and L<OSSL_QUIC_client_thread_method(3)>. 56*e7be843bSPierre Pronchery 57*e7be843bSPierre ProncheryThe first one is the equivalent of L<TLS_client_method(3)> but for the QUIC 58*e7be843bSPierre Proncheryprotocol. The second one is the same, but it will additionally create a 59*e7be843bSPierre Proncherybackground thread for handling time based events (known as "thread assisted 60*e7be843bSPierre Proncherymode", see L<ossl-guide-quic-introduction(7)>). For this tutorial we will be 61*e7be843bSPierre Proncheryusing L<OSSL_QUIC_client_method(3)> because we will not be leaving the QUIC 62*e7be843bSPierre Proncheryconnection idle in our application and so thread assisted mode is not needed. 63*e7be843bSPierre Pronchery 64*e7be843bSPierre Pronchery /* 65*e7be843bSPierre Pronchery * Create an SSL_CTX which we can use to create SSL objects from. We 66*e7be843bSPierre Pronchery * want an SSL_CTX for creating clients so we use OSSL_QUIC_client_method() 67*e7be843bSPierre Pronchery * here. 68*e7be843bSPierre Pronchery */ 69*e7be843bSPierre Pronchery ctx = SSL_CTX_new(OSSL_QUIC_client_method()); 70*e7be843bSPierre Pronchery if (ctx == NULL) { 71*e7be843bSPierre Pronchery printf("Failed to create the SSL_CTX\n"); 72*e7be843bSPierre Pronchery goto end; 73*e7be843bSPierre Pronchery } 74*e7be843bSPierre Pronchery 75*e7be843bSPierre ProncheryThe other setup steps that we applied to the B<SSL_CTX> for TLS also apply to 76*e7be843bSPierre ProncheryQUIC except for restricting the TLS versions that we are willing to accept. The 77*e7be843bSPierre ProncheryQUIC protocol implementation in OpenSSL currently only supports TLSv1.3. There 78*e7be843bSPierre Proncheryis no need to call L<SSL_CTX_set_min_proto_version(3)> or 79*e7be843bSPierre ProncheryL<SSL_CTX_set_max_proto_version(3)> in an OpenSSL QUIC application, and any such 80*e7be843bSPierre Proncherycall will be ignored. 81*e7be843bSPierre Pronchery 82*e7be843bSPierre ProncheryOnce the B<SSL_CTX> is created, the B<SSL> object is constructed in exactly the 83*e7be843bSPierre Proncherysame way as for the TLS application. 84*e7be843bSPierre Pronchery 85*e7be843bSPierre Pronchery=head2 Creating the socket and BIO 86*e7be843bSPierre Pronchery 87*e7be843bSPierre ProncheryA major difference between TLS and QUIC is the underlying transport protocol. 88*e7be843bSPierre ProncheryTLS uses TCP while QUIC uses UDP. The way that the QUIC socket is created in our 89*e7be843bSPierre Proncheryexample code is much the same as for TLS. We use the L<BIO_lookup_ex(3)> and 90*e7be843bSPierre ProncheryL<BIO_socket(3)> helper functions as we did in the previous tutorial except that 91*e7be843bSPierre Proncherywe pass B<SOCK_DGRAM> as an argument to indicate UDP (instead of B<SOCK_STREAM> 92*e7be843bSPierre Proncheryfor TCP). 93*e7be843bSPierre Pronchery 94*e7be843bSPierre Pronchery /* 95*e7be843bSPierre Pronchery * Lookup IP address info for the server. 96*e7be843bSPierre Pronchery */ 97*e7be843bSPierre Pronchery if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, 98*e7be843bSPierre Pronchery &res)) 99*e7be843bSPierre Pronchery return NULL; 100*e7be843bSPierre Pronchery 101*e7be843bSPierre Pronchery /* 102*e7be843bSPierre Pronchery * Loop through all the possible addresses for the server and find one 103*e7be843bSPierre Pronchery * we can connect to. 104*e7be843bSPierre Pronchery */ 105*e7be843bSPierre Pronchery for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) { 106*e7be843bSPierre Pronchery /* 107*e7be843bSPierre Pronchery * Create a TCP socket. We could equally use non-OpenSSL calls such 108*e7be843bSPierre Pronchery * as "socket" here for this and the subsequent connect and close 109*e7be843bSPierre Pronchery * functions. But for portability reasons and also so that we get 110*e7be843bSPierre Pronchery * errors on the OpenSSL stack in the event of a failure we use 111*e7be843bSPierre Pronchery * OpenSSL's versions of these functions. 112*e7be843bSPierre Pronchery */ 113*e7be843bSPierre Pronchery sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0); 114*e7be843bSPierre Pronchery if (sock == -1) 115*e7be843bSPierre Pronchery continue; 116*e7be843bSPierre Pronchery 117*e7be843bSPierre Pronchery /* Connect the socket to the server's address */ 118*e7be843bSPierre Pronchery if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) { 119*e7be843bSPierre Pronchery BIO_closesocket(sock); 120*e7be843bSPierre Pronchery sock = -1; 121*e7be843bSPierre Pronchery continue; 122*e7be843bSPierre Pronchery } 123*e7be843bSPierre Pronchery 124*e7be843bSPierre Pronchery /* Set to nonblocking mode */ 125*e7be843bSPierre Pronchery if (!BIO_socket_nbio(sock, 1)) { 126*e7be843bSPierre Pronchery BIO_closesocket(sock); 127*e7be843bSPierre Pronchery sock = -1; 128*e7be843bSPierre Pronchery continue; 129*e7be843bSPierre Pronchery } 130*e7be843bSPierre Pronchery 131*e7be843bSPierre Pronchery break; 132*e7be843bSPierre Pronchery } 133*e7be843bSPierre Pronchery 134*e7be843bSPierre Pronchery if (sock != -1) { 135*e7be843bSPierre Pronchery *peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai)); 136*e7be843bSPierre Pronchery if (*peer_addr == NULL) { 137*e7be843bSPierre Pronchery BIO_closesocket(sock); 138*e7be843bSPierre Pronchery return NULL; 139*e7be843bSPierre Pronchery } 140*e7be843bSPierre Pronchery } 141*e7be843bSPierre Pronchery 142*e7be843bSPierre Pronchery /* Free the address information resources we allocated earlier */ 143*e7be843bSPierre Pronchery BIO_ADDRINFO_free(res); 144*e7be843bSPierre Pronchery 145*e7be843bSPierre ProncheryYou may notice a couple of other differences between this code and the version 146*e7be843bSPierre Proncherythat we used for TLS. 147*e7be843bSPierre Pronchery 148*e7be843bSPierre ProncheryFirstly, we set the socket into nonblocking mode. This must always be done for 149*e7be843bSPierre Proncheryan OpenSSL QUIC application. This may be surprising considering that we are 150*e7be843bSPierre Proncherytrying to write a blocking client. Despite this the B<SSL> object will still 151*e7be843bSPierre Proncheryhave blocking behaviour. See L<ossl-guide-quic-introduction(7)> for further 152*e7be843bSPierre Proncheryinformation on this. 153*e7be843bSPierre Pronchery 154*e7be843bSPierre ProncherySecondly, we take note of the IP address of the peer that we are connecting to. 155*e7be843bSPierre ProncheryWe store that information away. We will need it later. 156*e7be843bSPierre Pronchery 157*e7be843bSPierre ProncherySee L<BIO_lookup_ex(3)>, L<BIO_socket(3)>, L<BIO_connect(3)>, 158*e7be843bSPierre ProncheryL<BIO_closesocket(3)>, L<BIO_ADDRINFO_next(3)>, L<BIO_ADDRINFO_address(3)>, 159*e7be843bSPierre ProncheryL<BIO_ADDRINFO_free(3)> and L<BIO_ADDR_dup(3)> for further information on the 160*e7be843bSPierre Proncheryfunctions used here. In the above example code the B<hostname> and B<port> 161*e7be843bSPierre Proncheryvariables are strings, e.g. "www.example.com" and "443". 162*e7be843bSPierre Pronchery 163*e7be843bSPierre ProncheryAs for our TLS client, once the socket has been created and connected we need to 164*e7be843bSPierre Proncheryassociate it with a BIO object: 165*e7be843bSPierre Pronchery 166*e7be843bSPierre Pronchery BIO *bio; 167*e7be843bSPierre Pronchery 168*e7be843bSPierre Pronchery /* Create a BIO to wrap the socket */ 169*e7be843bSPierre Pronchery bio = BIO_new(BIO_s_datagram()); 170*e7be843bSPierre Pronchery if (bio == NULL) { 171*e7be843bSPierre Pronchery BIO_closesocket(sock); 172*e7be843bSPierre Pronchery return NULL; 173*e7be843bSPierre Pronchery } 174*e7be843bSPierre Pronchery 175*e7be843bSPierre Pronchery /* 176*e7be843bSPierre Pronchery * Associate the newly created BIO with the underlying socket. By 177*e7be843bSPierre Pronchery * passing BIO_CLOSE here the socket will be automatically closed when 178*e7be843bSPierre Pronchery * the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which 179*e7be843bSPierre Pronchery * case you must close the socket explicitly when it is no longer 180*e7be843bSPierre Pronchery * needed. 181*e7be843bSPierre Pronchery */ 182*e7be843bSPierre Pronchery BIO_set_fd(bio, sock, BIO_CLOSE); 183*e7be843bSPierre Pronchery 184*e7be843bSPierre ProncheryNote the use of L<BIO_s_datagram(3)> here as opposed to L<BIO_s_socket(3)> that 185*e7be843bSPierre Proncherywe used for our TLS client. This is again due to the fact that QUIC uses UDP 186*e7be843bSPierre Proncheryinstead of TCP for its transport layer. See L<BIO_new(3)>, L<BIO_s_datagram(3)> 187*e7be843bSPierre Proncheryand L<BIO_set_fd(3)> for further information on these functions. 188*e7be843bSPierre Pronchery 189*e7be843bSPierre Pronchery=head2 Setting the server's hostname 190*e7be843bSPierre Pronchery 191*e7be843bSPierre ProncheryAs in the TLS tutorial we need to set the server's hostname both for SNI (Server 192*e7be843bSPierre ProncheryName Indication) and for certificate validation purposes. The steps for this are 193*e7be843bSPierre Proncheryidentical to the TLS tutorial and won't be repeated here. 194*e7be843bSPierre Pronchery 195*e7be843bSPierre Pronchery=head2 Setting the ALPN 196*e7be843bSPierre Pronchery 197*e7be843bSPierre ProncheryALPN (Application-Layer Protocol Negotiation) is a feature of TLS that enables 198*e7be843bSPierre Proncherythe application to negotiate which protocol will be used over the connection. 199*e7be843bSPierre ProncheryFor example, if you intend to use HTTP/3 over the connection then the ALPN value 200*e7be843bSPierre Proncheryfor that is "h3" (see 201*e7be843bSPierre ProncheryL<https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xml#alpn-protocol-ids>). 202*e7be843bSPierre ProncheryOpenSSL provides the ability for a client to specify the ALPN to use via the 203*e7be843bSPierre ProncheryL<SSL_set_alpn_protos(3)> function. This is optional for a TLS client and so our 204*e7be843bSPierre Proncherysimple client that we developed in L<ossl-guide-tls-client-block(7)> did not use 205*e7be843bSPierre Proncheryit. However QUIC mandates that the TLS handshake used in establishing a QUIC 206*e7be843bSPierre Proncheryconnection must use ALPN. 207*e7be843bSPierre Pronchery 208*e7be843bSPierre Pronchery unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' }; 209*e7be843bSPierre Pronchery 210*e7be843bSPierre Pronchery /* SSL_set_alpn_protos returns 0 for success! */ 211*e7be843bSPierre Pronchery if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) { 212*e7be843bSPierre Pronchery printf("Failed to set the ALPN for the connection\n"); 213*e7be843bSPierre Pronchery goto end; 214*e7be843bSPierre Pronchery } 215*e7be843bSPierre Pronchery 216*e7be843bSPierre ProncheryThe ALPN is specified using a length prefixed array of unsigned chars (it is not 217*e7be843bSPierre Proncherya NUL terminated string). Our original TLS blocking client demo was using 218*e7be843bSPierre ProncheryHTTP/1.0. We will use the same for this example. Unlike most OpenSSL functions 219*e7be843bSPierre ProncheryL<SSL_set_alpn_protos(3)> returns zero for success and nonzero for failure. 220*e7be843bSPierre Pronchery 221*e7be843bSPierre Pronchery=head2 Setting the peer address 222*e7be843bSPierre Pronchery 223*e7be843bSPierre ProncheryAn OpenSSL QUIC application must specify the target address of the server that 224*e7be843bSPierre Proncheryis being connected to. In L</Creating the socket and BIO> above we saved that 225*e7be843bSPierre Proncheryaddress away for future use. Now we need to use it via the 226*e7be843bSPierre ProncheryL<SSL_set1_initial_peer_addr(3)> function. 227*e7be843bSPierre Pronchery 228*e7be843bSPierre Pronchery /* Set the IP address of the remote peer */ 229*e7be843bSPierre Pronchery if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) { 230*e7be843bSPierre Pronchery printf("Failed to set the initial peer address\n"); 231*e7be843bSPierre Pronchery goto end; 232*e7be843bSPierre Pronchery } 233*e7be843bSPierre Pronchery 234*e7be843bSPierre ProncheryNote that we will need to free the B<peer_addr> value that we allocated via 235*e7be843bSPierre ProncheryL<BIO_ADDR_dup(3)> earlier: 236*e7be843bSPierre Pronchery 237*e7be843bSPierre Pronchery BIO_ADDR_free(peer_addr); 238*e7be843bSPierre Pronchery 239*e7be843bSPierre Pronchery=head2 The handshake and application data transfer 240*e7be843bSPierre Pronchery 241*e7be843bSPierre ProncheryOnce initial setup of the B<SSL> object is complete then we perform the 242*e7be843bSPierre Proncheryhandshake via L<SSL_connect(3)> in exactly the same way as we did for the TLS 243*e7be843bSPierre Proncheryclient, so we won't repeat it here. 244*e7be843bSPierre Pronchery 245*e7be843bSPierre ProncheryWe can also perform data transfer using a default QUIC stream that is 246*e7be843bSPierre Proncheryautomatically associated with the B<SSL> object for us. We can transmit data 247*e7be843bSPierre Proncheryusing L<SSL_write_ex(3)>, and receive data using L<SSL_read_ex(3)> in the same 248*e7be843bSPierre Proncheryway as for TLS. The main difference is that we have to account for failures 249*e7be843bSPierre Proncheryslightly differently. With QUIC the stream can be reset by the peer (which is 250*e7be843bSPierre Proncheryfatal for that stream), but the underlying connection itself may still be 251*e7be843bSPierre Proncheryhealthy. 252*e7be843bSPierre Pronchery 253*e7be843bSPierre ProncheryFirst, we write the entire request to the stream. We also must make sure to 254*e7be843bSPierre Proncherysignal to the server that we have finished writing. This can be done by passing 255*e7be843bSPierre Proncherythe SSL_WRITE_FLAG_CONCLUDE flag to L<SSL_write_ex2(3)> or by calling 256*e7be843bSPierre ProncheryL<SSL_stream_conclude(3)>. Since the first way is more efficient, we choose to 257*e7be843bSPierre Proncherydo that. 258*e7be843bSPierre Pronchery 259*e7be843bSPierre Pronchery /* Write an HTTP GET request to the peer */ 260*e7be843bSPierre Pronchery if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) { 261*e7be843bSPierre Pronchery printf("Failed to write start of HTTP request\n"); 262*e7be843bSPierre Pronchery goto end; 263*e7be843bSPierre Pronchery } 264*e7be843bSPierre Pronchery if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) { 265*e7be843bSPierre Pronchery printf("Failed to write hostname in HTTP request\n"); 266*e7be843bSPierre Pronchery goto end; 267*e7be843bSPierre Pronchery } 268*e7be843bSPierre Pronchery if (!SSL_write_ex2(ssl, request_end, strlen(request_end), 269*e7be843bSPierre Pronchery SSL_WRITE_FLAG_CONCLUDE, &written)) { 270*e7be843bSPierre Pronchery printf("Failed to write end of HTTP request\n"); 271*e7be843bSPierre Pronchery goto end; 272*e7be843bSPierre Pronchery } 273*e7be843bSPierre Pronchery 274*e7be843bSPierre ProncheryThen, we read the response from the server. 275*e7be843bSPierre Pronchery 276*e7be843bSPierre Pronchery /* 277*e7be843bSPierre Pronchery * Get up to sizeof(buf) bytes of the response. We keep reading until the 278*e7be843bSPierre Pronchery * server closes the connection. 279*e7be843bSPierre Pronchery */ 280*e7be843bSPierre Pronchery while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) { 281*e7be843bSPierre Pronchery /* 282*e7be843bSPierre Pronchery * OpenSSL does not guarantee that the returned data is a string or 283*e7be843bSPierre Pronchery * that it is NUL terminated so we use fwrite() to write the exact 284*e7be843bSPierre Pronchery * number of bytes that we read. The data could be non-printable or 285*e7be843bSPierre Pronchery * have NUL characters in the middle of it. For this simple example 286*e7be843bSPierre Pronchery * we're going to print it to stdout anyway. 287*e7be843bSPierre Pronchery */ 288*e7be843bSPierre Pronchery fwrite(buf, 1, readbytes, stdout); 289*e7be843bSPierre Pronchery } 290*e7be843bSPierre Pronchery /* In case the response didn't finish with a newline we add one now */ 291*e7be843bSPierre Pronchery printf("\n"); 292*e7be843bSPierre Pronchery 293*e7be843bSPierre Pronchery /* 294*e7be843bSPierre Pronchery * Check whether we finished the while loop above normally or as the 295*e7be843bSPierre Pronchery * result of an error. The 0 argument to SSL_get_error() is the return 296*e7be843bSPierre Pronchery * code we received from the SSL_read_ex() call. It must be 0 in order 297*e7be843bSPierre Pronchery * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In 298*e7be843bSPierre Pronchery * QUIC terms this means that the peer has sent FIN on the stream to 299*e7be843bSPierre Pronchery * indicate that no further data will be sent. 300*e7be843bSPierre Pronchery */ 301*e7be843bSPierre Pronchery switch (SSL_get_error(ssl, 0)) { 302*e7be843bSPierre Pronchery case SSL_ERROR_ZERO_RETURN: 303*e7be843bSPierre Pronchery /* Normal completion of the stream */ 304*e7be843bSPierre Pronchery break; 305*e7be843bSPierre Pronchery 306*e7be843bSPierre Pronchery case SSL_ERROR_SSL: 307*e7be843bSPierre Pronchery /* 308*e7be843bSPierre Pronchery * Some stream fatal error occurred. This could be because of a stream 309*e7be843bSPierre Pronchery * reset - or some failure occurred on the underlying connection. 310*e7be843bSPierre Pronchery */ 311*e7be843bSPierre Pronchery switch (SSL_get_stream_read_state(ssl)) { 312*e7be843bSPierre Pronchery case SSL_STREAM_STATE_RESET_REMOTE: 313*e7be843bSPierre Pronchery printf("Stream reset occurred\n"); 314*e7be843bSPierre Pronchery /* The stream has been reset but the connection is still healthy. */ 315*e7be843bSPierre Pronchery break; 316*e7be843bSPierre Pronchery 317*e7be843bSPierre Pronchery case SSL_STREAM_STATE_CONN_CLOSED: 318*e7be843bSPierre Pronchery printf("Connection closed\n"); 319*e7be843bSPierre Pronchery /* Connection is already closed. Skip SSL_shutdown() */ 320*e7be843bSPierre Pronchery goto end; 321*e7be843bSPierre Pronchery 322*e7be843bSPierre Pronchery default: 323*e7be843bSPierre Pronchery printf("Unknown stream failure\n"); 324*e7be843bSPierre Pronchery break; 325*e7be843bSPierre Pronchery } 326*e7be843bSPierre Pronchery break; 327*e7be843bSPierre Pronchery 328*e7be843bSPierre Pronchery default: 329*e7be843bSPierre Pronchery /* Some other unexpected error occurred */ 330*e7be843bSPierre Pronchery printf ("Failed reading remaining data\n"); 331*e7be843bSPierre Pronchery break; 332*e7be843bSPierre Pronchery } 333*e7be843bSPierre Pronchery 334*e7be843bSPierre ProncheryIn the above code example you can see that B<SSL_ERROR_SSL> indicates a stream 335*e7be843bSPierre Proncheryfatal error. We can use L<SSL_get_stream_read_state(3)> to determine whether the 336*e7be843bSPierre Proncherystream has been reset, or if some other fatal error has occurred. 337*e7be843bSPierre Pronchery 338*e7be843bSPierre Pronchery=head2 Shutting down the connection 339*e7be843bSPierre Pronchery 340*e7be843bSPierre ProncheryIn the TLS tutorial we knew that the server had finished sending data because 341*e7be843bSPierre ProncheryL<SSL_read_ex(3)> returned 0, and L<SSL_get_error(3)> returned 342*e7be843bSPierre ProncheryB<SSL_ERROR_ZERO_RETURN>. The same is true with QUIC except that 343*e7be843bSPierre ProncheryB<SSL_ERROR_ZERO_RETURN> should be interpreted slightly differently. With TLS 344*e7be843bSPierre Proncherywe knew that this meant that the server had sent a "close_notify" alert. No 345*e7be843bSPierre Proncherymore data will be sent from the server on that connection. 346*e7be843bSPierre Pronchery 347*e7be843bSPierre ProncheryWith QUIC it means that the server has indicated "FIN" on the stream, meaning 348*e7be843bSPierre Proncherythat it will no longer send any more data on that stream. However this only 349*e7be843bSPierre Proncherygives us information about the stream itself and does not tell us anything about 350*e7be843bSPierre Proncherythe underlying connection. More data could still be sent from the server on some 351*e7be843bSPierre Proncheryother stream. Additionally, although the server will not send any more data to 352*e7be843bSPierre Proncherythe client, it does not prevent the client from sending more data to the server. 353*e7be843bSPierre Pronchery 354*e7be843bSPierre ProncheryIn this tutorial, once we have finished reading data from the server on the one 355*e7be843bSPierre Proncherystream that we are using, we will close the connection down. As before we do 356*e7be843bSPierre Proncherythis via the L<SSL_shutdown(3)> function. This example for QUIC is very similar 357*e7be843bSPierre Proncheryto the TLS version. However the L<SSL_shutdown(3)> function will need to be 358*e7be843bSPierre Proncherycalled more than once: 359*e7be843bSPierre Pronchery 360*e7be843bSPierre Pronchery /* 361*e7be843bSPierre Pronchery * Repeatedly call SSL_shutdown() until the connection is fully 362*e7be843bSPierre Pronchery * closed. 363*e7be843bSPierre Pronchery */ 364*e7be843bSPierre Pronchery do { 365*e7be843bSPierre Pronchery ret = SSL_shutdown(ssl); 366*e7be843bSPierre Pronchery if (ret < 0) { 367*e7be843bSPierre Pronchery printf("Error shutting down: %d\n", ret); 368*e7be843bSPierre Pronchery goto end; 369*e7be843bSPierre Pronchery } 370*e7be843bSPierre Pronchery } while (ret != 1); 371*e7be843bSPierre Pronchery 372*e7be843bSPierre ProncheryThe shutdown process is in two stages. In the first stage we wait until all the 373*e7be843bSPierre Proncherydata we have buffered for sending on any stream has been successfully sent and 374*e7be843bSPierre Proncheryacknowledged by the peer, and then we send a CONNECTION_CLOSE to the peer to 375*e7be843bSPierre Proncheryindicate that the connection is no longer usable. This immediately closes the 376*e7be843bSPierre Proncheryconnection and no more data can be sent or received. L<SSL_shutdown(3)> returns 377*e7be843bSPierre Pronchery0 once the first stage has been completed. 378*e7be843bSPierre Pronchery 379*e7be843bSPierre ProncheryIn the second stage the connection enters a "closing" state. Application data 380*e7be843bSPierre Proncherycannot be sent or received in this state, but late arriving packets coming from 381*e7be843bSPierre Proncherythe peer will be handled appropriately. Once this stage has completed 382*e7be843bSPierre Proncherysuccessfully L<SSL_shutdown(3)> will return 1 to indicate success. 383*e7be843bSPierre Pronchery 384*e7be843bSPierre Pronchery=head1 FURTHER READING 385*e7be843bSPierre Pronchery 386*e7be843bSPierre ProncherySee L<ossl-guide-quic-multi-stream(7)> to read a tutorial on how to modify the 387*e7be843bSPierre Proncheryclient developed on this page to support multiple streams. 388*e7be843bSPierre Pronchery 389*e7be843bSPierre Pronchery=head1 SEE ALSO 390*e7be843bSPierre Pronchery 391*e7be843bSPierre ProncheryL<ossl-guide-introduction(7)>, L<ossl-guide-libraries-introduction(7)>, 392*e7be843bSPierre ProncheryL<ossl-guide-libssl-introduction(7)>, L<ossl-guide-tls-introduction(7)>, 393*e7be843bSPierre ProncheryL<ossl-guide-tls-client-block(7)>, L<ossl-guide-quic-introduction(7)> 394*e7be843bSPierre Pronchery 395*e7be843bSPierre Pronchery=head1 COPYRIGHT 396*e7be843bSPierre Pronchery 397*e7be843bSPierre ProncheryCopyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved. 398*e7be843bSPierre Pronchery 399*e7be843bSPierre ProncheryLicensed under the Apache License 2.0 (the "License"). You may not use 400*e7be843bSPierre Proncherythis file except in compliance with the License. You can obtain a copy 401*e7be843bSPierre Proncheryin the file LICENSE in the source distribution or at 402*e7be843bSPierre ProncheryL<https://www.openssl.org/source/license.html>. 403*e7be843bSPierre Pronchery 404*e7be843bSPierre Pronchery=cut 405