Home | History | Annotate | Line # | Download | only in test
      1 /*
      2  * Copyright 2022-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 #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;
     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     s_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
     89     if (!TEST_int_ge(s_fd, 0))
     90         goto err;
     91 
     92     if (!TEST_true(BIO_socket_nbio(s_fd, 1)))
     93         goto err;
     94 
     95     if (!TEST_ptr(s_addr_ = BIO_ADDR_new()))
     96         goto err;
     97 
     98     if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina), 0)))
     99         goto err;
    100 
    101     if (!TEST_true(BIO_bind(s_fd, s_addr_, 0)))
    102         goto err;
    103 
    104     s_info.addr = s_addr_;
    105     if (!TEST_true(BIO_sock_info(s_fd, BIO_SOCK_INFO_ADDRESS, &s_info)))
    106         goto err;
    107 
    108     if (!TEST_int_gt(BIO_ADDR_rawport(s_addr_), 0))
    109         goto err;
    110 
    111     if (!TEST_ptr(s_net_bio = s_net_bio_own = BIO_new_dgram(s_fd, 0)))
    112         goto err;
    113 
    114     if (!BIO_up_ref(s_net_bio))
    115         goto err;
    116 
    117     fake_time = ossl_ms2time(1000);
    118 
    119     tserver_args.net_rbio = s_net_bio;
    120     tserver_args.net_wbio = s_net_bio;
    121     tserver_args.alpn = NULL;
    122     tserver_args.ctx = NULL;
    123     if (use_fake_time)
    124         tserver_args.now_cb = fake_now;
    125 
    126     if (!TEST_ptr(tserver = ossl_quic_tserver_new(&tserver_args, certfile,
    127                       keyfile))) {
    128         BIO_free(s_net_bio);
    129         goto err;
    130     }
    131 
    132     s_net_bio_own = NULL;
    133 
    134     if (use_inject) {
    135         /*
    136          * In inject mode we create a dgram pair to feed to the QUIC client on
    137          * the read side. We don't feed anything to this, it is just a
    138          * placeholder to give the client something which never returns any
    139          * datagrams.
    140          */
    141         if (!TEST_true(BIO_new_bio_dgram_pair(&c_pair_own, 5000,
    142                 &s_pair_own, 5000)))
    143             goto err;
    144     }
    145 
    146     /* Setup test client. */
    147     c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
    148     if (!TEST_int_ge(c_fd, 0))
    149         goto err;
    150 
    151     if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
    152         goto err;
    153 
    154     if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0)))
    155         goto err;
    156 
    157     if (!BIO_dgram_set_peer(c_net_bio, s_addr_))
    158         goto err;
    159 
    160     if (!TEST_ptr(c_ctx = SSL_CTX_new(use_thread_assist
    161                           ? OSSL_QUIC_client_thread_method()
    162                           : OSSL_QUIC_client_method())))
    163         goto err;
    164 
    165     if (!TEST_ptr(c_ssl = SSL_new(c_ctx)))
    166         goto err;
    167 
    168     if (use_fake_time)
    169         if (!TEST_true(ossl_quic_set_override_now_cb(c_ssl, fake_now, NULL)))
    170             goto err;
    171 
    172     /* 0 is a success for SSL_set_alpn_protos() */
    173     if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn))))
    174         goto err;
    175 
    176     /* Takes ownership of our reference to the BIO. */
    177     if (use_inject) {
    178         SSL_set0_rbio(c_ssl, c_pair_own);
    179         c_pair_own = NULL;
    180     } else {
    181         SSL_set0_rbio(c_ssl, c_net_bio);
    182 
    183         /* Get another reference to be transferred in the SSL_set0_wbio call. */
    184         if (!TEST_true(BIO_up_ref(c_net_bio))) {
    185             c_net_bio_own = NULL; /* SSL_free will free the first reference. */
    186             goto err;
    187         }
    188     }
    189 
    190     SSL_set0_wbio(c_ssl, c_net_bio);
    191     c_net_bio_own = NULL;
    192 
    193     if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0)))
    194         goto err;
    195 
    196     /*
    197      * We use real time for the timeout not fake time. Otherwise with fake time
    198      * we could hit a hang if we never increment the fake time
    199      */
    200     start_time = real_now(NULL);
    201 
    202     for (;;) {
    203         if (ossl_time_compare(ossl_time_subtract(real_now(NULL), start_time),
    204                 ossl_ms2time(limit_ms))
    205             >= 0) {
    206             TEST_error("timeout while attempting QUIC server test");
    207             goto err;
    208         }
    209 
    210         if (!c_start_idle_test) {
    211             ret = SSL_connect(c_ssl);
    212             if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
    213                 goto err;
    214 
    215             if (ret == 1) {
    216                 c_connected = 1;
    217             } else {
    218                 /*
    219                  * keep timer ticking to keep handshake running.
    220                  * The timer is important for calculation of ping deadline.
    221                  * If things stall for whatever reason we at least send
    222                  * ACK eliciting ping to let peer know we are here ready
    223                  * to hear back.
    224                  */
    225                 if (!TEST_true(CRYPTO_THREAD_write_lock(fake_time_lock)))
    226                     goto err;
    227                 fake_time = ossl_time_add(fake_time, ossl_ms2time(100));
    228                 CRYPTO_THREAD_unlock(fake_time_lock);
    229             }
    230         }
    231 
    232         if (c_connected && !c_write_done) {
    233             if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1),
    234                     (int)sizeof(msg1) - 1))
    235                 goto err;
    236 
    237             if (!TEST_true(SSL_stream_conclude(c_ssl, 0)))
    238                 goto err;
    239 
    240             c_write_done = 1;
    241         }
    242 
    243         if (c_connected && c_write_done && !s_read_done) {
    244             if (!ossl_quic_tserver_read(tserver, 0,
    245                     (unsigned char *)msg2 + s_total_read,
    246                     sizeof(msg2) - s_total_read, &l)) {
    247                 if (!TEST_true(ossl_quic_tserver_has_read_ended(tserver, 0)))
    248                     goto err;
    249 
    250                 if (!TEST_mem_eq(msg1, sizeof(msg1) - 1, msg2, s_total_read))
    251                     goto err;
    252 
    253                 s_begin_write = 1;
    254                 s_read_done = 1;
    255             } else {
    256                 s_total_read += l;
    257                 if (!TEST_size_t_le(s_total_read, sizeof(msg1) - 1))
    258                     goto err;
    259             }
    260         }
    261 
    262         if (s_begin_write && s_total_written < sizeof(msg1) - 1) {
    263             if (!TEST_true(ossl_quic_tserver_write(tserver, 0,
    264                     (unsigned char *)msg2 + s_total_written,
    265                     sizeof(msg1) - 1 - s_total_written, &l)))
    266                 goto err;
    267 
    268             s_total_written += l;
    269 
    270             if (s_total_written == sizeof(msg1) - 1) {
    271                 ossl_quic_tserver_conclude(tserver, 0);
    272                 c_begin_read = 1;
    273             }
    274         }
    275 
    276         if (c_begin_read && c_total_read < sizeof(msg1) - 1) {
    277             ret = SSL_read_ex(c_ssl, msg3 + c_total_read,
    278                 sizeof(msg1) - 1 - c_total_read, &l);
    279             if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
    280                 goto err;
    281 
    282             c_total_read += l;
    283 
    284             if (c_total_read == sizeof(msg1) - 1) {
    285                 if (!TEST_mem_eq(msg1, sizeof(msg1) - 1,
    286                         msg3, c_total_read))
    287                     goto err;
    288 
    289                 c_wait_eos = 1;
    290             }
    291         }
    292 
    293         if (c_wait_eos && !c_done_eos) {
    294             unsigned char c;
    295 
    296             ret = SSL_read_ex(c_ssl, &c, sizeof(c), &l);
    297             if (!TEST_false(ret))
    298                 goto err;
    299 
    300             /*
    301              * Allow the implementation to take as long as it wants to finally
    302              * notice EOS. Account for varied timings in OS networking stacks.
    303              */
    304             if (SSL_get_error(c_ssl, ret) != SSL_ERROR_WANT_READ) {
    305                 if (!TEST_int_eq(SSL_get_error(c_ssl, ret),
    306                         SSL_ERROR_ZERO_RETURN))
    307                     goto err;
    308 
    309                 c_done_eos = 1;
    310                 if (use_thread_assist && use_fake_time) {
    311                     if (!TEST_true(ossl_quic_tserver_is_connected(tserver)))
    312                         goto err;
    313                     c_start_idle_test = 1;
    314                     limit_ms = 120000; /* extend time limit */
    315                 } else {
    316                     /* DONE */
    317                     break;
    318                 }
    319             }
    320         }
    321 
    322         if (c_start_idle_test && !c_done_idle_test) {
    323             /* This is more than our default idle timeout of 30s. */
    324             if (idle_units_done < 600) {
    325                 struct timeval tv;
    326                 int isinf;
    327 
    328                 if (!TEST_true(CRYPTO_THREAD_write_lock(fake_time_lock)))
    329                     goto err;
    330                 fake_time = ossl_time_add(fake_time, ossl_ms2time(100));
    331                 CRYPTO_THREAD_unlock(fake_time_lock);
    332 
    333                 ++idle_units_done;
    334                 ossl_quic_conn_force_assist_thread_wake(c_ssl);
    335 
    336                 /*
    337                  * If the event timeout has expired then give the assistance
    338                  * thread a chance to catch up
    339                  */
    340                 if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf)))
    341                     goto err;
    342                 if (!isinf && ossl_time_compare(ossl_time_zero(), ossl_time_from_timeval(tv)) >= 0)
    343                     OSSL_sleep(100); /* Ensure CPU scheduling for test purposes */
    344             } else {
    345                 c_done_idle_test = 1;
    346             }
    347         }
    348 
    349         if (c_done_idle_test) {
    350             /*
    351              * If we have finished the fake idling duration, the connection
    352              * should still be healthy in TA mode.
    353              */
    354             if (!TEST_true(ossl_quic_tserver_is_connected(tserver)))
    355                 goto err;
    356 
    357             /* DONE */
    358             break;
    359         }
    360 
    361         /*
    362          * This is inefficient because we spin until things work without
    363          * blocking but this is just a test.
    364          */
    365         if (!c_start_idle_test || c_done_idle_test) {
    366             /* Inhibit manual ticking during idle test to test TA mode. */
    367             SSL_handle_events(c_ssl);
    368         }
    369 
    370         ossl_quic_tserver_tick(tserver);
    371 
    372         if (use_inject) {
    373             BIO_MSG rmsg = { 0 };
    374             size_t msgs_processed = 0;
    375 
    376             for (;;) {
    377                 /*
    378                  * Manually spoonfeed received datagrams from the real BIO_dgram
    379                  * into QUIC via the injection interface, thereby testing the
    380                  * injection interface.
    381                  */
    382                 rmsg.data = scratch_buf;
    383                 rmsg.data_len = sizeof(scratch_buf);
    384 
    385                 if (!BIO_recvmmsg(c_net_bio, &rmsg, sizeof(rmsg), 1, 0, &msgs_processed)
    386                     || msgs_processed == 0 || rmsg.data_len == 0)
    387                     break;
    388 
    389                 if (!TEST_true(SSL_inject_net_dgram(c_ssl, rmsg.data, rmsg.data_len,
    390                         NULL, NULL)))
    391                     goto err;
    392             }
    393         }
    394     }
    395 
    396     testresult = 1;
    397 err:
    398     SSL_free(c_ssl);
    399     SSL_CTX_free(c_ctx);
    400     ossl_quic_tserver_free(tserver);
    401     BIO_ADDR_free(s_addr_);
    402     BIO_free(s_net_bio_own);
    403     BIO_free(c_net_bio_own);
    404     BIO_free(c_pair_own);
    405     BIO_free(s_pair_own);
    406     if (s_fd >= 0)
    407         BIO_closesocket(s_fd);
    408     if (c_fd >= 0)
    409         BIO_closesocket(c_fd);
    410     return testresult;
    411 }
    412 
    413 static int test_tserver(int idx)
    414 {
    415     int thread_assisted, use_fake_time, use_inject;
    416 
    417     thread_assisted = idx % 2;
    418     idx /= 2;
    419 
    420     use_inject = idx % 2;
    421     idx /= 2;
    422 
    423     use_fake_time = idx % 2;
    424 
    425     if (use_fake_time && !thread_assisted)
    426         return 1;
    427 
    428     return do_test(thread_assisted, use_fake_time, use_inject);
    429 }
    430 
    431 OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n")
    432 
    433 int setup_tests(void)
    434 {
    435     if (!test_skip_common_options()) {
    436         TEST_error("Error parsing test options\n");
    437         return 0;
    438     }
    439 
    440     if (!TEST_ptr(certfile = test_get_argument(0))
    441         || !TEST_ptr(keyfile = test_get_argument(1)))
    442         return 0;
    443 
    444     if ((fake_time_lock = CRYPTO_THREAD_lock_new()) == NULL)
    445         return 0;
    446 
    447     ADD_ALL_TESTS(test_tserver, 2 * 2 * 2);
    448     return 1;
    449 }
    450