diff options
author | Tomas Mraz <tomas@openssl.org> | 2023-03-16 18:25:37 +0100 |
---|---|---|
committer | Pauli <pauli@openssl.org> | 2023-03-22 00:13:30 +0100 |
commit | fc11028089c374bb24655895c90eaf069c3cee6f (patch) | |
tree | 9c23b28c127a4b83de99bf9eb98a3f1a641f42b6 | |
parent | Added `CERTIFICATE_VERIFY_MAX_LENGTH` constant (diff) | |
download | openssl-fc11028089c374bb24655895c90eaf069c3cee6f.tar.xz openssl-fc11028089c374bb24655895c90eaf069c3cee6f.zip |
Add simple interoperability test with Cloudflare quiche
This is an external test which requires recursive checkout
of the cloudflare-quiche submodule.
We simply run a client against the example quiche-server
serving HTTP/0.9 requests.
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20527)
-rw-r--r-- | .github/workflows/ci.yml | 18 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | cloudflare-quiche | 0 | ||||
-rw-r--r-- | test/build.info | 5 | ||||
-rw-r--r-- | test/quic_client_test.c | 176 | ||||
-rw-r--r-- | test/recipes/95-test_external_cf_quiche.t | 44 | ||||
-rwxr-xr-x | test/recipes/95-test_external_cf_quiche_data/quiche-build.sh | 26 | ||||
-rwxr-xr-x | test/recipes/95-test_external_cf_quiche_data/quiche-server.sh | 29 |
8 files changed, 301 insertions, 0 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb4384f926..17a87cd1e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,3 +410,21 @@ jobs: default: true - name: test external pyca run: make test TESTS="test_external_pyca" VERBOSE=1 + + external-test-cf-quiche: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Configure OpenSSL + run: ./config --banner=Configured --strict-warnings enable-external-tests enable-quic && perl configdata.pm --dump + - name: make + run: make -s -j4 + - uses: actions-rs/toolchain@v1 + with: + profile: default + toolchain: stable + default: true + - name: test external Cloudflare quiche + run: make test TESTS="test_external_cf_quiche" VERBOSE=1 diff --git a/.gitmodules b/.gitmodules index 6531705b2a..ee878a96ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "oqs-provider"] path = oqs-provider url = https://github.com/open-quantum-safe/oqs-provider.git +[submodule "cloudflare-quiche"] + path = cloudflare-quiche + url = https://github.com/cloudflare/quiche diff --git a/cloudflare-quiche b/cloudflare-quiche new file mode 160000 +Subproject 24a959abf115923910ce18985aa199d85fb602d diff --git a/test/build.info b/test/build.info index dc3e4518ac..56d53445dc 100644 --- a/test/build.info +++ b/test/build.info @@ -332,6 +332,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[quic_tserver_test]=../include ../apps/include DEPEND[quic_tserver_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[quic_client_test]=quic_client_test.c + INCLUDE[quic_client_test]=../include ../apps/include + DEPEND[quic_client_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[asynctest]=asynctest.c INCLUDE[asynctest]=../include ../apps/include DEPEND[asynctest]=../libcrypto @@ -1076,6 +1080,7 @@ ENDIF PROGRAMS{noinst}=quic_wire_test quic_ackm_test quic_record_test PROGRAMS{noinst}=quic_fc_test quic_stream_test quic_cfq_test quic_txpim_test PROGRAMS{noinst}=quic_fifd_test quic_txp_test quic_tserver_test + PROGRAMS{noinst}=quic_client_test ENDIF SOURCE[quic_ackm_test]=quic_ackm_test.c diff --git a/test/quic_client_test.c b/test/quic_client_test.c new file mode 100644 index 0000000000..81b21fda3a --- /dev/null +++ b/test/quic_client_test.c @@ -0,0 +1,176 @@ +/* + * Copyright 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include <stdio.h> +#include <openssl/ssl.h> +#include <openssl/quic.h> +#include <openssl/bio.h> +#include "internal/common.h" +#include "internal/sockets.h" +#include "internal/time.h" +#include "testutil.h" + +static const char msg1[] = "GET LICENSE.txt\r\n"; +static char msg2[16000]; + +static int is_want(SSL *s, int ret) +{ + int ec = SSL_get_error(s, ret); + + return ec == SSL_ERROR_WANT_READ || ec == SSL_ERROR_WANT_WRITE; +} + +static int test_quic_client(void) +{ + int testresult = 0, ret; + int c_fd = INVALID_SOCKET; + BIO *c_net_bio = NULL, *c_net_bio_own = NULL; + BIO_ADDR *s_addr_ = NULL; + struct in_addr ina = {0}; + SSL_CTX *c_ctx = NULL; + SSL *c_ssl = NULL; + short port = 4433; + int c_connected = 0, c_write_done = 0, c_shutdown = 0; + size_t l = 0, c_total_read = 0; + OSSL_TIME start_time; + unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '0', '.', '9' }; + + ina.s_addr = htonl(0x7f000001UL); + + /* Setup test client. */ + c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0); + if (!TEST_int_ne(c_fd, INVALID_SOCKET)) + goto err; + + if (!TEST_true(BIO_socket_nbio(c_fd, 1))) + goto err; + + if (!TEST_ptr(s_addr_ = BIO_ADDR_new())) + goto err; + + if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina), + htons(port)))) + goto err; + + if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0))) + goto err; + + if (!BIO_dgram_set_peer(c_net_bio, s_addr_)) + goto err; + + if (!TEST_ptr(c_ctx = SSL_CTX_new(OSSL_QUIC_client_method()))) + goto err; + + if (!TEST_ptr(c_ssl = SSL_new(c_ctx))) + goto err; + + /* 0 is a success for SSL_set_alpn_protos() */ + if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn)))) + goto err; + + /* Takes ownership of our reference to the BIO. */ + SSL_set0_rbio(c_ssl, c_net_bio); + + /* Get another reference to be transferred in the SSL_set0_wbio call. */ + if (!TEST_true(BIO_up_ref(c_net_bio))) { + c_net_bio_own = NULL; /* SSL_free will free the first reference. */ + goto err; + } + + SSL_set0_wbio(c_ssl, c_net_bio); + c_net_bio_own = NULL; + + if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0))) + goto err; + + start_time = ossl_time_now(); + + for (;;) { + if (ossl_time_compare(ossl_time_subtract(ossl_time_now(), start_time), + ossl_ms2time(3000)) >= 0) { + TEST_error("timeout while attempting QUIC client test"); + goto err; + } + + if (!c_connected) { + ret = SSL_connect(c_ssl); + if (!TEST_true(ret == 1 || is_want(c_ssl, ret))) + goto err; + + if (ret == 1) { + c_connected = 1; + TEST_info("Connected!"); + } + } + + if (c_connected && !c_write_done) { + if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1), + (int)sizeof(msg1) - 1)) + goto err; + + if (!TEST_true(SSL_stream_conclude(c_ssl, 0))) + goto err; + + c_write_done = 1; + } + + if (c_write_done && !c_shutdown && c_total_read < sizeof(msg2) - 1) { + ret = SSL_read_ex(c_ssl, msg2 + c_total_read, + sizeof(msg2) - 1 - c_total_read, &l); + if (ret != 1) { + if (SSL_get_error(c_ssl, ret) == SSL_ERROR_ZERO_RETURN) { + c_shutdown = 1; + TEST_info("Message: \n%s\n", msg2); + } else if (!TEST_true(is_want(c_ssl, ret))) { + goto err; + } + } else { + c_total_read += l; + + if (!TEST_size_t_lt(c_total_read, sizeof(msg2) - 1)) + goto err; + } + } + + if (c_shutdown) { + ret = SSL_shutdown(c_ssl); + if (ret == 1) + break; + } + + /* + * This is inefficient because we spin until things work without + * blocking but this is just a test. + */ + OSSL_sleep(0); + SSL_tick(c_ssl); + } + + testresult = 1; +err: + SSL_free(c_ssl); + SSL_CTX_free(c_ctx); + BIO_ADDR_free(s_addr_); + BIO_free(c_net_bio_own); + if (c_fd != INVALID_SOCKET) + BIO_closesocket(c_fd); + return testresult; +} + +OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") + +int setup_tests(void) +{ + if (!test_skip_common_options()) { + TEST_error("Error parsing test options\n"); + return 0; + } + + ADD_TEST(test_quic_client); + return 1; +} diff --git a/test/recipes/95-test_external_cf_quiche.t b/test/recipes/95-test_external_cf_quiche.t new file mode 100644 index 0000000000..133038fd19 --- /dev/null +++ b/test/recipes/95-test_external_cf_quiche.t @@ -0,0 +1,44 @@ +#! /usr/bin/env perl +# Copyright 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 +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +use OpenSSL::Test; +use OpenSSL::Test::Utils; +use OpenSSL::Test qw/:DEFAULT data_file bldtop_dir srctop_dir srctop_file/; + +setup("test_external_cf_quiche"); + +plan skip_all => "No external tests in this configuration" + if disabled("external-tests"); +plan skip_all => "Cloudflare quiche tests not available on Windows or VMS" + if $^O =~ /^(VMS|MSWin32)$/; +plan skip_all => "Cloudflare quiche tests only available with QUIC support" + if disabled("quic"); +plan skip_all => "Cloudflare & Boringssl not checked out" + if ! -f srctop_file("cloudflare-quiche", "quiche", "deps", "boringssl", "LICENSE"); + +plan tests => 3; + +ok(run(cmd(["sh", data_file("quiche-build.sh")])), + "running Cloudflare quiche build"); + +ok(run(cmd(["sh", data_file("quiche-server.sh")])), + "running Cloudflare quiche server"); + +ok(run(test(["quic_client_test"])), + "running quic_client_test"); + +open my $fh, '<', "server.pid" + or die "Error opening server.pid - $!\n"; +$serverpid = <$fh>; +close($fh); + +kill('TERM', $serverpid); +sleep(1); +kill('KILL', $serverpid); +waitpid($serverpid, 0); diff --git a/test/recipes/95-test_external_cf_quiche_data/quiche-build.sh b/test/recipes/95-test_external_cf_quiche_data/quiche-build.sh new file mode 100755 index 0000000000..5444d4e060 --- /dev/null +++ b/test/recipes/95-test_external_cf_quiche_data/quiche-build.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Copyright 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 +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# +# Build the Cloudflare quiche +# +set -e + +SRCTOP="$(cd $SRCTOP; pwd)" +BLDTOP="$(cd $BLDTOP; pwd)" + +echo "------------------------------------------------------------------" +echo "Building Cloudflare quiche" +echo "------------------------------------------------------------------" + +QUICHE_TARGET_PATH="$BLDTOP/quiche" +test -d "$QUICHE_TARGET_PATH" || mkdir "$QUICHE_TARGET_PATH" +echo " QUICHE_TARGET_PATH: $QUICHE_TARGET_PATH" + +(cd "$SRCTOP/cloudflare-quiche" && cargo build --verbose --target-dir "$QUICHE_TARGET_PATH") diff --git a/test/recipes/95-test_external_cf_quiche_data/quiche-server.sh b/test/recipes/95-test_external_cf_quiche_data/quiche-server.sh new file mode 100755 index 0000000000..4ed566a575 --- /dev/null +++ b/test/recipes/95-test_external_cf_quiche_data/quiche-server.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright 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 +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# +# Run the quiche server +# +set -e + +SRCTOP="$(cd $SRCTOP; pwd)" +BLDTOP="$(cd $BLDTOP; pwd)" + +echo "------------------------------------------------------------------" +echo "Running Cloudflare quiche-server" +echo "------------------------------------------------------------------" + +QUICHE_TARGET_PATH="$BLDTOP/quiche" +test -d "$QUICHE_TARGET_PATH" || exit 1 + +"$QUICHE_TARGET_PATH/debug/quiche-server" --cert "$SRCTOP/test/certs/servercert.pem" \ + --key "$SRCTOP/test/certs/serverkey.pem" --disable-gso \ + --http-version HTTP/0.9 --root "$SRCTOP" --no-grease --disable-hystart & + +echo $! >server.pid |