]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add a skeleton quicfaultstest
authorMatt Caswell <matt@openssl.org>
Wed, 30 Nov 2022 14:21:00 +0000 (14:21 +0000)
committerHugo Landau <hlandau@openssl.org>
Wed, 22 Feb 2023 05:33:24 +0000 (05:33 +0000)
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)

include/internal/quic_tserver.h
ssl/quic/quic_tserver.c
test/build.info
test/helpers/quictestlib.c [new file with mode: 0644]
test/helpers/quictestlib.h [new file with mode: 0644]
test/quicfaultstest.c [new file with mode: 0644]
test/recipes/90-test_quicfaults.t [new file with mode: 0644]

index a19ec882ef73f34a15dab00457414e9b62f60322..0d564d8c21b08f056105655d4ca616cda42b3ab6 100644 (file)
@@ -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
index 2b5f04ac5a9ff5d2320d5f057aa7e0802bddef56..ea131ca9c15c62b0453a72881ea744fc1ed6dcf5 100644 (file)
@@ -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,
index 3b8d17a3251cbee5983eda80eb324202da422220..15c0985150f074682da32c515a3db02f8d451ff4 100644 (file)
@@ -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 (file)
index 0000000..1c4fa8d
--- /dev/null
@@ -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 (file)
index 0000000..3afea60
--- /dev/null
@@ -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 (file)
index 0000000..30d7caf
--- /dev/null
@@ -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 (file)
index 0000000..f4bd8ea
--- /dev/null
@@ -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");