summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2023-04-18 19:30:56 +0100
committerHugo Landau <hlandau@openssl.org>2023-05-12 14:47:13 +0100
commitc3a04ea2fdd073e55b57e70e4f17f3ccbaa8c8a6 (patch)
tree2f9714368d7195d214ac85f5e860629ad9f5af80
parentb6fc2294a1a5bd6053647afea02180147018112b (diff)
downloadopenssl-new-c3a04ea2fdd073e55b57e70e4f17f3ccbaa8c8a6.tar.gz
QUIC DISPATCH/APL: Add SSL_stream_reset and status query APIs
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20765)
-rw-r--r--include/internal/quic_ssl.h14
-rw-r--r--include/openssl/ssl.h.in32
-rw-r--r--ssl/quic/quic_impl.c185
-rw-r--r--ssl/ssl_lib.c75
-rw-r--r--util/libssl.num6
5 files changed, 312 insertions, 0 deletions
diff --git a/include/internal/quic_ssl.h b/include/internal/quic_ssl.h
index ed17005843..050bfe9d24 100644
--- a/include/internal/quic_ssl.h
+++ b/include/internal/quic_ssl.h
@@ -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.
diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in
index dcc7cfdb3a..81d49e6299 100644
--- a/include/openssl/ssl.h.in
+++ b/include/openssl/ssl.h.in
@@ -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
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c
index e3402e5c65..fbcc85e52a 100644
--- a/ssl/quic/quic_impl.c
+++ b/ssl/quic/quic_impl.c
@@ -2306,6 +2306,191 @@ size_t ossl_quic_get_accept_stream_queue_len(SSL *s)
}
/*
+ * 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
* ==========================================
*/
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 1d84ac39dc..3fb80824de 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -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;
diff --git a/util/libssl.num b/util/libssl.num
index b99ed33a9e..457e75f801 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -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: