Enable QUIC test server to find out the termination reason
authorMatt Caswell <matt@openssl.org>
Thu, 1 Dec 2022 16:37:47 +0000 (16:37 +0000)
committerHugo Landau <hlandau@openssl.org>
Wed, 22 Feb 2023 05:34:03 +0000 (05:34 +0000)
We enable querying of the termination reason which is useful for tests.

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_channel.h
include/internal/quic_tserver.h
ssl/quic/quic_channel.c
ssl/quic/quic_channel_local.h
ssl/quic/quic_impl.c
ssl/quic/quic_tserver.c
test/helpers/quictestlib.c

index a4039d8c0221b4aabcc8af05ca4fb2d6d8ae7318..4048bcd9c9fa41177b24f01a884af554d6402ce0 100644 (file)
@@ -64,6 +64,34 @@ typedef struct quic_channel_args_st {
 
 typedef struct quic_channel_st QUIC_CHANNEL;
 
+/* Represents the cause for a connection's termination. */
+typedef struct quic_terminate_cause_st {
+    /*
+     * If we are in a TERMINATING or TERMINATED state, this is the error code
+     * associated with the error. This field is valid iff we are in the
+     * TERMINATING or TERMINATED states.
+     */
+    uint64_t                        error_code;
+
+    /*
+     * If terminate_app is set and this is nonzero, this is the frame type which
+     * caused the connection to be terminated.
+     */
+    uint64_t                        frame_type;
+
+    /* Is this error code in the transport (0) or application (1) space? */
+    unsigned int                    app : 1;
+
+    /*
+     * If set, the cause of the termination is a received CONNECTION_CLOSE
+     * frame. Otherwise, we decided to terminate ourselves and sent a
+     * CONNECTION_CLOSE frame (regardless of whether the peer later also sends
+     * one).
+     */
+    unsigned int                    remote : 1;
+} QUIC_TERMINATE_CAUSE;
+
+
 /*
  * Create a new QUIC channel using the given arguments. The argument structure
  * does not need to remain allocated. Returns NULL on failure.
@@ -158,9 +186,12 @@ QUIC_STREAM *ossl_quic_channel_get_stream_by_id(QUIC_CHANNEL *ch,
                                                 uint64_t stream_id);
 
 /* Returns 1 if channel is terminating or terminated. */
-int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch);
-int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch);
-int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch);
+int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch,
+                                  QUIC_TERMINATE_CAUSE *cause);
+int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch,
+                                     QUIC_TERMINATE_CAUSE *cause);
+int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch,
+                                    QUIC_TERMINATE_CAUSE *cause);
 int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch);
 int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch);
 
index 0d564d8c21b08f056105655d4ca616cda42b3ab6..cd26c81042baf62c3c71b6bf7673b72254c0dc69 100644 (file)
@@ -12,6 +12,7 @@
 
 # include <openssl/ssl.h>
 # include "internal/quic_stream.h"
+# include "internal/quic_channel.h"
 
 # ifndef OPENSSL_NO_QUIC
 
@@ -54,8 +55,12 @@ int ossl_quic_tserver_tick(QUIC_TSERVER *srv);
 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);
+int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv,
+                                  QUIC_TERMINATE_CAUSE *cause);
 
+/* Returns 1 if the server is in a terminated state */
+int ossl_quic_tserver_is_terminated(QUIC_TSERVER *srv,
+                                    QUIC_TERMINATE_CAUSE *cause);
 /*
  * 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 6a0cc6d0049a6f8c60c98a3498f92bd7f5bde430..293dddd29862c171ad8ec7bcead48feb8209e788 100644 (file)
@@ -398,21 +398,34 @@ int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch)
     return ch != NULL && ch->state == QUIC_CHANNEL_STATE_ACTIVE;
 }
 
-int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch)
+int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch,
+                                     QUIC_TERMINATE_CAUSE *cause)
 {
-    return ch->state == QUIC_CHANNEL_STATE_TERMINATING_CLOSING
-        || ch->state == QUIC_CHANNEL_STATE_TERMINATING_DRAINING;
+    if (ch->state == QUIC_CHANNEL_STATE_TERMINATING_CLOSING
+            || ch->state == QUIC_CHANNEL_STATE_TERMINATING_DRAINING) {
+        if (cause != NULL)
+            *cause = ch->terminate_cause;
+        return 1;
+    }
+    return 0;
 }
 
-int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch)
+int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch,
+                                    QUIC_TERMINATE_CAUSE *cause)
 {
-    return ch->state == QUIC_CHANNEL_STATE_TERMINATED;
+    if (ch->state == QUIC_CHANNEL_STATE_TERMINATED) {
+        if (cause != NULL)
+            *cause = ch->terminate_cause;
+        return 1;
+    }
+    return 0;
 }
 
-int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch)
+int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch,
+                                  QUIC_TERMINATE_CAUSE *cause)
 {
-    return ossl_quic_channel_is_terminating(ch)
-        || ossl_quic_channel_is_terminated(ch);
+    return ossl_quic_channel_is_terminating(ch, cause)
+        || ossl_quic_channel_is_terminated(ch, cause);
 }
 
 int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch)
@@ -1191,7 +1204,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
      */
 
     /* If we are in the TERMINATED state, there is nothing to do. */
