]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC DISPATCH/APL: Add SSL_stream_reset and status query APIs
authorHugo Landau <hlandau@openssl.org>
Tue, 18 Apr 2023 18:30:56 +0000 (19:30 +0100)
committerHugo Landau <hlandau@openssl.org>
Fri, 12 May 2023 13:47:13 +0000 (14:47 +0100)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20765)

include/internal/quic_ssl.h
include/openssl/ssl.h.in
ssl/quic/quic_impl.c
ssl/ssl_lib.c
util/libssl.num

index ed17005843d6479cf09153e773b27584f8f942a2..050bfe9d24f40f51a80635b07506afc1251e8870 100644 (file)
@@ -77,6 +77,20 @@ __owur int ossl_quic_set_incoming_stream_reject_policy(SSL *s, int policy,
 __owur SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags);
 __owur size_t ossl_quic_get_accept_stream_queue_len(SSL *s);
 
+__owur int ossl_quic_stream_reset(SSL *ssl,
+                                  const SSL_STREAM_RESET_ARGS *args,
+                                  size_t args_len);
+
+__owur int ossl_quic_get_stream_read_state(SSL *ssl);
+__owur int ossl_quic_get_stream_write_state(SSL *ssl);
+__owur int ossl_quic_get_stream_read_error_code(SSL *ssl,
+                                                uint64_t *app_error_code);
+__owur int ossl_quic_get_stream_write_error_code(SSL *ssl,
+                                                 uint64_t *app_error_code);
+__owur int ossl_quic_get_conn_close_info(SSL *ssl,
+                                         SSL_CONN_CLOSE_INFO *info,
+                                         size_t info_len);
+
 /*
  * Used to override ossl_time_now() for debug purposes. Must be called before
  * connecting.
index dcc7cfdb3a5809f992d0210b48a2bee5892e1ae2..81d49e62995795eaf4d48a40ded14f56a694cee2 100644 (file)
@@ -2318,6 +2318,38 @@ __owur int SSL_shutdown_ex(SSL *ssl, uint64_t flags,
 
 __owur int SSL_stream_conclude(SSL *ssl, uint64_t flags);
 
+typedef struct ssl_stream_reset_args_st {
+    uint64_t quic_error_code;
+} SSL_STREAM_RESET_ARGS;
+
+__owur int SSL_stream_reset(SSL *ssl,
+                            const SSL_STREAM_RESET_ARGS *args,
+                            size_t args_len);
+
+#define SSL_STREAM_STATE_NONE           0
+#define SSL_STREAM_STATE_OK             1
+#define SSL_STREAM_STATE_WRONG_DIR      2
+#define SSL_STREAM_STATE_FINISHED       3
+#define SSL_STREAM_STATE_RESET_LOCAL    4
+#define SSL_STREAM_STATE_RESET_REMOTE   5
+#define SSL_STREAM_STATE_CONN_CLOSED    6
+__owur int SSL_get_stream_read_state(SSL *ssl);
+__owur int SSL_get_stream_write_state(SSL *ssl);
+
+__owur int SSL_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code);
+__owur int SSL_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code);
+
+typedef struct ssl_conn_close_info_st {
+    uint64_t error_code;
+    char     *reason;
+    size_t    reason_len;
+    char      is_local, is_transport;
+} SSL_CONN_CLOSE_INFO;
+
+__owur int SSL_get_conn_close_info(SSL *ssl,
+                                   SSL_CONN_CLOSE_INFO *info,
+                                   size_t info_len);
+
 # ifndef OPENSSL_NO_DEPRECATED_1_1_0
 #  define SSL_cache_hit(s) SSL_session_reused(s)
 # endif
index e3402e5c651f1fd2cc82d2d61a46985cdcfa19c5..fbcc85e52a1b2ef2018f2649d4cb34fb6cdeae80 100644 (file)
@@ -2305,6 +2305,191 @@ size_t ossl_quic_get_accept_stream_queue_len(SSL *s)
     return v;
 }
 
+/*
+ * SSL_stream_reset
+ * ----------------
+ */
+int ossl_quic_stream_reset(SSL *ssl,
+                           const SSL_STREAM_RESET_ARGS *args,
+                           size_t args_len)
+{
+    QCTX ctx;
+    QUIC_STREAM_MAP *qsm;
+    QUIC_STREAM *qs;
+    uint64_t error_code;
+
+    if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/0, &ctx))
+        return 0;
+
+    qsm         = ossl_quic_channel_get_qsm(ctx.qc->ch);
+    qs          = ctx.xso->stream;
+    error_code  = (args != NULL ? args->quic_error_code : 0);
+
+    ossl_quic_stream_map_reset_stream_send_part(qsm, qs, error_code);
+
+    quic_unlock(ctx.qc);
+    return 1;
+}
+
+/*
+ * SSL_get_stream_read_state
+ * -------------------------
+ */
+static void quic_classify_stream(QUIC_CONNECTION *qc,
+                                 QUIC_STREAM *qs,
+                                 int is_write,
+                                 int *state,
+                                 uint64_t *app_error_code)
+{
+    int local_init;
+    uint64_t final_size;
+
+    local_init = (ossl_quic_stream_is_server_init(qs) == qc->as_server);
+
+    if (app_error_code != NULL)
+        *app_error_code = UINT64_MAX;
+    else
+        app_error_code = &final_size; /* throw away value */
+
+    if (!ossl_quic_stream_is_bidi(qs) && local_init != is_write) {
+        /*
+         * Unidirectional stream and this direction of transmission doesn't
+         * exist.
+         */
+        *state = SSL_STREAM_STATE_WRONG_DIR;
+    } else if (ossl_quic_channel_is_term_any(qc->ch)) {
+        /* Connection already closed. */
+        *state = SSL_STREAM_STATE_CONN_CLOSED;
+    } else if (!is_write && qs->recv_fin_retired) {
+        /* Application has read a FIN. */
+        *state = SSL_STREAM_STATE_FINISHED;
+    } else if ((!is_write && qs->stop_sending)
+               || (is_write && qs->reset_stream)) {
+        /*
+         * Stream has been reset locally. FIN takes precedence over this for the
+         * read case as the application need not care if the stream is reset
+         * after a FIN has been successfully processed.
+         */
+        *state          = SSL_STREAM_STATE_RESET_LOCAL;
+        *app_error_code = !is_write
+            ? qs->stop_sending_aec
+            : qs->reset_stream_aec;
+    } else if ((!is_write && qs->peer_reset_stream)
+               || (is_write && qs->peer_stop_sending)) {
+        /*
+         * Stream has been reset remotely. */
+        *state          = SSL_STREAM_STATE_RESET_REMOTE;
+        *app_error_code = !is_write
+            ? qs->peer_reset_stream_aec
+            : qs->peer_stop_sending_aec;
+    } else if (is_write && ossl_quic_sstream_get_final_size(qs->sstream,
+                                                            &final_size)) {
+        /*
+         * Stream has been finished. Stream reset takes precedence over this for
+         * the write case as peer may not have received all data.
+         */
+        *state = SSL_STREAM_STATE_FINISHED;
+    } else {
+        /* Stream still healthy. */
+        *state = SSL_STREAM_STATE_OK;
+    }
+}
+
+static int quic_get_stream_state(SSL *ssl, int is_write)
+{
+    QCTX ctx;
+    int state;
+
+    if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx))
+        return SSL_STREAM_STATE_NONE;
+
+    quic_classify_stream(ctx.qc, ctx.xso->stream, is_write, &state, NULL);
+    quic_unlock(ctx.qc);
+    return state;
+}
+
+int ossl_quic_get_stream_read_state(SSL *ssl)
+{
+    return quic_get_stream_state(ssl, /*is_write=*/0);
+}
+
+/*
+ * SSL_get_stream_write_state
+ * --------------------------
+ */
+int ossl_quic_get_stream_write_state(SSL *ssl)
+{
+    return quic_get_stream_state(ssl, /*is_write=*/1);
+}
+
+/*
+ * SSL_get_stream_read_error_code
+ * ------------------------------
+ */
+static int quic_get_stream_error_code(SSL *ssl, int is_write,
+                                      uint64_t *app_error_code)
+{
+    QCTX ctx;
+    int state;
+
+    if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx))
+        return -1;
+
+    quic_classify_stream(ctx.qc, ctx.xso->stream, /*is_write=*/0,
+                         &state, app_error_code);
+
+    quic_unlock(ctx.qc);
+    switch (state) {
+        case SSL_STREAM_STATE_FINISHED:
+             return 0;
+        case SSL_STREAM_STATE_RESET_LOCAL:
+        case SSL_STREAM_STATE_RESET_REMOTE:
+             return 1;
+        default:
+             return -1;
+    }
+}
+
+int ossl_quic_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code)
+{
+    return quic_get_stream_error_code(ssl, /*is_write=*/0, app_error_code);
+}
+
+/*
+ * SSL_get_stream_write_error_code
+ * -------------------------------
+ */
+int ossl_quic_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code)
+{
+    return quic_get_stream_error_code(ssl, /*is_write=*/1, app_error_code);
+}
+
+/*
+ * SSL_get_conn_close_info
+ * -----------------------
+ */
+int ossl_quic_get_conn_close_info(SSL *ssl,
+                                  SSL_CONN_CLOSE_INFO *info,
+                                  size_t info_len)
+{
+    QCTX ctx;
+    const QUIC_TERMINATE_CAUSE *tc;
+
+    if (!expect_quic_conn_only(ssl, &ctx))
+        return -1;
+
+    tc = ossl_quic_channel_get_terminate_cause(ctx.qc->ch);
+    if (tc == NULL)
+        return 0;
+
+    info->error_code    = tc->error_code;
+    info->reason        = NULL; /* TODO(QUIC): Wire reason */
+    info->reason_len    = 0;
+    info->is_local      = !tc->remote;
+    info->is_transport  = !tc->app;
+    return 1;
+}
+
 /*
  * QUIC Front-End I/O API: SSL_CTX Management
  * ==========================================
index 1d84ac39dccef9a5264d9e7347455cea61f368ee..3fb80824de2b0f26c7ede911b0307efb2ea8d650 100644 (file)
@@ -7424,6 +7424,81 @@ size_t SSL_get_accept_stream_queue_len(SSL *s)
 #endif
 }
 
+int SSL_stream_reset(SSL *s,
+                     const SSL_STREAM_RESET_ARGS *args,
+                     size_t args_len)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return 0;
+
+    return ossl_quic_stream_reset(s, args, args_len);
+#else
+    return 0;
+#endif
+}
+
+int SSL_get_stream_read_state(SSL *s)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return SSL_STREAM_STATE_NONE;
+
+    return ossl_quic_get_stream_read_state(s);
+#else
+    return SSL_STREAM_STATE_NONE;
+#endif
+}
+
+int SSL_get_stream_write_state(SSL *s)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return SSL_STREAM_STATE_NONE;
+
+    return ossl_quic_get_stream_write_state(s);
+#else
+    return SSL_STREAM_STATE_NONE;
+#endif
+}
+
+int SSL_get_stream_read_error_code(SSL *s, uint64_t *app_error_code)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return -1;
+
+    return ossl_quic_get_stream_read_error_code(s, app_error_code);
+#else
+    return -1;
+#endif
+}
+
+int SSL_get_stream_write_error_code(SSL *s, uint64_t *app_error_code)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return -1;
+
+    return ossl_quic_get_stream_write_error_code(s, app_error_code);
+#else
+    return -1;
+#endif
+}
+
+int SSL_get_conn_close_info(SSL *s, SSL_CONN_CLOSE_INFO *info,
+                            size_t info_len)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return -1;
+
+    return ossl_quic_get_conn_close_info(s, info, info_len);
+#else
+    return -1;
+#endif
+}
+
 int SSL_add_expected_rpk(SSL *s, EVP_PKEY *rpk)
 {
     unsigned char *data = NULL;
index b99ed33a9e9cfe452c418a8d3fa4774bc938ea75..457e75f80101f0574ee2cd26ea99dfbd17ed0524 100644 (file)
@@ -571,3 +571,9 @@ SSL_attach_stream                       ?   3_2_0   EXIST::FUNCTION:
 SSL_set_incoming_stream_reject_policy   ?      3_2_0   EXIST::FUNCTION:
 SSL_accept_stream                       ?      3_2_0   EXIST::FUNCTION:
 SSL_get_accept_stream_queue_len         ?      3_2_0   EXIST::FUNCTION:
+SSL_stream_reset                        ?      3_2_0   EXIST::FUNCTION:
+SSL_get_stream_read_state               ?      3_2_0   EXIST::FUNCTION:
+SSL_get_stream_write_state              ?      3_2_0   EXIST::FUNCTION:
+SSL_get_stream_read_error_code          ?      3_2_0   EXIST::FUNCTION:
+SSL_get_stream_write_error_code         ?      3_2_0   EXIST::FUNCTION:
+SSL_get_conn_close_info                 ?      3_2_0   EXIST::FUNCTION: