/*
- * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2023 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
#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
static void packet_plain_finish(void *arg);
static void handshake_finish(void *arg);
-static BIO_METHOD *get_bio_method(void);
-
static OSSL_TIME fake_now;
static OSSL_TIME fake_now_cb(void *arg)
}
int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx,
- char *certfile, char *keyfile,
+ SSL_CTX *serverctx, char *certfile, char *keyfile,
int flags, QUIC_TSERVER **qtserv, SSL **cssl,
QTEST_FAULT **fault)
{
*qtserv = NULL;
if (fault != NULL)
*fault = NULL;
- *cssl = SSL_new(clientctx);
- if (!TEST_ptr(*cssl))
- return 0;
+
+ if (*cssl == NULL) {
+ *cssl = SSL_new(clientctx);
+ if (!TEST_ptr(*cssl))
+ return 0;
+ }
/* SSL_set_alpn_protos returns 0 for success! */
if (!TEST_false(SSL_set_alpn_protos(*cssl, alpn, sizeof(alpn))))
(flags & QTEST_FLAG_BLOCK) != 0 ? 1 : 0)))
goto err;
- if (!TEST_true(SSL_set_initial_peer_addr(*cssl, peeraddr)))
+ if (!TEST_true(SSL_set1_initial_peer_addr(*cssl, peeraddr)))
goto err;
if (fault != NULL) {
goto err;
}
- fisbio = BIO_new(get_bio_method());
+ fisbio = BIO_new(qtest_get_bio_method());
if (!TEST_ptr(fisbio))
goto err;
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 ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
fake_now = ossl_time_zero();
+ /* zero time can have a special meaning, bump it */
+ qtest_add_time(1);
tserver_args.now_cb = fake_now_cb;
+ (void)ossl_quic_conn_set_override_now_cb(*cssl, fake_now_cb, NULL);
}
if (!TEST_ptr(*qtserv = ossl_quic_tserver_new(&tserver_args, certfile,
return 1;
err:
+ SSL_CTX_free(tserver_args.ctx);
BIO_ADDR_free(peeraddr);
BIO_free(cbio);
BIO_free(fisbio);
fake_now = ossl_time_add(fake_now, ossl_ms2time(millis));
}
+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)
#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;
}
#endif
-int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
+int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl,
+ int wanterr)
{
- int retc = -1, rets = 0, err, abortctr = 0, ret = 0;
+ int retc = -1, rets = 0, abortctr = 0, ret = 0;
int clienterr = 0, servererr = 0;
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
/*
* t uninitialised
*/
thread_t t = thread_zero;
+
+ if (clientssl != NULL)
+ abortserverthread = 0;
#endif
if (!TEST_ptr(qtserv)) {
}
do {
- err = SSL_ERROR_WANT_WRITE;
- while (!clienterr && retc <= 0 && err == SSL_ERROR_WANT_WRITE) {
+ if (!clienterr && retc <= 0) {
+ int err;
+
retc = SSL_connect(clientssl);
- if (retc <= 0)
+ if (retc <= 0) {
err = SSL_get_error(clientssl, retc);
- }
- if (!clienterr && retc <= 0 && err != SSL_ERROR_WANT_READ) {
- TEST_info("SSL_connect() failed %d, %d", retc, err);
- TEST_openssl_errors();
- clienterr = 1;
+ 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;
+ }
+ }
+ }
}
/*
*/
if (!clienterr && retc <= 0)
SSL_handle_events(clientssl);
+
if (!servererr && rets <= 0) {
qtest_add_time(1);
ossl_quic_tserver_tick(qtserv);
TEST_info("No progress made");
goto err;
}
- } while ((retc <= 0 && !clienterr) || (rets <= 0 && !servererr));
+ } 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)
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 */
- while (SSL_shutdown(clientssl) != 1)
- ossl_quic_tserver_tick(qtserv);
+ for (;;) {
+ int rc = SSL_shutdown(clientssl);
- return 1;
+ 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)
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 qtest_check_server_transport_err(qtserv, QUIC_ERR_PROTOCOL_VIOLATION);
}
+int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv)
+{
+ return qtest_check_server_transport_err(qtserv, QUIC_ERR_FRAME_ENCODING_ERROR);
+}
+
void qtest_fault_free(QTEST_FAULT *fault)
{
if (fault == NULL)
* Prepend frame data into a packet. To be called from a packet_plain_listener
* callback
*/
-int qtest_fault_prepend_frame(QTEST_FAULT *fault, unsigned char *frame,
+int qtest_fault_prepend_frame(QTEST_FAULT *fault, const unsigned char *frame,
size_t frame_len)
{
unsigned char *buf;
do {
if (!ossl_quic_wire_decode_pkt_hdr(&pkt,
- 0 /* TODO(QUIC): Not sure how this should be set*/, 1,
+ /*
+ * 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;
goto out;
/*
- * TODO(QUIC): At the moment modifications to hdr by the callback
+ * 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
return BIO_ctrl(next, cmd, larg, parg);
}
-static BIO_METHOD *get_bio_method(void)
+BIO_METHOD *qtest_get_bio_method(void)
{
BIO_METHOD *tmp;