]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC Test Server Implementation
authorHugo Landau <hlandau@openssl.org>
Tue, 22 Nov 2022 13:45:18 +0000 (13:45 +0000)
committerHugo Landau <hlandau@openssl.org>
Thu, 19 Jan 2023 13:17:40 +0000 (13:17 +0000)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19734)

include/internal/quic_tserver.h [new file with mode: 0644]
ssl/quic/build.info
ssl/quic/quic_tserver.c [new file with mode: 0644]

diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h
new file mode 100644 (file)
index 0000000..055d2f8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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
+ */
+
+#ifndef OSSL_QUIC_TSERVER_H
+# define OSSL_QUIC_TSERVER_H
+
+# include <openssl/ssl.h>
+# include "internal/quic_stream.h"
+
+# ifndef OPENSSL_NO_QUIC
+
+/*
+ * QUIC Test Server Module
+ * =======================
+ *
+ * This implements a QUIC test server. Since full QUIC server support is not yet
+ * implemented this server is limited in features and scope. It exists to
+ * provide a target for our QUIC client to talk to for testing purposes.
+ *
+ * A given QUIC test server instance supports only one client at a time.
+ *
+ * Note that this test server is not suitable for production use because it does
+ * not implement address verification, anti-amplification or retry logic.
+ */
+typedef struct quic_tserver_st QUIC_TSERVER;
+
+typedef struct quic_tserver_args_st {
+    OSSL_LIB_CTX *libctx;
+    const char *propq;
+    BIO *net_rbio, *net_wbio;
+} QUIC_TSERVER_ARGS;
+
+QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args);
+
+void ossl_quic_tserver_free(QUIC_TSERVER *srv);
+
+/* Advances the state machine. */
+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);
+
+/*
+ * 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
+ * to *bytes_read and 1 is returned (this is considered a success case).
+ *
+ * Returns 0 if connection is not currently active.
+ */
+int ossl_quic_tserver_read(QUIC_TSERVER *srv,
+                           unsigned char *buf,
+                           size_t buf_len,
+                           size_t *bytes_read);
+
+/*
+ * Attempts to write to stream 0. Writes the number of bytes consumed to
+ * *consumed and returns 1 on success. If there is no space currently available
+ * to write any bytes, 0 is written to *consumed and 1 is returned (this is
+ * considered a success case).
+ *
+ * Note that unlike libssl public APIs, this API always works in a 'partial
+ * write' mode.
+ *
+ * Returns 0 if connection is not currently active.
+ */
+int ossl_quic_tserver_write(QUIC_TSERVER *srv,
+                            const unsigned char *buf,
+                            size_t buf_len,
+                            size_t *bytes_written);
+
+# endif
+
+#endif
index 5f4d761b26e9e59408d60a8f26b13a689a77f2a4..c3718017cedede9459ae8168171214f5a36b0db3 100644 (file)
@@ -11,3 +11,4 @@ SOURCE[$LIBSSL]=quic_sf_list.c quic_rstream.c quic_sstream.c
 SOURCE[$LIBSSL]=quic_dummy_handshake.c
 SOURCE[$LIBSSL]=quic_reactor.c
 SOURCE[$LIBSSL]=quic_channel.c