-    if (ossl_quic_channel_is_terminated(ch)) {
+    if (ossl_quic_channel_is_terminated(ch, NULL)) {
         res->net_read_desired   = 0;
         res->net_write_desired  = 0;
         res->tick_deadline      = ossl_time_infinite();
@@ -1202,7 +1215,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
      * If we are in the TERMINATING state, check if the terminating timer has
      * expired.
      */
-    if (ossl_quic_channel_is_terminating(ch)) {
+    if (ossl_quic_channel_is_terminating(ch, NULL)) {
         now = ossl_time_now();
 
         if (ossl_time_compare(now, ch->terminate_deadline) >= 0) {
@@ -1273,11 +1286,11 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
      * errors in ch_rx_pre() or ch_tx() may have caused us to transition to the
      * Terminated state.
      */
-    res->net_read_desired = !ossl_quic_channel_is_terminated(ch);
+    res->net_read_desired = !ossl_quic_channel_is_terminated(ch, NULL);
 
     /* We want to write to the network if we have any in our queue. */
     res->net_write_desired
-        = (!ossl_quic_channel_is_terminated(ch)
+        = (!ossl_quic_channel_is_terminated(ch, NULL)
            && ossl_qtx_get_queue_len_datagrams(ch->qtx) > 0);
 }
 
@@ -1581,7 +1594,7 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch)
     OSSL_TIME deadline;
     uint32_t pn_space;
 
-    if (ossl_quic_channel_is_terminated(ch))
+    if (ossl_quic_channel_is_terminated(ch, NULL))
         return ossl_time_infinite();
 
     deadline = ossl_ackm_get_loss_detection_deadline(ch->ackm);
@@ -1599,7 +1612,7 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch)
                                  ch->cc_method->get_next_credit_time(ch->cc_data));
 
     /* Is the terminating timer armed? */
-    if (ossl_quic_channel_is_terminating(ch))
+    if (ossl_quic_channel_is_terminating(ch, NULL))
         deadline = ossl_time_min(deadline,
                                  ch->terminate_deadline);
     else if (!ossl_time_is_infinite(ch->idle_deadline))
@@ -1729,7 +1742,7 @@ void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code)
 {
     QUIC_TERMINATE_CAUSE tcause = {0};
 
-    if (ossl_quic_channel_is_term_any(ch))
+    if (ossl_quic_channel_is_term_any(ch, NULL))
         return;
 
     tcause.app          = 1;
index 2240ad6e20398ee39026afb2c9332bc0ca8debac..fa2618bce54c0fc2a4a2cb291dc8ad4fa7718c83 100644 (file)
@@ -5,33 +5,6 @@
 
 # ifndef OPENSSL_NO_QUIC
 
-/* Represents the cause for a connection's termination. */
-typedef struct quic_terminate_cause_st {
-    /*
-     * If we are in a TERMINATING or TERMINATED state, this is the error code
-     * associated with the error. This field is valid iff we are in the
-     * TERMINATING or TERMINATED states.
-     */
-    uint64_t                        error_code;
-
-    /*
-     * If terminate_app is set and this is nonzero, this is the frame type which
-     * caused the connection to be terminated.
-     */
-    uint64_t                        frame_type;
-
-    /* Is this error code in the transport (0) or application (1) space? */
-    unsigned int                    app : 1;
-
-    /*
-     * If set, the cause of the termination is a received CONNECTION_CLOSE
-     * frame. Otherwise, we decided to terminate ourselves and sent a
-     * CONNECTION_CLOSE frame (regardless of whether the peer later also sends
-     * one).
-     */
-    unsigned int                    remote : 1;
-} QUIC_TERMINATE_CAUSE;
-
 /*
  * QUIC Channel Structure
  * ======================
index 5448e32e73faa4cca8259b52eb849b8ab885d8d6..bb9132eb759bb0f3682accbeb05446f91659f3dc 100644 (file)
@@ -646,7 +646,7 @@ int ossl_quic_do_handshake(QUIC_CONNECTION *qc)
         /* Handshake already completed. */
         return 1;
 
-    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
+    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
         return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
 
     if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
@@ -1012,7 +1012,7 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
     if (!expect_quic_conn(qc))
         return 0;
 
-    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
+    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
         return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
 
     /*
@@ -1133,7 +1133,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
     if (!expect_quic_conn(qc))
         return 0;
 
-    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
+    if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
         return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
 
     /* If we haven't finished the handshake, try to advance it. */
index ea131ca9c15c62b0453a72881ea744fc1ed6dcf5..1bb17e8e53ed24f24aa91abff60e2c92b82bf302 100644 (file)
@@ -148,9 +148,17 @@ 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)
+int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv,
+                                  QUIC_TERMINATE_CAUSE *cause)
 {
-    return ossl_quic_channel_is_term_any(srv->ch);
+    return ossl_quic_channel_is_term_any(srv->ch, cause);
+}
+
+/* Returns 1 if the server is in a terminated state */
+int ossl_quic_tserver_is_terminated(QUIC_TSERVER *srv,
+                                    QUIC_TERMINATE_CAUSE *cause)
+{
+    return ossl_quic_channel_is_terminated(srv->ch, cause);
 }
 
 int ossl_quic_tserver_read(QUIC_TSERVER *srv,
index 3886014c3c5d0ee848e2b698558d70cd419aac00..34672a5913b05dc26f62d1230daaf2ba7aa8cadb 100644 (file)
@@ -144,7 +144,7 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
             SSL_tick(clientssl);
         if (!servererr) {
             ossl_quic_tserver_tick(qtserv);
-            servererr = ossl_quic_tserver_is_term_any(qtserv);
+            servererr = ossl_quic_tserver_is_term_any(qtserv, NULL);
             if (!servererr && !rets)
                 rets = ossl_quic_tserver_is_connected(qtserv);
         }