xref: /freebsd/crypto/openssl/test/quic_tserver_test.c (revision dece5b05f577fc4c6b89af7ad48dfbdf7cc4945c)
1 /*
2  * Copyright 2022-2026 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 #include <openssl/ssl.h>
10 #include <openssl/quic.h>
11 #include <openssl/bio.h>
12 #include "internal/common.h"
13 #include "internal/sockets.h"
14 #include "internal/quic_tserver.h"
15 #include "internal/quic_thread_assist.h"
16 #include "internal/quic_ssl.h"
17 #include "internal/time.h"
18 #include "testutil.h"
19 
20 static const char msg1[] = "The quick brown fox jumped over the lazy dogs.";
21 static char msg2[1024], msg3[1024];
22 static OSSL_TIME fake_time;
23 static CRYPTO_RWLOCK *fake_time_lock;
24 
25 static const char *certfile, *keyfile;
26 
27 static int is_want(SSL *s, int ret)
28 {
29     int ec = SSL_get_error(s, ret);
30 
31     return ec == SSL_ERROR_WANT_READ || ec == SSL_ERROR_WANT_WRITE;
32 }
33 
34 static unsigned char scratch_buf[2048];
35 
36 static OSSL_TIME fake_now(void *arg)
37 {
38     OSSL_TIME t;
39 
40     if (!CRYPTO_THREAD_read_lock(fake_time_lock))
41         return ossl_time_zero();
42 
43     t = fake_time;
44 
45     CRYPTO_THREAD_unlock(fake_time_lock);
46     return t;
47 }
48 
49 static OSSL_TIME real_now(void *arg)
50 {
51     return ossl_time_now();
52 }
53 
54 static int do_test(int use_thread_assist, int use_fake_time, int use_inject)
55 {
56     int testresult = 0, ret;
57     int s_fd = -1, c_fd = -1;
58     BIO *s_net_bio = NULL, *s_net_bio_own = NULL;
59     BIO *c_net_bio = NULL, *c_net_bio_own = NULL;
60     BIO *c_pair_own = NULL, *s_pair_own = NULL;
61     QUIC_TSERVER_ARGS tserver_args = { 0 };
62     QUIC_TSERVER *tserver = NULL;
63     BIO_ADDR *s_addr_ = NULL, *c_addr_ = NULL;
64     struct in_addr ina = { 0 };
65     union BIO_sock_info_u s_info = { 0 };
66     SSL_CTX *c_ctx = NULL;
67     SSL *c_ssl = NULL;
68     int c_connected = 0, c_write_done = 0, c_begin_read = 0, s_read_done = 0;
69     int c_wait_eos = 0, c_done_eos = 0;
70     int c_start_idle_test = 0, c_done_idle_test = 0;
71     size_t l = 0, s_total_read = 0, s_total_written = 0, c_total_read = 0;
72     size_t idle_units_done = 0;
73     int s_begin_write = 0;
74     OSSL_TIME start_time;
75     unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
76     size_t limit_ms = 10000;
77 
78 #if defined(OPENSSL_NO_QUIC_THREAD_ASSIST)
79     if (use_thread_assist) {
80         TEST_skip("thread assisted mode not enabled");
81         return 1;
82     }
83 #endif
84 
85     ina.s_addr = htonl(0x7f000001UL);
86 
87     /* Setup test server. */
88     if (!TEST_ptr(s_addr_ = BIO_ADDR_new()))
89         goto err;
90 
91     if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina), 0)))
92         goto err;
93 
94     if (use_fake_time) {
95         /*
96          * Keep accelerated fake time independent of OS network scheduling.
97          * Other iterations retain real UDP socket coverage.
98          */
99         if (!TEST_true(BIO_new_bio_dgram_pair(&c_net_bio_own, 0,
100                 &s_net_bio_own, 0)))
101             goto err;
102 
103         c_net_bio = c_net_bio_own;
104         s_net_bio = s_net_bio_own;
105 
106         if (!TEST_true(BIO_dgram_set_caps(c_net_bio,
107                 BIO_DGRAM_CAP_HANDLES_DST_ADDR))
108             || !TEST_true(BIO_dgram_set_caps(s_net_bio,
109                 BIO_DGRAM_CAP_HANDLES_DST_ADDR)))
110             goto err;
111 
112         if (!TEST_ptr(c_addr_ = BIO_ADDR_new())
113             || !TEST_true(BIO_ADDR_rawmake(c_addr_, AF_INET, &ina,
114                 sizeof(ina), 0))
115             || !TEST_true(BIO_dgram_set0_local_addr(c_net_bio, c_addr_)))
116             goto err;
117         c_addr_ = NULL;
118     } else {
119         s_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
120         if (!TEST_int_ge(s_fd, 0))
121             goto err;
122 
123         if (!TEST_true(BIO_socket_nbio(s_fd, 1)))
124             goto err;
125 
126         if (!TEST_true(BIO_bind(s_fd, s_addr_, 0)))
127             goto err;
128 
129         s_info.addr = s_addr_;
130         if (!TEST_true(BIO_sock_info(s_fd, BIO_SOCK_INFO_ADDRESS, &s_info)))
131             goto err;
132 
133         if (!TEST_int_gt(BIO_ADDR_rawport(s_addr_), 0))
134             goto err;
135 
136         if (!TEST_ptr(s_net_bio = s_net_bio_own = BIO_new_dgram(s_fd, 0)))
137             goto err;
138     }
139 
140     if (!BIO_up_ref(s_net_bio))
141         goto err;
142 
143     fake_time = ossl_ms2time(1000);
144 
145     tserver_args.net_rbio = s_net_bio;
146     tserver_args.net_wbio = s_net_bio;
147     tserver_args.alpn = NULL;
148     tserver_args.ctx = NULL;
149     if (use_fake_time)
150         tserver_args.now_cb = fake_now;
151 
152     if (!TEST_ptr(tserver = ossl_quic_tserver_new(&tserver_args, certfile,
153                       keyfile))) {
154         BIO_free(s_net_bio);
155         goto err;
156     }
157 
158     s_net_bio_own = NULL;
159 
160     if (use_inject) {
161         /*
162          * In inject mode we create a dgram pair to feed to the QUIC client on
163          * the read side. We don't feed anything to this, it is just a
164          * placeholder to give the client something which never returns any
165          * datagrams.
166          */
167         if (!TEST_true(BIO_new_bio_dgram_pair(&c_pair_own, 5000,
168                 &s_pair_own, 5000)))
169             goto err;
170     }
171 
172     /* Setup test client. */
173     if (!use_fake_time) {
174         c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
175         if (!TEST_int_ge(c_fd, 0))
176             goto err;
177 
178         if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
179             goto err;
180 
181         if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0)))
182             goto err;
183 
184         if (!BIO_dgram_set_peer(c_net_bio, s_addr_))
185             goto err;
186     }
187 
188     if (!TEST_ptr(c_ctx = SSL_CTX_new(use_thread_assist
189                           ? OSSL_QUIC_client_thread_method()
190                           : OSSL_QUIC_client_method())))
191         goto err;
192 
193     if (!TEST_ptr(c_ssl = SSL_new(c_ctx)))
194         goto err;
195 
196     if (use_fake_time) {
197         if (!TEST_true(SSL_set1_initial_peer_addr(c_ssl, s_addr_)))
198             goto err;
199         if (!TEST_true(ossl_quic_set_override_now_cb(c_ssl, fake_now, NULL)))
200             goto err;
201     }
202 
203     /* 0 is a success for SSL_set_alpn_protos() */
204     if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn))))
205         goto err;
206 
207     /* Takes ownership of our reference to the BIO. */
208     if (use_inject) {
209         SSL_set0_rbio(c_ssl, c_pair_own);
210         c_pair_own = NULL;
211     } else {
212         SSL_set0_rbio(c_ssl, c_net_bio);
213 
214         /* Get another reference to be transferred in the SSL_set0_wbio call. */
215         if (!TEST_true(BIO_up_ref(c_net_bio))) {
216             c_net_bio_own = NULL; /* SSL_free will free the first reference. */
217             goto err;
218         }
219     }
220 
221     SSL_set0_wbio(c_ssl, c_net_bio);
222     c_net_bio_own = NULL;
223 
224     if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0)))
225         goto err;
226 
227     /*
228      * We use real time for the timeout not fake time. Otherwise with fake time
229      * we could hit a hang if we never increment the fake time
230      */
231     start_time = real_now(NULL);
232 
233     for (;;) {
234         if (ossl_time_compare(ossl_time_subtract(real_now(NULL), start_time),
235                 ossl_ms2time(limit_ms))
236             >= 0) {
237             TEST_error("timeout while attempting QUIC server test");
238             goto err;
239         }
240 
241         if (!c_start_idle_test) {
242             ret = SSL_connect(c_ssl);
243             if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
244                 goto err;
245 
246             if (ret == 1) {
247                 c_connected = 1;
248             } else {
249                 /*
250                  * keep timer ticking to keep handshake running.
251                  * The timer is important for calculation of ping deadline.
252                  * If things stall for whatever reason we at least send
253                  * ACK eliciting ping to let peer know we are here ready
254                  * to hear back.
255                  */
256                 if (!TEST_true(CRYPTO_THREAD_write_lock(fake_time_lock)))
257                     goto err;
258                 fake_time = ossl_time_add(fake_time, ossl_ms2time(100));
259                 CRYPTO_THREAD_unlock(fake_time_lock);
260             }
261         }
262 
263         if (c_connected && !c_write_done) {
264             if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1),
265                     (int)sizeof(msg1) - 1))
266                 goto err;
267 
268             if (!TEST_true(SSL_stream_conclude(c_ssl, 0)))
269                 goto err;
270 
271             c_write_done = 1;
272         }
273 
274         if (c_connected && c_write_done && !s_read_done) {
275             if (!ossl_quic_tserver_read(tserver, 0,
276                     (unsigned char *)msg2 + s_total_read,
277                     sizeof(msg2) - s_total_read, &l)) {
278                 if (!TEST_true(ossl_quic_tserver_has_read_ended(tserver, 0)))
279                     goto err;
280 
281                 if (!TEST_mem_eq(msg1, sizeof(msg1) - 1, msg2, s_total_read))
282                     goto err;
283 
284                 s_begin_write = 1;
285                 s_read_done = 1;
286             } else {
287                 s_total_read += l;
288                 if (!TEST_size_t_le(s_total_read, sizeof(msg1) - 1))
289                     goto err;
290             }
291         }
292 
293         if (s_begin_write && s_total_written < sizeof(msg1) - 1) {
294             if (!TEST_true(ossl_quic_tserver_write(tserver, 0,
295                     (unsigned char *)msg2 + s_total_written,
296                     sizeof(msg1) - 1 - s_total_written, &l)))
297                 goto err;
298 
299             s_total_written += l;
300 
301             if (s_total_written == sizeof(msg1) - 1) {
302                 ossl_quic_tserver_conclude(tserver, 0);
303                 c_begin_read = 1;
304             }
305         }
306 
307         if (c_begin_read && c_total_read < sizeof(msg1) - 1) {
308             ret = SSL_read_ex(c_ssl, msg3 + c_total_read,
309                 sizeof(msg1) - 1 - c_total_read, &l);
310             if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
311                 goto err;
312 
313             c_total_read += l;
314 
315             if (c_total_read == sizeof(msg1) - 1) {
316                 if (!TEST_mem_eq(msg1, sizeof(msg1) - 1,
317                         msg3, c_total_read))
318                     goto err;
319 
320                 c_wait_eos = 1;
321             }
322         }
323 
324         if (c_wait_eos && !c_done_eos) {
325             unsigned char c;
326 
327             ret = SSL_read_ex(c_ssl, &c, sizeof(c), &l);
328             if (!TEST_false(ret))
329                 goto err;
330 
331             /*
332              * Allow the implementation to take as long as it wants to finally
333              * notice EOS. Account for varied timings in OS networking stacks.
334              */
335             if (SSL_get_error(c_ssl, ret) != SSL_ERROR_WANT_READ) {
336                 if (!TEST_int_eq(SSL_get_error(c_ssl, ret),
337                         SSL_ERROR_ZERO_RETURN))
338                     goto err;
339 
340                 c_done_eos = 1;
341                 if (use_thread_assist && use_fake_time) {
342                     if (!TEST_true(ossl_quic_tserver_is_connected(tserver)))
343                         goto err;
344                     c_start_idle_test = 1;
345                     limit_ms = 120000; /* extend time limit */
346                 } else {
347                     /* DONE */
348                     break;
349                 }
350             }
351         }
352 
353         if (c_start_idle_test && !c_done_idle_test) {
354             /* This is more than our default idle timeout of 30s. */
355             if (idle_units_done < 600) {
356                 struct timeval tv;
357                 int isinf;
358 
359                 if (!TEST_true(CRYPTO_THREAD_write_lock(fake_time_lock)))
360                     goto err;
361                 fake_time = ossl_time_add(fake_time, ossl_ms2time(100));
362                 CRYPTO_THREAD_unlock(fake_time_lock);
363 
364                 ++idle_units_done;
365 
366                 /*
367                  * The assist thread alone keeps the idle connection alive. It
368                  * waits on real time internally, so advancing fake time can
369                  * outrun it. Rather than race it, wait until it has caught up:
370                  * the event timeout is computed against fake time, so once the
371                  * next deadline is back in the future all events due up to now
372                  * - including any keepalive - have been serviced.
373                  */
374                 for (;;) {
375                     ossl_quic_conn_force_assist_thread_wake(c_ssl);
376 
377                     if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf)))
378                         goto err;
379 
380                     if (isinf
381                         || ossl_time_compare(ossl_time_from_timeval(tv),
382                                ossl_time_zero())
383                             > 0)
384                         break;
385 
386                     if (ossl_time_compare(ossl_time_subtract(real_now(NULL),
387                                               start_time),
388                             ossl_ms2time(limit_ms))
389                         >= 0) {
390                         TEST_error("timeout waiting for assist thread to send "
391                                    "keepalive during idle test");
392                         goto err;
393                     }
394 
395                     OSSL_sleep(1); /* Yield so the assist thread can run. */
396                 }
397             } else {
398                 c_done_idle_test = 1;
399             }
400         }
401 
402         if (c_done_idle_test) {
403             /*
404              * If we have finished the fake idling duration, the connection
405              * should still be healthy in TA mode.
406              */
407             if (!TEST_true(ossl_quic_tserver_is_connected(tserver)))
408                 goto err;
409 
410             /* DONE */
411             break;
412         }
413 
414         /*
415          * This is inefficient because we spin until things work without
416          * blocking but this is just a test.
417          */
418         if (!c_start_idle_test || c_done_idle_test) {
419             /* Inhibit manual ticking during idle test to test TA mode. */
420             SSL_handle_events(c_ssl);
421         }
422 
423         ossl_quic_tserver_tick(tserver);
424 
425         if (use_inject) {
426             BIO_MSG rmsg = { 0 };
427             size_t msgs_processed = 0;
428 
429             for (;;) {
430                 /*
431                  * Manually spoonfeed received datagrams from the network BIO
432                  * into QUIC via the injection interface, thereby testing the
433                  * injection interface.
434                  */
435                 rmsg.data = scratch_buf;
436                 rmsg.data_len = sizeof(scratch_buf);
437 
438                 if (!BIO_recvmmsg(c_net_bio, &rmsg, sizeof(rmsg), 1, 0, &msgs_processed)
439                     || msgs_processed == 0 || rmsg.data_len == 0)
440                     break;
441 
442                 if (!TEST_true(SSL_inject_net_dgram(c_ssl, rmsg.data, rmsg.data_len,
443                         NULL, NULL)))
444                     goto err;
445             }
446         }
447     }
448 
449     testresult = 1;
450 err:
451     SSL_free(c_ssl);
452     SSL_CTX_free(c_ctx);
453     ossl_quic_tserver_free(tserver);
454     BIO_ADDR_free(s_addr_);
455     BIO_ADDR_free(c_addr_);
456     BIO_free(s_net_bio_own);
457     BIO_free(c_net_bio_own);
458     BIO_free(c_pair_own);
459     BIO_free(s_pair_own);
460     if (s_fd >= 0)
461         BIO_closesocket(s_fd);
462     if (c_fd >= 0)
463         BIO_closesocket(c_fd);
464     return testresult;
465 }
466 
467 static int test_tserver(int idx)
468 {
469     int thread_assisted, use_fake_time, use_inject;
470 
471     thread_assisted = idx % 2;
472     idx /= 2;
473 
474     use_inject = idx % 2;
475     idx /= 2;
476 
477     use_fake_time = idx % 2;
478 
479     if (use_fake_time && !thread_assisted)
480         return 1;
481 
482     return do_test(thread_assisted, use_fake_time, use_inject);
483 }
484 
485 OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n")
486 
487 int setup_tests(void)
488 {
489     if (!test_skip_common_options()) {
490         TEST_error("Error parsing test options\n");
491         return 0;
492     }
493 
494     if (!TEST_ptr(certfile = test_get_argument(0))
495         || !TEST_ptr(keyfile = test_get_argument(1)))
496         return 0;
497 
498     if ((fake_time_lock = CRYPTO_THREAD_lock_new()) == NULL)
499         return 0;
500 
501     ADD_ALL_TESTS(test_tserver, 2 * 2 * 2);
502     return 1;
503 }
504