+SOURCE[$LIBSSL]=quic_tserver.c
diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c
new file mode 100644 (file)
index 0000000..144fa07
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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 "internal/quic_tserver.h"
+#include "internal/quic_channel.h"
+#include "internal/quic_statm.h"
+#include "internal/common.h"
+
+/*
+ * QUIC Test Server Module
+ * =======================
+ */
+struct quic_tserver_st {
+    QUIC_TSERVER_ARGS   args;
+
+    /*
+     * The QUIC channel providing the core QUIC connection implementation.
+     */
+    QUIC_CHANNEL    *ch;
+
+    /* Our single bidirectional application data stream. */
+    QUIC_STREAM     *stream0;
+
+    /* The current peer L4 address. AF_UNSPEC if we do not have a peer yet. */
+    BIO_ADDR        cur_peer_addr;
+
+    /* Are we connected to a peer? */
+    unsigned int    connected       : 1;
+};
+
+QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args)
+{
+    QUIC_TSERVER *srv = NULL;
+    QUIC_CHANNEL_ARGS ch_args = {0};
+
+    if (args->net_rbio == NULL || args->net_wbio == NULL)
+        goto err;
+
+    if ((srv = OPENSSL_zalloc(sizeof(*srv))) == NULL)
+        goto err;
+
+    srv->args = *args;
+
+    ch_args.libctx      = srv->args.libctx;
+    ch_args.propq       = srv->args.propq;
+    ch_args.is_server   = 1;
+
+    if ((srv->ch = ossl_quic_channel_new(&ch_args)) == NULL)
+        goto err;
+
+    if (!ossl_quic_channel_set_net_rbio(srv->ch, srv->args.net_rbio)
+        || !ossl_quic_channel_set_net_wbio(srv->ch, srv->args.net_wbio))
+        goto err;
+
+    srv->stream0 = ossl_quic_channel_get_stream_by_id(srv->ch, 0);
+    if (srv->stream0 == NULL)
+        goto err;
+
+    return srv;
+
+err:
+    if (srv != NULL)
+        ossl_quic_channel_free(srv->ch);
+
+    OPENSSL_free(srv);
+    return NULL;
+}
+
+void ossl_quic_tserver_free(QUIC_TSERVER *srv)
+{
+    if (srv == NULL)
+        return;
+
+    ossl_quic_channel_free(srv->ch);
+    BIO_free(srv->args.net_rbio);
+    BIO_free(srv->args.net_wbio);
+    OPENSSL_free(srv);
+}
+
+int ossl_quic_tserver_tick(QUIC_TSERVER *srv)
+{
+    ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(srv->ch));
+
+    if (ossl_quic_channel_is_active(srv->ch))
+        srv->connected = 1;
+
+    return 1;
+}
+
+int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv)
+{
+    return ossl_quic_channel_is_active(srv->ch);
+}
+
+int ossl_quic_tserver_read(QUIC_TSERVER *srv,
+                           unsigned char *buf,
+                           size_t buf_len,
+                           size_t *bytes_read)
+{
+    int is_fin = 0; /* TODO(QUIC): Handle FIN in API */
+
+    if (!ossl_quic_channel_is_active(srv->ch))
+        return 0;
+
+    if (!ossl_quic_rstream_read(srv->stream0->rstream, buf, buf_len,
+                                bytes_read, &is_fin))
+        return 0;
+
+    if (*bytes_read > 0) {
+        /*
+         * We have read at least one byte from the stream. Inform stream-level
+         * RXFC of the retirement of controlled bytes. Update the active stream
+         * status (the RXFC may now want to emit a frame granting more credit to
+         * the peer).
+         */
+        OSSL_RTT_INFO rtt_info;
+        ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(srv->ch), &rtt_info);
+
+        if (!ossl_quic_rxfc_on_retire(&srv->stream0->rxfc, *bytes_read,
+                                      rtt_info.smoothed_rtt))
+            return 0;
+    }
+
+    if (is_fin)
+        srv->stream0->recv_fin_retired = 1;
+
+    if (*bytes_read > 0)
+        ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(srv->ch),
+                                          srv->stream0);
+
+    return 1;
+}
+
+int ossl_quic_tserver_write(QUIC_TSERVER *srv,
+                            const unsigned char *buf,
+                            size_t buf_len,
+                            size_t *bytes_written)
+{
+    if (!ossl_quic_channel_is_active(srv->ch))
+        return 0;
+
+    if (!ossl_quic_sstream_append(srv->stream0->sstream,
+                                  buf, buf_len, bytes_written))
+        return 0;
+
+    if (*bytes_written > 0)
+        /*
+         * We have appended at least one byte to the stream. Potentially mark
+         * the stream as active, depending on FC.
+         */
+        ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(srv->ch),
+                                          srv->stream0);
+
+    /* Try and send. */
+    ossl_quic_tserver_tick(srv);
+    return 1;
+}