summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2022-11-30 14:21:00 +0000
committerHugo Landau <hlandau@openssl.org>2023-02-22 05:33:24 +0000
commitadef87a2c6a0136aa3d965162932f961daf28411 (patch)
tree66224540f4ef06d9ae7089b243af5cedfb7e993c
parent14e314093943ffd89633746179c2c8f0b5c631a4 (diff)
Add a skeleton quicfaultstest
Also includes helper support to create a QUIC connection inside a test. We wil use quicfaultstest to deliberately inject faulty datagrams/packets to test how we handle them. Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20030)
-rw-r--r--include/internal/quic_tserver.h3
-rw-r--r--ssl/quic/quic_tserver.c6
-rw-r--r--test/build.info6
-rw-r--r--test/helpers/quictestlib.c149
-rw-r--r--test/helpers/quictestlib.h18
-rw-r--r--test/quicfaultstest.c103
-rw-r--r--test/recipes/90-test_quicfaults.t26
7 files changed, 310 insertions, 1 deletions
diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h
index a19ec882ef..0d564d8c21 100644
--- a/include/internal/quic_tserver.h
+++ b/include/internal/quic_tserver.h
@@ -53,6 +53,9 @@ int ossl_quic_tserver_tick(QUIC_TSERVER *srv);
/* Returns 1 if we have a (non-terminated) client. */
int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv);
+/* Returns 1 if the server is in any terminating or terminated state */
+int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv);
+
/*
* Attempts to read from stream 0. Writes the number of bytes read to
* *bytes_read and returns 1 on success. If no bytes are available, 0 is written
diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c
index 2b5f04ac5a..ea131ca9c1 100644
--- a/ssl/quic/quic_tserver.c
+++ b/ssl/quic/quic_tserver.c
@@ -147,6 +147,12 @@ int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv)
return ossl_quic_channel_is_active(srv->ch);
}
+/* Returns 1 if the server is in any terminating or terminated state */
+int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv)
+{
+ return ossl_quic_channel_is_term_any(srv->ch);
+}
+
int ossl_quic_tserver_read(QUIC_TSERVER *srv,
unsigned char *buf,
size_t buf_len,
diff --git a/test/build.info b/test/build.info
index 3b8d17a325..15c0985150 100644
--- a/test/build.info
+++ b/test/build.info
@@ -71,7 +71,7 @@ IF[{- !$disabled{tests} -}]
ENDIF
IF[{- !$disabled{quic} -}]
- PROGRAMS{noinst}=priority_queue_test event_queue_test
+ PROGRAMS{noinst}=priority_queue_test event_queue_test quicfaultstest
ENDIF
IF[{- !$disabled{comp} && (!$disabled{brotli} || !$disabled{zstd} || !$disabled{zlib}) -}]
@@ -798,6 +798,10 @@ IF[{- !$disabled{tests} -}]
SOURCE[event_queue_test]=event_queue_test.c
INCLUDE[event_queue_test]=../include ../apps/include
DEPEND[event_queue_test]=../libcrypto ../libssl.a libtestutil.a
+
+ SOURCE[quicfaultstest]=quicfaultstest.c helpers/quictestlib.c
+ INCLUDE[quicfaultstest]=../include ../apps/include ..
+ DEPEND[quicfaultstest]=../libcrypto.a ../libssl.a libtestutil.a
ENDIF
SOURCE[dhtest]=dhtest.c
diff --git a/test/helpers/quictestlib.c b/test/helpers/quictestlib.c
new file mode 100644
index 0000000000..1c4fa8d9b3
--- /dev/null
+++ b/test/helpers/quictestlib.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2022 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 "quictestlib.h"
+#include "../testutil.h"
+
+struct ossl_quic_fault {
+ QUIC_TSERVER *qtserv;
+};
+
+int qtest_create_quic_objects(SSL_CTX *clientctx, char *certfile, char *keyfile,
+ QUIC_TSERVER **qtserv, SSL **cssl,
+ OSSL_QUIC_FAULT **fault)
+{
+ /* 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 *bio1 = NULL, *bio2 = NULL;
+ BIO_ADDR *peeraddr = NULL;
+ struct in_addr ina = {0};
+
+ *qtserv = NULL;
+ if (fault != NULL)
+ *fault = NULL;
+ *cssl = SSL_new(clientctx);
+ if (!TEST_ptr(*cssl))
+ return 0;
+
+ if (!TEST_true(SSL_set_blocking_mode(*cssl, 0)))
+ goto err;
+
+ /* SSL_set_alpn_protos returns 0 for success! */
+ if (!TEST_false(SSL_set_alpn_protos(*cssl, alpn, sizeof(alpn))))
+ goto err;
+
+ if (!TEST_true(BIO_new_bio_dgram_pair(&bio1, 0, &bio2, 0)))
+ goto err;
+
+ if (!TEST_true(BIO_dgram_set_caps(bio1, BIO_DGRAM_CAP_HANDLES_DST_ADDR))
+ || !TEST_true(BIO_dgram_set_caps(bio2, BIO_DGRAM_CAP_HANDLES_DST_ADDR)))
+ goto err;
+
+ SSL_set_bio(*cssl, bio1, bio1);
+
+ if (!TEST_ptr(peeraddr = BIO_ADDR_new()))
+ goto err;
+
+ /* Dummy server address */
+ if (!TEST_true(BIO_ADDR_rawmake(peeraddr, AF_INET, &ina, sizeof(ina),
+ htons(0))))
+ goto err;
+
+ if (!TEST_true(SSL_set_initial_peer_addr(*cssl, peeraddr)))
+ goto err;
+
+ /* 2 refs are passed for bio2 */
+ if (!BIO_up_ref(bio2))
+ goto err;
+ tserver_args.net_rbio = bio2;
+ tserver_args.net_wbio = bio2;
+
+ if (!TEST_ptr(*qtserv = ossl_quic_tserver_new(&tserver_args, certfile,
+ keyfile))) {
+ /* We hold 2 refs to bio2 at the moment */
+ BIO_free(bio2);
+ goto err;
+ }
+ /* Ownership of bio2 is now held by *qtserv */
+ bio2 = NULL;
+
+ if (fault != NULL) {
+ *fault = OPENSSL_zalloc(sizeof(**fault));
+ if (*fault == NULL)
+ goto err;
+
+ (*fault)->qtserv = *qtserv;
+ }
+
+ BIO_ADDR_free(peeraddr);
+
+ return 1;
+ err:
+ BIO_ADDR_free(peeraddr);
+ BIO_free(bio1);
+ BIO_free(bio2);
+ SSL_free(*cssl);
+ ossl_quic_tserver_free(*qtserv);
+ if (fault != NULL)
+ OPENSSL_free(*fault);
+
+ return 0;
+}
+
+#define MAXLOOPS 1000
+
+int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
+{
+ int retc = -1, rets = 0, err, abortctr = 0, ret = 0;
+ int clienterr = 0, servererr = 0;
+
+ do {
+ err = SSL_ERROR_WANT_WRITE;
+ while (!clienterr && retc <= 0 && err == SSL_ERROR_WANT_WRITE) {
+ retc = SSL_connect(clientssl);
+ 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;
+ }
+
+ /*
+ * We're cheating. We don't take any notice of SSL_get_tick_timeout()
+ * and tick everytime around the loop anyway. This is inefficient. We
+ * can get away with it in test code because we control both ends of
+ * the communications and don't expect network delays. This shouldn't
+ * be done in a real application.
+ */
+ if (!clienterr)
+ SSL_tick(clientssl);
+ if (!servererr) {
+ ossl_quic_tserver_tick(qtserv);
+ servererr = ossl_quic_tserver_is_term_any(qtserv);
+ if (!servererr && !rets)
+ rets = ossl_quic_tserver_is_connected(qtserv);
+ }
+
+ if (clienterr && servererr)
+ goto err;
+
+ if (++abortctr == MAXLOOPS) {
+ TEST_info("No progress made");
+ goto err;
+ }
+ } while (retc <=0 || rets <= 0);
+
+ ret = 1;
+ err:
+ return ret;
+}
diff --git a/test/helpers/quictestlib.h b/test/helpers/quictestlib.h
new file mode 100644
index 0000000000..3afea60e55
--- /dev/null
+++ b/test/helpers/quictestlib.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2022 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 <openssl/ssl.h>
+#include <internal/quic_tserver.h>
+
+typedef struct ossl_quic_fault OSSL_QUIC_FAULT;
+
+int qtest_create_quic_objects(SSL_CTX *clientctx, char *certfile, char *keyfile,
+ QUIC_TSERVER **qtserv, SSL **cssl,
+ OSSL_QUIC_FAULT **fault);
+int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl);
diff --git a/test/quicfaultstest.c b/test/quicfaultstest.c
new file mode 100644
index 0000000000..30d7caf9ac
--- /dev/null
+++ b/test/quicfaultstest.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 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 <string.h>
+#include <openssl/ssl.h>
+#include "helpers/quictestlib.h"
+#include "testutil.h"
+
+static char *cert = NULL;
+static char *privkey = NULL;
+
+/*
+ * Basic test that just creates a connection and sends some data without any
+ * faults injected.
+ */
+static int test_basic(void)
+{
+ int testresult = 0;
+ SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
+ QUIC_TSERVER *qtserv = NULL;
+ SSL *cssl = NULL;
+ char *msg = "Hello World!";
+ size_t msglen = strlen(msg);
+ unsigned char buf[80];
+ size_t bytesread;
+
+ if (!TEST_ptr(cctx))
+ goto err;
+
+ if (!TEST_true(qtest_create_quic_objects(cctx, cert, privkey, &qtserv,
+ &cssl, NULL)))
+ goto err;
+
+ if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
+ goto err;
+
+ if (!TEST_int_eq(SSL_write(cssl, msg, msglen), msglen))
+ goto err;
+
+ ossl_quic_tserver_tick(qtserv);
+ if (!TEST_true(ossl_quic_tserver_read(qtserv, buf, sizeof(buf), &bytesread)))
+ goto err;
+
+ /*
+ * We assume the entire message is read from the server in one go. In
+ * theory this could get fragmented but its a small message so we assume
+ * not.
+ */
+ if (!TEST_mem_eq(msg, msglen, buf, bytesread))
+ goto err;
+
+ testresult = 1;
+ err:
+ SSL_free(cssl);
+ ossl_quic_tserver_free(qtserv);
+ SSL_CTX_free(cctx);
+ return testresult;
+}
+
+OPT_TEST_DECLARE_USAGE("certsdir\n")
+
+int setup_tests(void)
+{
+ char *certsdir = NULL;
+
+ if (!test_skip_common_options()) {
+ TEST_error("Error parsing test options\n");
+ return 0;
+ }
+
+ if (!TEST_ptr(certsdir = test_get_argument(0)))
+ return 0;
+
+
+ cert = test_mk_file_path(certsdir, "servercert.pem");
+ if (cert == NULL)
+ goto err;
+
+ privkey = test_mk_file_path(certsdir, "serverkey.pem");
+ if (privkey == NULL)
+ goto err;
+
+ ADD_TEST(test_basic);
+
+ return 1;
+
+ err:
+ OPENSSL_free(cert);
+ OPENSSL_free(privkey);
+ return 0;
+}
+
+void cleanup_tests(void)
+{
+ OPENSSL_free(cert);
+ OPENSSL_free(privkey);
+}
diff --git a/test/recipes/90-test_quicfaults.t b/test/recipes/90-test_quicfaults.t
new file mode 100644
index 0000000000..f4bd8ea9b7
--- /dev/null
+++ b/test/recipes/90-test_quicfaults.t
@@ -0,0 +1,26 @@
+#! /usr/bin/env perl
+# Copyright 2022 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
+
+
+use OpenSSL::Test::Utils;
+use OpenSSL::Test qw/:DEFAULT srctop_dir bldtop_dir/;
+
+BEGIN {
+setup("test_quicfaults");
+}
+
+use lib srctop_dir('Configurations');
+use lib bldtop_dir('.');
+
+plan skip_all => "QUIC protocol is not supported by this OpenSSL build"
+ if disabled('quic');
+
+plan tests => 1;
+
+ok(run(test(["quicfaultstest", srctop_dir("test", "certs")])),
+ "running quicfaultstest");