123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278 |
- /*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include <assert.h>
- #include <openssl/configuration.h>
- #include <openssl/bio.h>
- #include "internal/e_os.h" /* For struct timeval */
- #include "quictestlib.h"
- #include "ssltestlib.h"
- #include "../testutil.h"
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- # include "../threadstest.h"
- #endif
- #include "internal/quic_ssl.h"
- #include "internal/quic_wire_pkt.h"
- #include "internal/quic_record_tx.h"
- #include "internal/quic_error.h"
- #include "internal/packet.h"
- #include "internal/tsan_assist.h"
- #define GROWTH_ALLOWANCE 1024
- struct noise_args_data_st {
- BIO *cbio;
- BIO *sbio;
- BIO *tracebio;
- int flags;
- };
- struct qtest_fault {
- QUIC_TSERVER *qtserv;
- /* Plain packet mutations */
- /* Header for the plaintext packet */
- QUIC_PKT_HDR pplainhdr;
- /* iovec for the plaintext packet data buffer */
- OSSL_QTX_IOVEC pplainio;
- /* Allocated size of the plaintext packet data buffer */
- size_t pplainbuf_alloc;
- qtest_fault_on_packet_plain_cb pplaincb;
- void *pplaincbarg;
- /* Handshake message mutations */
- /* Handshake message buffer */
- unsigned char *handbuf;
- /* Allocated size of the handshake message buffer */
- size_t handbufalloc;
- /* Actual length of the handshake message */
- size_t handbuflen;
- qtest_fault_on_handshake_cb handshakecb;
- void *handshakecbarg;
- qtest_fault_on_enc_ext_cb encextcb;
- void *encextcbarg;
- /* Cipher packet mutations */
- qtest_fault_on_packet_cipher_cb pciphercb;
- void *pciphercbarg;
- /* Datagram mutations */
- qtest_fault_on_datagram_cb datagramcb;
- void *datagramcbarg;
- /* The currently processed message */
- BIO_MSG msg;
- /* Allocated size of msg data buffer */
- size_t msgalloc;
- struct noise_args_data_st noiseargs;
- };
- static void packet_plain_finish(void *arg);
- static void handshake_finish(void *arg);
- static OSSL_TIME qtest_get_time(void);
- static void qtest_reset_time(void);
- static int using_fake_time = 0;
- static OSSL_TIME fake_now;
- static CRYPTO_RWLOCK *fake_now_lock = NULL;
- static OSSL_TIME start_time;
- static OSSL_TIME fake_now_cb(void *arg)
- {
- return qtest_get_time();
- }
- static void noise_msg_callback(int write_p, int version, int content_type,
- const void *buf, size_t len, SSL *ssl,
- void *arg)
- {
- struct noise_args_data_st *noiseargs = (struct noise_args_data_st *)arg;
- if (content_type == SSL3_RT_QUIC_FRAME_FULL) {
- PACKET pkt;
- uint64_t frame_type;
- if (!PACKET_buf_init(&pkt, buf, len))
- return;
- if (!ossl_quic_wire_peek_frame_header(&pkt, &frame_type, NULL))
- return;
- if (frame_type == OSSL_QUIC_FRAME_TYPE_PING) {
- /*
- * If either endpoint issues a ping frame then we are in danger
- * of our noise being too much such that the connection itself
- * fails. We back off on the noise for a bit to avoid that.
- */
- (void)BIO_ctrl(noiseargs->cbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
- (void)BIO_ctrl(noiseargs->sbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
- }
- }
- #ifndef OPENSSL_NO_SSL_TRACE
- if ((noiseargs->flags & QTEST_FLAG_CLIENT_TRACE) != 0
- && !SSL_is_server(ssl))
- SSL_trace(write_p, version, content_type, buf, len, ssl,
- noiseargs->tracebio);
- #endif
- }
- int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx,
- SSL_CTX *serverctx, char *certfile, char *keyfile,
- int flags, QUIC_TSERVER **qtserv, SSL **cssl,
- QTEST_FAULT **fault, BIO **tracebio)
- {
- /* ALPN value as recognised by QUIC_TSERVER */
- unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
- QUIC_TSERVER_ARGS tserver_args = {0};
- BIO *cbio = NULL, *sbio = NULL, *fisbio = NULL;
- BIO_ADDR *peeraddr = NULL;
- struct in_addr ina = {0};
- BIO *tmpbio = NULL;
- *qtserv = NULL;
- if (fault != NULL) {
- *fault = OPENSSL_zalloc(sizeof(**fault));
- if (*fault == NULL)
- goto err;
- }
- if (*cssl == NULL) {
- *cssl = SSL_new(clientctx);
- if (!TEST_ptr(*cssl))
- goto err;
- }
- #ifndef OPENSSL_NO_SSL_TRACE
- if ((flags & QTEST_FLAG_CLIENT_TRACE) != 0) {
- tmpbio = BIO_new_fp(stdout, BIO_NOCLOSE);
- if (!TEST_ptr(tmpbio))
- goto err;
- SSL_set_msg_callback(*cssl, SSL_trace);
- SSL_set_msg_callback_arg(*cssl, tmpbio);
- }
- #endif
- if (tracebio != NULL)
- *tracebio = tmpbio;
- /* SSL_set_alpn_protos returns 0 for success! */
- if (!TEST_false(SSL_set_alpn_protos(*cssl, alpn, sizeof(alpn))))
- goto err;
- if (!TEST_ptr(peeraddr = BIO_ADDR_new()))
- goto err;
- if ((flags & QTEST_FLAG_BLOCK) != 0) {
- #if !defined(OPENSSL_NO_POSIX_IO)
- int cfd, sfd;
- /*
- * For blocking mode we need to create actual sockets rather than doing
- * everything in memory
- */
- if (!TEST_true(create_test_sockets(&cfd, &sfd, SOCK_DGRAM, peeraddr)))
- goto err;
- cbio = BIO_new_dgram(cfd, 1);
- if (!TEST_ptr(cbio)) {
- close(cfd);
- close(sfd);
- goto err;
- }
- sbio = BIO_new_dgram(sfd, 1);
- if (!TEST_ptr(sbio)) {
- close(sfd);
- goto err;
- }
- #else
- goto err;
- #endif
- } else {
- if (!TEST_true(BIO_new_bio_dgram_pair(&cbio, 0, &sbio, 0)))
- goto err;
- if (!TEST_true(BIO_dgram_set_caps(cbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR))
- || !TEST_true(BIO_dgram_set_caps(sbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR)))
- goto err;
- /* Dummy server address */
- if (!TEST_true(BIO_ADDR_rawmake(peeraddr, AF_INET, &ina, sizeof(ina),
- htons(0))))
- goto err;
- }
- if ((flags & QTEST_FLAG_PACKET_SPLIT) != 0) {
- BIO *pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
- if (!TEST_ptr(pktsplitbio))
- goto err;
- cbio = BIO_push(pktsplitbio, cbio);
- pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
- if (!TEST_ptr(pktsplitbio))
- goto err;
- sbio = BIO_push(pktsplitbio, sbio);
- }
- if ((flags & QTEST_FLAG_NOISE) != 0) {
- BIO *noisebio;
- struct bio_noise_now_cb_st now_cb = { fake_now_cb, NULL };
- /*
- * It is an error to not have a QTEST_FAULT object when introducing noise
- */
- if (!TEST_ptr(fault))
- goto err;
- noisebio = BIO_new(bio_f_noisy_dgram_filter());
- if (!TEST_ptr(noisebio))
- goto err;
- cbio = BIO_push(noisebio, cbio);
- if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
- if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_SET_NOW_CB,
- 0, &now_cb), 1))
- goto err;
- }
- noisebio = BIO_new(bio_f_noisy_dgram_filter());
- if (!TEST_ptr(noisebio))
- goto err;
- sbio = BIO_push(noisebio, sbio);
- if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
- if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_SET_NOW_CB,
- 0, &now_cb), 1))
- goto err;
- }
- /*
- * TODO(QUIC SERVER):
- * Currently the simplistic handler of the quic tserver cannot cope
- * with noise introduced in the first packet received from the
- * client. This needs to be removed once we have proper server side
- * handling.
- */
- (void)BIO_ctrl(sbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
- (*fault)->noiseargs.cbio = cbio;
- (*fault)->noiseargs.sbio = sbio;
- (*fault)->noiseargs.tracebio = tmpbio;
- (*fault)->noiseargs.flags = flags;
- SSL_set_msg_callback(*cssl, noise_msg_callback);
- SSL_set_msg_callback_arg(*cssl, &(*fault)->noiseargs);
- }
- SSL_set_bio(*cssl, cbio, cbio);
- if (!TEST_true(SSL_set_blocking_mode(*cssl,
- (flags & QTEST_FLAG_BLOCK) != 0 ? 1 : 0)))
- goto err;
- if (!TEST_true(SSL_set1_initial_peer_addr(*cssl, peeraddr)))
- goto err;
- fisbio = BIO_new(qtest_get_bio_method());
- if (!TEST_ptr(fisbio))
- goto err;
- BIO_set_data(fisbio, fault == NULL ? NULL : *fault);
- if (!BIO_up_ref(sbio))
- goto err;
- if (!TEST_ptr(BIO_push(fisbio, sbio))) {
- BIO_free(sbio);
- goto err;
- }
- tserver_args.libctx = libctx;
- tserver_args.net_rbio = sbio;
- tserver_args.net_wbio = fisbio;
- tserver_args.alpn = NULL;
- if (serverctx != NULL && !TEST_true(SSL_CTX_up_ref(serverctx)))
- goto err;
- tserver_args.ctx = serverctx;
- if (fake_now_lock == NULL) {
- fake_now_lock = CRYPTO_THREAD_lock_new();
- if (fake_now_lock == NULL)
- goto err;
- }
- if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
- using_fake_time = 1;
- qtest_reset_time();
- tserver_args.now_cb = fake_now_cb;
- (void)ossl_quic_conn_set_override_now_cb(*cssl, fake_now_cb, NULL);
- } else {
- using_fake_time = 0;
- }
- if (!TEST_ptr(*qtserv = ossl_quic_tserver_new(&tserver_args, certfile,
- keyfile)))
- goto err;
- /* Ownership of fisbio and sbio is now held by *qtserv */
- sbio = NULL;
- fisbio = NULL;
- if ((flags & QTEST_FLAG_NOISE) != 0)
- ossl_quic_tserver_set_msg_callback(*qtserv, noise_msg_callback,
- &(*fault)->noiseargs);
- if (fault != NULL)
- (*fault)->qtserv = *qtserv;
- BIO_ADDR_free(peeraddr);
- return 1;
- err:
- SSL_CTX_free(tserver_args.ctx);
- BIO_ADDR_free(peeraddr);
- BIO_free_all(cbio);
- BIO_free_all(fisbio);
- BIO_free_all(sbio);
- SSL_free(*cssl);
- *cssl = NULL;
- ossl_quic_tserver_free(*qtserv);
- if (fault != NULL)
- OPENSSL_free(*fault);
- BIO_free(tmpbio);
- if (tracebio != NULL)
- *tracebio = NULL;
- return 0;
- }
- void qtest_add_time(uint64_t millis)
- {
- if (!CRYPTO_THREAD_write_lock(fake_now_lock))
- return;
- fake_now = ossl_time_add(fake_now, ossl_ms2time(millis));
- CRYPTO_THREAD_unlock(fake_now_lock);
- }
- static OSSL_TIME qtest_get_time(void)
- {
- OSSL_TIME ret;
- if (!CRYPTO_THREAD_read_lock(fake_now_lock))
- return ossl_time_zero();
- ret = fake_now;
- CRYPTO_THREAD_unlock(fake_now_lock);
- return ret;
- }
- static void qtest_reset_time(void)
- {
- if (!CRYPTO_THREAD_write_lock(fake_now_lock))
- return;
- fake_now = ossl_time_zero();
- CRYPTO_THREAD_unlock(fake_now_lock);
- /* zero time can have a special meaning, bump it */
- qtest_add_time(1);
- }
- void qtest_start_stopwatch(void)
- {
- start_time = qtest_get_time();
- }
- uint64_t qtest_get_stopwatch_time(void)
- {
- return ossl_time2ms(ossl_time_subtract(qtest_get_time(), start_time));
- }
- QTEST_FAULT *qtest_create_injector(QUIC_TSERVER *ts)
- {
- QTEST_FAULT *f;
- f = OPENSSL_zalloc(sizeof(*f));
- if (f == NULL)
- return NULL;
- f->qtserv = ts;
- return f;
- }
- int qtest_supports_blocking(void)
- {
- #if !defined(OPENSSL_NO_POSIX_IO) && defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- return 1;
- #else
- return 0;
- #endif
- }
- #define MAXLOOPS 1000
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- static int globserverret = 0;
- static TSAN_QUALIFIER int abortserverthread = 0;
- static QUIC_TSERVER *globtserv;
- static const thread_t thread_zero;
- static void run_server_thread(void)
- {
- /*
- * This will operate in a busy loop because the server does not block,
- * but should be acceptable because it is local and we expect this to be
- * fast
- */
- globserverret = qtest_create_quic_connection(globtserv, NULL);
- }
- #endif
- int qtest_wait_for_timeout(SSL *s, QUIC_TSERVER *qtserv)
- {
- struct timeval tv;
- OSSL_TIME ctimeout, stimeout, mintimeout, now;
- int cinf;
- /* We don't need to wait in blocking mode */
- if (s == NULL || SSL_get_blocking_mode(s))
- return 1;
- /* Don't wait if either BIO has data waiting */
- if (BIO_pending(SSL_get_rbio(s)) > 0
- || BIO_pending(ossl_quic_tserver_get0_rbio(qtserv)) > 0)
- return 1;
- /*
- * Neither endpoint has data waiting to be read. We assume data transmission
- * is instantaneous due to using mem based BIOs, so there is no data "in
- * flight" and no more data will be sent by either endpoint until some time
- * based event has occurred. Therefore, wait for a timeout to occur. This
- * might happen if we are using the noisy BIO and datagrams have been lost.
- */
- if (!SSL_get_event_timeout(s, &tv, &cinf))
- return 0;
- if (using_fake_time)
- now = qtest_get_time();
- else
- now = ossl_time_now();
- ctimeout = cinf ? ossl_time_infinite() : ossl_time_from_timeval(tv);
- stimeout = ossl_time_subtract(ossl_quic_tserver_get_deadline(qtserv), now);
- mintimeout = ossl_time_min(ctimeout, stimeout);
- if (ossl_time_is_infinite(mintimeout))
- return 0;
- if (using_fake_time)
- qtest_add_time(ossl_time2ms(mintimeout));
- else
- OSSL_sleep(ossl_time2ms(mintimeout));
- return 1;
- }
- int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl,
- int wanterr)
- {
- int retc = -1, rets = 0, abortctr = 0, ret = 0;
- int clienterr = 0, servererr = 0;
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- /*
- * Pointless initialisation to avoid bogus compiler warnings about using
- * t uninitialised
- */
- thread_t t = thread_zero;
- if (clientssl != NULL)
- abortserverthread = 0;
- #endif
- if (!TEST_ptr(qtserv)) {
- goto err;
- } else if (clientssl == NULL) {
- retc = 1;
- } else if (SSL_get_blocking_mode(clientssl) > 0) {
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- /*
- * clientssl is blocking. We will need a thread to complete the
- * connection
- */
- globtserv = qtserv;
- if (!TEST_true(run_thread(&t, run_server_thread)))
- goto err;
- qtserv = NULL;
- rets = 1;
- #else
- TEST_error("No thread support in this build");
- goto err;
- #endif
- }
- do {
- if (!clienterr && retc <= 0) {
- int err;
- retc = SSL_connect(clientssl);
- if (retc <= 0) {
- err = SSL_get_error(clientssl, retc);
- if (err == wanterr) {
- retc = 1;
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- if (qtserv == NULL && rets > 0)
- tsan_store(&abortserverthread, 1);
- else
- #endif
- rets = 1;
- } else {
- if (err != SSL_ERROR_WANT_READ
- && err != SSL_ERROR_WANT_WRITE) {
- TEST_info("SSL_connect() failed %d, %d", retc, err);
- TEST_openssl_errors();
- clienterr = 1;
- }
- }
- }
- }
- qtest_add_time(1);
- if (clientssl != NULL)
- SSL_handle_events(clientssl);
- if (qtserv != NULL)
- ossl_quic_tserver_tick(qtserv);
- if (!servererr && rets <= 0) {
- servererr = ossl_quic_tserver_is_term_any(qtserv);
- if (!servererr)
- rets = ossl_quic_tserver_is_handshake_confirmed(qtserv);
- }
- if (clienterr && servererr)
- goto err;
- if (clientssl != NULL && ++abortctr == MAXLOOPS) {
- TEST_info("No progress made");
- goto err;
- }
- if ((retc <= 0 && !clienterr) || (rets <= 0 && !servererr)) {
- if (!qtest_wait_for_timeout(clientssl, qtserv))
- goto err;
- }
- } while ((retc <= 0 && !clienterr)
- || (rets <= 0 && !servererr
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- && !tsan_load(&abortserverthread)
- #endif
- ));
- if (qtserv == NULL && rets > 0) {
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- if (!TEST_true(wait_for_thread(t)) || !TEST_true(globserverret))
- goto err;
- #else
- TEST_error("Should not happen");
- goto err;
- #endif
- }
- if (!clienterr && !servererr)
- ret = 1;
- err:
- return ret;
- }
- int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
- {
- return qtest_create_quic_connection_ex(qtserv, clientssl, SSL_ERROR_NONE);
- }
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- static TSAN_QUALIFIER int shutdowndone;
- static void run_server_shutdown_thread(void)
- {
- /*
- * This will operate in a busy loop because the server does not block,
- * but should be acceptable because it is local and we expect this to be
- * fast
- */
- do {
- ossl_quic_tserver_tick(globtserv);
- } while(!tsan_load(&shutdowndone));
- }
- #endif
- int qtest_shutdown(QUIC_TSERVER *qtserv, SSL *clientssl)
- {
- int tickserver = 1;
- int ret = 0;
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- /*
- * Pointless initialisation to avoid bogus compiler warnings about using
- * t uninitialised
- */
- thread_t t = thread_zero;
- #endif
- if (SSL_get_blocking_mode(clientssl) > 0) {
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- /*
- * clientssl is blocking. We will need a thread to complete the
- * connection
- */
- globtserv = qtserv;
- shutdowndone = 0;
- if (!TEST_true(run_thread(&t, run_server_shutdown_thread)))
- return 0;
- tickserver = 0;
- #else
- TEST_error("No thread support in this build");
- return 0;
- #endif
- }
- /* Busy loop in non-blocking mode. It should be quick because its local */
- for (;;) {
- int rc = SSL_shutdown(clientssl);
- if (rc == 1) {
- ret = 1;
- break;
- }
- if (rc < 0)
- break;
- if (tickserver)
- ossl_quic_tserver_tick(qtserv);
- }
- #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
- tsan_store(&shutdowndone, 1);
- if (!tickserver) {
- if (!TEST_true(wait_for_thread(t)))
- ret = 0;
- }
- #endif
- return ret;
- }
- int qtest_check_server_transport_err(QUIC_TSERVER *qtserv, uint64_t code)
- {
- const QUIC_TERMINATE_CAUSE *cause;
- ossl_quic_tserver_tick(qtserv);
- /*
- * Check that the server has closed with the specified code from the client
- */
- if (!TEST_true(ossl_quic_tserver_is_term_any(qtserv)))
- return 0;
- cause = ossl_quic_tserver_get_terminate_cause(qtserv);
- if (!TEST_ptr(cause)
- || !TEST_true(cause->remote)
- || !TEST_false(cause->app)
- || !TEST_uint64_t_eq(cause->error_code, code))
- return 0;
- return 1;
- }
- int qtest_check_server_protocol_err(QUIC_TSERVER *qtserv)
- {
- return qtest_check_server_transport_err(qtserv, OSSL_QUIC_ERR_PROTOCOL_VIOLATION);
- }
- int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv)
- {
- return qtest_check_server_transport_err(qtserv, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR);
- }
- void qtest_fault_free(QTEST_FAULT *fault)
- {
- if (fault == NULL)
- return;
- packet_plain_finish(fault);
- handshake_finish(fault);
- OPENSSL_free(fault);
- }
- static int packet_plain_mutate(const QUIC_PKT_HDR *hdrin,
- const OSSL_QTX_IOVEC *iovecin, size_t numin,
- QUIC_PKT_HDR **hdrout,
- const OSSL_QTX_IOVEC **iovecout,
- size_t *numout,
- void *arg)
- {
- QTEST_FAULT *fault = arg;
- size_t i, bufsz = 0;
- unsigned char *cur;
- /* Coalesce our data into a single buffer */
- /* First calculate required buffer size */
- for (i = 0; i < numin; i++)
- bufsz += iovecin[i].buf_len;
- fault->pplainio.buf_len = bufsz;
- /* Add an allowance for possible growth */
- bufsz += GROWTH_ALLOWANCE;
- fault->pplainio.buf = cur = OPENSSL_malloc(bufsz);
- if (cur == NULL) {
- fault->pplainio.buf_len = 0;
- return 0;
- }
- fault->pplainbuf_alloc = bufsz;
- /* Copy in the data from the input buffers */
- for (i = 0; i < numin; i++) {
- memcpy(cur, iovecin[i].buf, iovecin[i].buf_len);
- cur += iovecin[i].buf_len;
- }
- fault->pplainhdr = *hdrin;
- /* Cast below is safe because we allocated the buffer */
- if (fault->pplaincb != NULL
- && !fault->pplaincb(fault, &fault->pplainhdr,
- (unsigned char *)fault->pplainio.buf,
- fault->pplainio.buf_len, fault->pplaincbarg))
- return 0;
- *hdrout = &fault->pplainhdr;
- *iovecout = &fault->pplainio;
- *numout = 1;
- return 1;
- }
- static void packet_plain_finish(void *arg)
- {
- QTEST_FAULT *fault = arg;
- /* Cast below is safe because we allocated the buffer */
- OPENSSL_free((unsigned char *)fault->pplainio.buf);
- fault->pplainio.buf_len = 0;
- fault->pplainbuf_alloc = 0;
- fault->pplainio.buf = NULL;
- }
- int qtest_fault_set_packet_plain_listener(QTEST_FAULT *fault,
- qtest_fault_on_packet_plain_cb pplaincb,
- void *pplaincbarg)
- {
- fault->pplaincb = pplaincb;
- fault->pplaincbarg = pplaincbarg;
- return ossl_quic_tserver_set_plain_packet_mutator(fault->qtserv,
- packet_plain_mutate,
- packet_plain_finish,
- fault);
- }
- /* To be called from a packet_plain_listener callback */
- int qtest_fault_resize_plain_packet(QTEST_FAULT *fault, size_t newlen)
- {
- unsigned char *buf;
- size_t oldlen = fault->pplainio.buf_len;
- /*
- * Alloc'd size should always be non-zero, so if this fails we've been
- * incorrectly called
- */
- if (fault->pplainbuf_alloc == 0)
- return 0;
- if (newlen > fault->pplainbuf_alloc) {
- /* This exceeds our growth allowance. Fail */
- return 0;
- }
- /* Cast below is safe because we allocated the buffer */
- buf = (unsigned char *)fault->pplainio.buf;
- if (newlen > oldlen) {
- /* Extend packet with 0 bytes */
- memset(buf + oldlen, 0, newlen - oldlen);
- } /* else we're truncating or staying the same */
- fault->pplainio.buf_len = newlen;
- fault->pplainhdr.len = newlen;
- return 1;
- }
- /*
- * Prepend frame data into a packet. To be called from a packet_plain_listener
- * callback
- */
- int qtest_fault_prepend_frame(QTEST_FAULT *fault, const unsigned char *frame,
- size_t frame_len)
- {
- unsigned char *buf;
- size_t old_len;
- /*
- * Alloc'd size should always be non-zero, so if this fails we've been
- * incorrectly called
- */
- if (fault->pplainbuf_alloc == 0)
- return 0;
- /* Cast below is safe because we allocated the buffer */
- buf = (unsigned char *)fault->pplainio.buf;
- old_len = fault->pplainio.buf_len;
- /* Extend the size of the packet by the size of the new frame */
- if (!TEST_true(qtest_fault_resize_plain_packet(fault,
- old_len + frame_len)))
- return 0;
- memmove(buf + frame_len, buf, old_len);
- memcpy(buf, frame, frame_len);
- return 1;
- }
- static int handshake_mutate(const unsigned char *msgin, size_t msginlen,
- unsigned char **msgout, size_t *msgoutlen,
- void *arg)
- {
- QTEST_FAULT *fault = arg;
- unsigned char *buf;
- unsigned long payloadlen;
- unsigned int msgtype;
- PACKET pkt;
- buf = OPENSSL_malloc(msginlen + GROWTH_ALLOWANCE);
- if (buf == NULL)
- return 0;
- fault->handbuf = buf;
- fault->handbuflen = msginlen;
- fault->handbufalloc = msginlen + GROWTH_ALLOWANCE;
- memcpy(buf, msgin, msginlen);
- if (!PACKET_buf_init(&pkt, buf, msginlen)
- || !PACKET_get_1(&pkt, &msgtype)
- || !PACKET_get_net_3(&pkt, &payloadlen)
- || PACKET_remaining(&pkt) != payloadlen)
- return 0;
- /* Parse specific message types */
- switch (msgtype) {
- case SSL3_MT_ENCRYPTED_EXTENSIONS:
- {
- QTEST_ENCRYPTED_EXTENSIONS ee;
- if (fault->encextcb == NULL)
- break;
- /*
- * The EncryptedExtensions message is very simple. It just has an
- * extensions block in it and nothing else.
- */
- ee.extensions = (unsigned char *)PACKET_data(&pkt);
- ee.extensionslen = payloadlen;
- if (!fault->encextcb(fault, &ee, payloadlen, fault->encextcbarg))
- return 0;
- }
- default:
- /* No specific handlers for these message types yet */
- break;
- }
- if (fault->handshakecb != NULL
- && !fault->handshakecb(fault, buf, fault->handbuflen,
- fault->handshakecbarg))
- return 0;
- *msgout = buf;
- *msgoutlen = fault->handbuflen;
- return 1;
- }
- static void handshake_finish(void *arg)
- {
- QTEST_FAULT *fault = arg;
- OPENSSL_free(fault->handbuf);
- fault->handbuf = NULL;
- }
- int qtest_fault_set_handshake_listener(QTEST_FAULT *fault,
- qtest_fault_on_handshake_cb handshakecb,
- void *handshakecbarg)
- {
- fault->handshakecb = handshakecb;
- fault->handshakecbarg = handshakecbarg;
- return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
- handshake_mutate,
- handshake_finish,
- fault);
- }
- int qtest_fault_set_hand_enc_ext_listener(QTEST_FAULT *fault,
- qtest_fault_on_enc_ext_cb encextcb,
- void *encextcbarg)
- {
- fault->encextcb = encextcb;
- fault->encextcbarg = encextcbarg;
- return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
- handshake_mutate,
- handshake_finish,
- fault);
- }
- /* To be called from a handshake_listener callback */
- int qtest_fault_resize_handshake(QTEST_FAULT *fault, size_t newlen)
- {
- unsigned char *buf;
- size_t oldlen = fault->handbuflen;
- /*
- * Alloc'd size should always be non-zero, so if this fails we've been
- * incorrectly called
- */
- if (fault->handbufalloc == 0)
- return 0;
- if (newlen > fault->handbufalloc) {
- /* This exceeds our growth allowance. Fail */
- return 0;
- }
- buf = (unsigned char *)fault->handbuf;
- if (newlen > oldlen) {
- /* Extend packet with 0 bytes */
- memset(buf + oldlen, 0, newlen - oldlen);
- } /* else we're truncating or staying the same */
- fault->handbuflen = newlen;
- return 1;
- }
- /* To be called from message specific listener callbacks */
- int qtest_fault_resize_message(QTEST_FAULT *fault, size_t newlen)
- {
- /* First resize the underlying message */
- if (!qtest_fault_resize_handshake(fault, newlen + SSL3_HM_HEADER_LENGTH))
- return 0;
- /* Fixup the handshake message header */
- fault->handbuf[1] = (unsigned char)((newlen >> 16) & 0xff);
- fault->handbuf[2] = (unsigned char)((newlen >> 8) & 0xff);
- fault->handbuf[3] = (unsigned char)((newlen ) & 0xff);
- return 1;
- }
- int qtest_fault_delete_extension(QTEST_FAULT *fault,
- unsigned int exttype, unsigned char *ext,
- size_t *extlen,
- BUF_MEM *old_ext)
- {
- PACKET pkt, sub, subext;
- WPACKET old_ext_wpkt;
- unsigned int type;
- const unsigned char *start, *end;
- size_t newlen, w;
- size_t msglen = fault->handbuflen;
- if (!PACKET_buf_init(&pkt, ext, *extlen))
- return 0;
- /* Extension block starts with 2 bytes for extension block length */
- if (!PACKET_as_length_prefixed_2(&pkt, &sub))
- return 0;
- do {
- start = PACKET_data(&sub);
- if (!PACKET_get_net_2(&sub, &type)
- || !PACKET_get_length_prefixed_2(&sub, &subext))
- return 0;
- } while (type != exttype);
- /* Found it */
- end = PACKET_data(&sub);
- if (old_ext != NULL) {
- if (!WPACKET_init(&old_ext_wpkt, old_ext))
- return 0;
- if (!WPACKET_memcpy(&old_ext_wpkt, PACKET_data(&subext),
- PACKET_remaining(&subext))
- || !WPACKET_get_total_written(&old_ext_wpkt, &w)) {
- WPACKET_cleanup(&old_ext_wpkt);
- return 0;
- }
- WPACKET_finish(&old_ext_wpkt);
- old_ext->length = w;
- }
- /*
- * If we're not the last extension we need to move the rest earlier. The
- * cast below is safe because we own the underlying buffer and we're no
- * longer making PACKET calls.
- */
- if (end < ext + *extlen)
- memmove((unsigned char *)start, end, end - start);
- /*
- * Calculate new extensions payload length =
- * Original length
- * - 2 extension block length bytes
- * - length of removed extension
- */
- newlen = *extlen - 2 - (end - start);
- /* Fixup the length bytes for the extension block */
- ext[0] = (unsigned char)((newlen >> 8) & 0xff);
- ext[1] = (unsigned char)((newlen ) & 0xff);
- /*
- * Length of the whole extension block is the new payload length plus the
- * 2 bytes for the length
- */
- *extlen = newlen + 2;
- /* We can now resize the message */
- if ((size_t)(end - start) + SSL3_HM_HEADER_LENGTH > msglen)
- return 0; /* Should not happen */
- msglen -= (end - start) + SSL3_HM_HEADER_LENGTH;
- if (!qtest_fault_resize_message(fault, msglen))
- return 0;
- return 1;
- }
- #define BIO_TYPE_CIPHER_PACKET_FILTER (0x80 | BIO_TYPE_FILTER)
- static BIO_METHOD *pcipherbiometh = NULL;
- # define BIO_MSG_N(array, stride, n) (*(BIO_MSG *)((char *)(array) + (n)*(stride)))
- static int pcipher_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
- size_t num_msg, uint64_t flags,
- size_t *num_processed)
- {
- QTEST_FAULT *fault;
- BIO *next = BIO_next(b);
- ossl_ssize_t ret = 0;
- size_t i = 0, tmpnump;
- QUIC_PKT_HDR hdr;
- PACKET pkt;
- unsigned char *tmpdata;
- if (next == NULL)
- return 0;
- fault = BIO_get_data(b);
- if (fault == NULL
- || (fault->pciphercb == NULL && fault->datagramcb == NULL))
- return BIO_sendmmsg(next, msg, stride, num_msg, flags, num_processed);
- if (num_msg == 0) {
- *num_processed = 0;
- return 1;
- }
- for (i = 0; i < num_msg; ++i) {
- fault->msg = BIO_MSG_N(msg, stride, i);
- /* Take a copy of the data so that callbacks can modify it */
- tmpdata = OPENSSL_malloc(fault->msg.data_len + GROWTH_ALLOWANCE);
- if (tmpdata == NULL)
- return 0;
- memcpy(tmpdata, fault->msg.data, fault->msg.data_len);
- fault->msg.data = tmpdata;
- fault->msgalloc = fault->msg.data_len + GROWTH_ALLOWANCE;
- if (fault->pciphercb != NULL) {
- if (!PACKET_buf_init(&pkt, fault->msg.data, fault->msg.data_len))
- return 0;
- do {
- if (!ossl_quic_wire_decode_pkt_hdr(&pkt,
- /*
- * TODO(QUIC SERVER):
- * Needs to be set to the actual short header CID length
- * when testing the server implementation.
- */
- 0,
- 1,
- 0, &hdr, NULL))
- goto out;
- /*
- * hdr.data is const - but its our buffer so casting away the
- * const is safe
- */
- if (!fault->pciphercb(fault, &hdr, (unsigned char *)hdr.data,
- hdr.len, fault->pciphercbarg))
- goto out;
- /*
- * At the moment modifications to hdr by the callback
- * are ignored. We might need to rewrite the QUIC header to
- * enable tests to change this. We also don't yet have a
- * mechanism for the callback to change the encrypted data
- * length. It's not clear if that's needed or not.
- */
- } while (PACKET_remaining(&pkt) > 0);
- }
- if (fault->datagramcb != NULL
- && !fault->datagramcb(fault, &fault->msg, stride,
- fault->datagramcbarg))
- goto out;
- if (!BIO_sendmmsg(next, &fault->msg, stride, 1, flags, &tmpnump)) {
- *num_processed = i;
- goto out;
- }
- OPENSSL_free(fault->msg.data);
- fault->msg.data = NULL;
- fault->msgalloc = 0;
- }
- *num_processed = i;
- out:
- ret = i > 0;
- OPENSSL_free(fault->msg.data);
- fault->msg.data = NULL;
- return ret;
- }
- static long pcipher_ctrl(BIO *b, int cmd, long larg, void *parg)
- {
- BIO *next = BIO_next(b);
- if (next == NULL)
- return -1;
- return BIO_ctrl(next, cmd, larg, parg);
- }
- BIO_METHOD *qtest_get_bio_method(void)
- {
- BIO_METHOD *tmp;
- if (pcipherbiometh != NULL)
- return pcipherbiometh;
- tmp = BIO_meth_new(BIO_TYPE_CIPHER_PACKET_FILTER, "Cipher Packet Filter");
- if (!TEST_ptr(tmp))
- return NULL;
- if (!TEST_true(BIO_meth_set_sendmmsg(tmp, pcipher_sendmmsg))
- || !TEST_true(BIO_meth_set_ctrl(tmp, pcipher_ctrl)))
- goto err;
- pcipherbiometh = tmp;
- tmp = NULL;
- err:
- BIO_meth_free(tmp);
- return pcipherbiometh;
- }
- int qtest_fault_set_packet_cipher_listener(QTEST_FAULT *fault,
- qtest_fault_on_packet_cipher_cb pciphercb,
- void *pciphercbarg)
- {
- fault->pciphercb = pciphercb;
- fault->pciphercbarg = pciphercbarg;
- return 1;
- }
- int qtest_fault_set_datagram_listener(QTEST_FAULT *fault,
- qtest_fault_on_datagram_cb datagramcb,
- void *datagramcbarg)
- {
- fault->datagramcb = datagramcb;
- fault->datagramcbarg = datagramcbarg;
- return 1;
- }
- /* To be called from a datagram_listener callback */
- int qtest_fault_resize_datagram(QTEST_FAULT *fault, size_t newlen)
- {
- if (newlen > fault->msgalloc)
- return 0;
- if (newlen > fault->msg.data_len)
- memset((unsigned char *)fault->msg.data + fault->msg.data_len, 0,
- newlen - fault->msg.data_len);
- fault->msg.data_len = newlen;
- return 1;
- }
- int qtest_fault_set_bw_limit(QTEST_FAULT *fault,
- size_t ctos_bw, size_t stoc_bw,
- int noise_rate)
- {
- BIO *sbio = fault->noiseargs.sbio;
- BIO *cbio = fault->noiseargs.cbio;
- if (!TEST_ptr(sbio) || !TEST_ptr(cbio))
- return 0;
- if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_RATE, noise_rate, NULL), 1))
- return 0;
- if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_RATE, noise_rate, NULL), 1))
- return 0;
- /* We set the bandwidth limit on the sending side */
- if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_SEND_BANDWIDTH,
- (long)ctos_bw, NULL), 1))
- return 0;
- if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_SEND_BANDWIDTH,
- (long)stoc_bw, NULL), 1))
- return 0;
- return 1;
- }
- int bio_msg_copy(BIO_MSG *dst, BIO_MSG *src)
- {
- /*
- * Note it is assumed that the originally allocated data sizes for dst and
- * src are the same
- */
- memcpy(dst->data, src->data, src->data_len);
- dst->data_len = src->data_len;
- dst->flags = src->flags;
- if (dst->local != NULL) {
- if (src->local != NULL) {
- if (!TEST_true(BIO_ADDR_copy(dst->local, src->local)))
- return 0;
- } else {
- BIO_ADDR_clear(dst->local);
- }
- }
- if (!TEST_true(BIO_ADDR_copy(dst->peer, src->peer)))
- return 0;
- return 1;
- }
|