xref: /freebsd/crypto/openssl/test/quic_client_test.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1*e7be843bSPierre Pronchery /*
2*e7be843bSPierre Pronchery  * Copyright 2023-2024 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 #include <stdio.h>
10*e7be843bSPierre Pronchery #include <openssl/ssl.h>
11*e7be843bSPierre Pronchery #include <openssl/quic.h>
12*e7be843bSPierre Pronchery #include <openssl/bio.h>
13*e7be843bSPierre Pronchery #include "internal/common.h"
14*e7be843bSPierre Pronchery #include "internal/sockets.h"
15*e7be843bSPierre Pronchery #include "internal/time.h"
16*e7be843bSPierre Pronchery #include "testutil.h"
17*e7be843bSPierre Pronchery 
18*e7be843bSPierre Pronchery static const char msg1[] = "GET LICENSE.txt\r\n";
19*e7be843bSPierre Pronchery static char msg2[16000];
20*e7be843bSPierre Pronchery 
21*e7be843bSPierre Pronchery #define DST_PORT        4433
22*e7be843bSPierre Pronchery #define DST_ADDR        0x7f000001UL
23*e7be843bSPierre Pronchery 
is_want(SSL * s,int ret)24*e7be843bSPierre Pronchery static int is_want(SSL *s, int ret)
25*e7be843bSPierre Pronchery {
26*e7be843bSPierre Pronchery     int ec = SSL_get_error(s, ret);
27*e7be843bSPierre Pronchery 
28*e7be843bSPierre Pronchery     return ec == SSL_ERROR_WANT_READ || ec == SSL_ERROR_WANT_WRITE;
29*e7be843bSPierre Pronchery }
30*e7be843bSPierre Pronchery 
test_quic_client_ex(int fd_arg)31*e7be843bSPierre Pronchery static int test_quic_client_ex(int fd_arg)
32*e7be843bSPierre Pronchery {
33*e7be843bSPierre Pronchery     int testresult = 0, ret;
34*e7be843bSPierre Pronchery     int c_fd;
35*e7be843bSPierre Pronchery     BIO *c_net_bio = NULL, *c_net_bio_own = NULL;
36*e7be843bSPierre Pronchery     BIO_ADDR *s_addr_ = NULL;
37*e7be843bSPierre Pronchery     struct in_addr ina = {0};
38*e7be843bSPierre Pronchery     SSL_CTX *c_ctx = NULL;
39*e7be843bSPierre Pronchery     SSL *c_ssl = NULL;
40*e7be843bSPierre Pronchery     short port = DST_PORT;
41*e7be843bSPierre Pronchery     int c_connected = 0, c_write_done = 0, c_shutdown = 0;
42*e7be843bSPierre Pronchery     size_t l = 0, c_total_read = 0;
43*e7be843bSPierre Pronchery     OSSL_TIME start_time;
44*e7be843bSPierre Pronchery     unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '0', '.', '9' };
45*e7be843bSPierre Pronchery 
46*e7be843bSPierre Pronchery 
47*e7be843bSPierre Pronchery     if (fd_arg == INVALID_SOCKET) {
48*e7be843bSPierre Pronchery         /* Setup test client. */
49*e7be843bSPierre Pronchery         c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
50*e7be843bSPierre Pronchery         if (!TEST_int_ne(c_fd, INVALID_SOCKET))
51*e7be843bSPierre Pronchery             goto err;
52*e7be843bSPierre Pronchery 
53*e7be843bSPierre Pronchery         if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
54*e7be843bSPierre Pronchery             goto err;
55*e7be843bSPierre Pronchery 
56*e7be843bSPierre Pronchery         if (!TEST_ptr(s_addr_ = BIO_ADDR_new()))
57*e7be843bSPierre Pronchery             goto err;
58*e7be843bSPierre Pronchery 
59*e7be843bSPierre Pronchery         ina.s_addr = htonl(DST_ADDR);
60*e7be843bSPierre Pronchery         if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina),
61*e7be843bSPierre Pronchery                                         htons(port))))
62*e7be843bSPierre Pronchery             goto err;
63*e7be843bSPierre Pronchery     } else {
64*e7be843bSPierre Pronchery         c_fd = fd_arg;
65*e7be843bSPierre Pronchery     }
66*e7be843bSPierre Pronchery 
67*e7be843bSPierre Pronchery     if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0)))
68*e7be843bSPierre Pronchery         goto err;
69*e7be843bSPierre Pronchery 
70*e7be843bSPierre Pronchery     /* connected socket does not need to set peer */
71*e7be843bSPierre Pronchery     if (s_addr_ != NULL && !BIO_dgram_set_peer(c_net_bio, s_addr_))
72*e7be843bSPierre Pronchery         goto err;
73*e7be843bSPierre Pronchery 
74*e7be843bSPierre Pronchery     if (!TEST_ptr(c_ctx = SSL_CTX_new(OSSL_QUIC_client_method())))
75*e7be843bSPierre Pronchery         goto err;
76*e7be843bSPierre Pronchery 
77*e7be843bSPierre Pronchery     if (!TEST_ptr(c_ssl = SSL_new(c_ctx)))
78*e7be843bSPierre Pronchery         goto err;
79*e7be843bSPierre Pronchery 
80*e7be843bSPierre Pronchery     /* 0 is a success for SSL_set_alpn_protos() */
81*e7be843bSPierre Pronchery     if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn))))
82*e7be843bSPierre Pronchery         goto err;
83*e7be843bSPierre Pronchery 
84*e7be843bSPierre Pronchery     /* Takes ownership of our reference to the BIO. */
85*e7be843bSPierre Pronchery     SSL_set0_rbio(c_ssl, c_net_bio);
86*e7be843bSPierre Pronchery 
87*e7be843bSPierre Pronchery     /* Get another reference to be transferred in the SSL_set0_wbio call. */
88*e7be843bSPierre Pronchery     if (!TEST_true(BIO_up_ref(c_net_bio))) {
89*e7be843bSPierre Pronchery         c_net_bio_own = NULL; /* SSL_free will free the first reference. */
90*e7be843bSPierre Pronchery         goto err;
91*e7be843bSPierre Pronchery     }
92*e7be843bSPierre Pronchery 
93*e7be843bSPierre Pronchery     SSL_set0_wbio(c_ssl, c_net_bio);
94*e7be843bSPierre Pronchery     c_net_bio_own = NULL;
95*e7be843bSPierre Pronchery 
96*e7be843bSPierre Pronchery     if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0)))
97*e7be843bSPierre Pronchery         goto err;
98*e7be843bSPierre Pronchery 
99*e7be843bSPierre Pronchery     start_time = ossl_time_now();
100*e7be843bSPierre Pronchery 
101*e7be843bSPierre Pronchery     for (;;) {
102*e7be843bSPierre Pronchery         if (ossl_time_compare(ossl_time_subtract(ossl_time_now(), start_time),
103*e7be843bSPierre Pronchery                               ossl_ms2time(10000)) >= 0) {
104*e7be843bSPierre Pronchery             TEST_error("timeout while attempting QUIC client test");
105*e7be843bSPierre Pronchery             goto err;
106*e7be843bSPierre Pronchery         }
107*e7be843bSPierre Pronchery 
108*e7be843bSPierre Pronchery         if (!c_connected) {
109*e7be843bSPierre Pronchery             ret = SSL_connect(c_ssl);
110*e7be843bSPierre Pronchery             if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
111*e7be843bSPierre Pronchery                 goto err;
112*e7be843bSPierre Pronchery 
113*e7be843bSPierre Pronchery             if (ret == 1) {
114*e7be843bSPierre Pronchery                 c_connected = 1;
115*e7be843bSPierre Pronchery                 TEST_info("Connected!");
116*e7be843bSPierre Pronchery             }
117*e7be843bSPierre Pronchery         }
118*e7be843bSPierre Pronchery 
119*e7be843bSPierre Pronchery         if (c_connected && !c_write_done) {
120*e7be843bSPierre Pronchery             if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1),
121*e7be843bSPierre Pronchery                              (int)sizeof(msg1) - 1))
122*e7be843bSPierre Pronchery                 goto err;
123*e7be843bSPierre Pronchery 
124*e7be843bSPierre Pronchery             if (!TEST_true(SSL_stream_conclude(c_ssl, 0)))
125*e7be843bSPierre Pronchery                 goto err;
126*e7be843bSPierre Pronchery 
127*e7be843bSPierre Pronchery             c_write_done = 1;
128*e7be843bSPierre Pronchery         }
129*e7be843bSPierre Pronchery 
130*e7be843bSPierre Pronchery         if (c_write_done && !c_shutdown && c_total_read < sizeof(msg2) - 1) {
131*e7be843bSPierre Pronchery             ret = SSL_read_ex(c_ssl, msg2 + c_total_read,
132*e7be843bSPierre Pronchery                               sizeof(msg2) - 1 - c_total_read, &l);
133*e7be843bSPierre Pronchery             if (ret != 1) {
134*e7be843bSPierre Pronchery                 if (SSL_get_error(c_ssl, ret) == SSL_ERROR_ZERO_RETURN) {
135*e7be843bSPierre Pronchery                     c_shutdown = 1;
136*e7be843bSPierre Pronchery                     TEST_info("Message:\n%s\n", msg2);
137*e7be843bSPierre Pronchery                 } else if (!TEST_true(is_want(c_ssl, ret))) {
138*e7be843bSPierre Pronchery                     goto err;
139*e7be843bSPierre Pronchery                 }
140*e7be843bSPierre Pronchery             } else {
141*e7be843bSPierre Pronchery                 c_total_read += l;
142*e7be843bSPierre Pronchery 
143*e7be843bSPierre Pronchery                 if (!TEST_size_t_lt(c_total_read, sizeof(msg2) - 1))
144*e7be843bSPierre Pronchery                     goto err;
145*e7be843bSPierre Pronchery             }
146*e7be843bSPierre Pronchery         }
147*e7be843bSPierre Pronchery 
148*e7be843bSPierre Pronchery         if (c_shutdown) {
149*e7be843bSPierre Pronchery             ret = SSL_shutdown(c_ssl);
150*e7be843bSPierre Pronchery             if (ret == 1)
151*e7be843bSPierre Pronchery                 break;
152*e7be843bSPierre Pronchery         }
153*e7be843bSPierre Pronchery 
154*e7be843bSPierre Pronchery         /*
155*e7be843bSPierre Pronchery          * This is inefficient because we spin until things work without
156*e7be843bSPierre Pronchery          * blocking but this is just a test.
157*e7be843bSPierre Pronchery          */
158*e7be843bSPierre Pronchery         OSSL_sleep(0);
159*e7be843bSPierre Pronchery         SSL_handle_events(c_ssl);
160*e7be843bSPierre Pronchery     }
161*e7be843bSPierre Pronchery 
162*e7be843bSPierre Pronchery     testresult = 1;
163*e7be843bSPierre Pronchery err:
164*e7be843bSPierre Pronchery     SSL_free(c_ssl);
165*e7be843bSPierre Pronchery     SSL_CTX_free(c_ctx);
166*e7be843bSPierre Pronchery     BIO_ADDR_free(s_addr_);
167*e7be843bSPierre Pronchery     BIO_free(c_net_bio_own);
168*e7be843bSPierre Pronchery     if (fd_arg == INVALID_SOCKET && c_fd != INVALID_SOCKET)
169*e7be843bSPierre Pronchery         BIO_closesocket(c_fd);
170*e7be843bSPierre Pronchery     return testresult;
171*e7be843bSPierre Pronchery }
172*e7be843bSPierre Pronchery 
test_quic_client(void)173*e7be843bSPierre Pronchery static int test_quic_client(void)
174*e7be843bSPierre Pronchery {
175*e7be843bSPierre Pronchery     return (test_quic_client_ex(INVALID_SOCKET));
176*e7be843bSPierre Pronchery }
177*e7be843bSPierre Pronchery 
test_quic_client_connect_first(void)178*e7be843bSPierre Pronchery static int test_quic_client_connect_first(void)
179*e7be843bSPierre Pronchery {
180*e7be843bSPierre Pronchery     struct sockaddr_in sin = {0};
181*e7be843bSPierre Pronchery     int c_fd;
182*e7be843bSPierre Pronchery     int rv;
183*e7be843bSPierre Pronchery 
184*e7be843bSPierre Pronchery #ifdef SA_LEN
185*e7be843bSPierre Pronchery     sin.sin_len = sizeof(struct sockaddr_in);
186*e7be843bSPierre Pronchery #endif
187*e7be843bSPierre Pronchery     sin.sin_family = AF_INET;
188*e7be843bSPierre Pronchery     sin.sin_port = htons(DST_PORT);
189*e7be843bSPierre Pronchery     sin.sin_addr.s_addr = htonl(DST_ADDR);
190*e7be843bSPierre Pronchery 
191*e7be843bSPierre Pronchery     c_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
192*e7be843bSPierre Pronchery     if (!TEST_int_ne(c_fd, INVALID_SOCKET))
193*e7be843bSPierre Pronchery         goto err;
194*e7be843bSPierre Pronchery 
195*e7be843bSPierre Pronchery     if (!TEST_int_eq(connect(c_fd, (const struct sockaddr *)&sin, sizeof(sin)), 0))
196*e7be843bSPierre Pronchery         goto err;
197*e7be843bSPierre Pronchery 
198*e7be843bSPierre Pronchery     if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
199*e7be843bSPierre Pronchery         goto err;
200*e7be843bSPierre Pronchery 
201*e7be843bSPierre Pronchery     rv = test_quic_client_ex(c_fd);
202*e7be843bSPierre Pronchery 
203*e7be843bSPierre Pronchery     close(c_fd);
204*e7be843bSPierre Pronchery 
205*e7be843bSPierre Pronchery     return (rv);
206*e7be843bSPierre Pronchery 
207*e7be843bSPierre Pronchery err:
208*e7be843bSPierre Pronchery     if (c_fd != INVALID_SOCKET)
209*e7be843bSPierre Pronchery         close(c_fd);
210*e7be843bSPierre Pronchery     return (0);
211*e7be843bSPierre Pronchery }
212*e7be843bSPierre Pronchery 
213*e7be843bSPierre Pronchery OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n")
214*e7be843bSPierre Pronchery 
setup_tests(void)215*e7be843bSPierre Pronchery int setup_tests(void)
216*e7be843bSPierre Pronchery {
217*e7be843bSPierre Pronchery     if (!test_skip_common_options()) {
218*e7be843bSPierre Pronchery         TEST_error("Error parsing test options\n");
219*e7be843bSPierre Pronchery         return 0;
220*e7be843bSPierre Pronchery     }
221*e7be843bSPierre Pronchery 
222*e7be843bSPierre Pronchery     ADD_TEST(test_quic_client);
223*e7be843bSPierre Pronchery     ADD_TEST(test_quic_client_connect_first);
224*e7be843bSPierre Pronchery 
225*e7be843bSPierre Pronchery     return 1;
226*e7be843bSPierre Pronchery }
227