QUIC APL: Make SSL_get_error per-stream, error raising refactor
authorHugo Landau <hlandau@openssl.org>
Fri, 28 Apr 2023 15:56:34 +0000 (16:56 +0100)
committerHugo Landau <hlandau@openssl.org>
Wed, 24 May 2023 09:34:47 +0000 (10:34 +0100)
This refactors the error raising code in the APL to automatically raise
errors in the correct SSL object, either a QCSO or QSSO depending on the
circumstances. The QCTX structure's responsibilities are expanded to
facilitate this. This further drives towards a unified mechanism for
generic dispatch precondition checking and error raising.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20856)

ssl/quic/quic_impl.c
ssl/quic/quic_local.h

index 4706756b26f773d9330c70e39430667d764d2053..1f76232c49c524bceefc69268284608956b16816 100644 (file)
 #include "internal/quic_error.h"
 #include "internal/time.h"
 
+typedef struct qctx_st QCTX;
+
 static void aon_write_finish(QUIC_XSO *xso);
 static int create_channel(QUIC_CONNECTION *qc);
 static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs);
-static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc);
-static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc);
+static int qc_try_create_default_xso_for_write(QCTX *ctx);
+static int qc_wait_for_default_xso_for_read(QCTX *ctx);
 static void quic_lock(QUIC_CONNECTION *qc);
 static void quic_unlock(QUIC_CONNECTION *qc);
-static int quic_do_handshake(QUIC_CONNECTION *qc);
+static int quic_do_handshake(QCTX *ctx);
 static void qc_update_reject_policy(QUIC_CONNECTION *qc);
 static void qc_touch_default_xso(QUIC_CONNECTION *qc);
 static void qc_set_default_xso(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch);
 static void qc_set_default_xso_keep_ref(QUIC_CONNECTION *qc, QUIC_XSO *xso,
                                         int touch, QUIC_XSO **old_xso);
-static SSL *quic_conn_stream_new(QUIC_CONNECTION *qc, uint64_t flags,
-                                 int need_lock);
+static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock);
 
 /*
  * QUIC Front-End I/O API: Common Utilities
@@ -58,14 +59,38 @@ static int block_until_pred(QUIC_CONNECTION *qc,
                                               qc->mutex);
 }
 
+/*
+ * QCTX is a utility structure which provides information we commonly wish to
+ * unwrap upon an API call being dispatched to us, namely:
+ *
+ *   - a pointer to the QUIC_CONNECTION (regardless of whether a QCSO or QSSO
+ *     was passed);
+ *   - a pointer to any applicable QUIC_XSO (e.g. if a QSSO was passed, or if
+ *     a QCSO with a default stream was passed);
+ *   - whether a QSSO was passed (xso == NULL must not be used to determine this
+ *     because it may be non-NULL when a QCSO is passed if that QCSO has a
+ *     default stream).
+ */
+struct qctx_st {
+    QUIC_CONNECTION *qc;
+    QUIC_XSO        *xso;
+    int             is_stream;
+};
+
 /*
  * Raise a 'normal' error, meaning one that can be reported via SSL_get_error()
- * rather than via ERR.
+ * rather than via ERR. Note that normal errors must always be raised while
+ * holding a lock.
  */
-static int quic_raise_normal_error(QUIC_CONNECTION *qc,
+QUIC_NEEDS_LOCK
+static int quic_raise_normal_error(QCTX *ctx,
                                    int err)
 {
-    qc->last_error = err;
+    if (ctx->is_stream)
+        ctx->xso->last_error = err;
+    else
+        ctx->qc->last_error = err;
+
     return 0;
 }
 
@@ -77,8 +102,10 @@ static int quic_raise_normal_error(QUIC_CONNECTION *qc,
  * not known NULL may be passed. This should generally only happen when an
  * expect_...() function defined below fails, which generally indicates a
  * dispatch error or caller error.
+ *
+ * ctx should be NULL if the connection lock is not held.
  */
-static int quic_raise_non_normal_error(QUIC_CONNECTION *qc,
+static int quic_raise_non_normal_error(QCTX *ctx,
                                        const char *file,
                                        int line,
                                        const char *func,
@@ -95,40 +122,26 @@ static int quic_raise_non_normal_error(QUIC_CONNECTION *qc,
     ERR_vset_error(ERR_LIB_SSL, reason, fmt, args);
     va_end(args);
 
-    if (qc != NULL)
-        qc->last_error = SSL_ERROR_SSL;
+    if (ctx != NULL) {
+        if (ctx->is_stream && ctx->xso != NULL)
+            ctx->xso->last_error = SSL_ERROR_SSL;
+        else if (!ctx->is_stream && ctx->qc != NULL)
+            ctx->qc->last_error = SSL_ERROR_SSL;
+    }
 
     return 0;
 }
 
-#define QUIC_RAISE_NORMAL_ERROR(qc, err)                        \
-    quic_raise_normal_error((qc), (err))
+#define QUIC_RAISE_NORMAL_ERROR(ctx, err)                       \
+    quic_raise_normal_error((ctx), (err))
 
-#define QUIC_RAISE_NON_NORMAL_ERROR(qc, reason, msg)            \
-    quic_raise_non_normal_error((qc),                           \
+#define QUIC_RAISE_NON_NORMAL_ERROR(ctx, reason, msg)           \
+    quic_raise_non_normal_error((ctx),                          \
                                 OPENSSL_FILE, OPENSSL_LINE,     \
                                 OPENSSL_FUNC,                   \
                                 (reason),                       \
                                 (msg))
 
-/*
- * QCTX is a utility structure which provides information we commonly wish to
- * unwrap upon an API call being dispatched to us, namely:
- *
- *   - a pointer to the QUIC_CONNECTION (regardless of whether a QCSO or QSSO
- *     was passed);
- *   - a pointer to any applicable QUIC_XSO (e.g. if a QSSO was passed, or if
- *     a QCSO with a default stream was passed);
- *   - whether a QSSO was passed (xso == NULL must not be used to determine this
- *     because it may be non-NULL when a QCSO is passed if that QCSO has a
- *     default stream).
- */
-typedef struct qctx_st {
-    QUIC_CONNECTION *qc;
-    QUIC_XSO        *xso;
-    int             is_stream;
-} QCTX;
-
 /*
  * Given a QCSO or QSSO, initialises a QCTX, determining the contextually
  * applicable QUIC_CONNECTION pointer and, if applicable, QUIC_XSO pointer.
@@ -190,20 +203,20 @@ static int ossl_unused expect_quic_with_stream_lock(const SSL *s, int remote_ini
 
     if (ctx->xso == NULL && remote_init >= 0) {
         if (ossl_quic_channel_is_term_any(ctx->qc->ch)) {
-            QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+            QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
             goto err;
         }
 
         /* If we haven't finished the handshake, try to advance it. */
-        if (quic_do_handshake(ctx->qc) < 1)
+        if (quic_do_handshake(ctx) < 1)
             /* ossl_quic_do_handshake raised error here */
             goto err;
 
         if (remote_init == 0) {
-            if (!qc_try_create_default_xso_for_write(ctx->qc))
+            if (!qc_try_create_default_xso_for_write(ctx))
                 goto err;
         } else {
-            if (!qc_wait_for_default_xso_for_read(ctx->qc))
+            if (!qc_wait_for_default_xso_for_read(ctx))
                 goto err;
         }
 
@@ -211,7 +224,7 @@ static int ossl_unused expect_quic_with_stream_lock(const SSL *s, int remote_ini
     }
 
     if (ctx->xso == NULL) {
-        QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_NO_STREAM, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL);
         goto err;
     }
 
@@ -232,7 +245,7 @@ static int ossl_unused expect_quic_conn_only(const SSL *s, QCTX *ctx)
         return 0;
 
     if (ctx->is_stream)
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, SSL_R_CONN_USE_ONLY, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_CONN_USE_ONLY, NULL);
 
     return 1;
 }
@@ -740,7 +753,7 @@ int ossl_quic_conn_set_blocking_mode(SSL *s, int blocking)
     /* Cannot enable blocking mode if we do not have pollable FDs. */
     if (blocking != 0 &&
         (!ctx.qc->can_poll_net_rbio || !ctx.qc->can_poll_net_wbio))
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_UNSUPPORTED, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL);
 
     if (!ctx.is_stream) {
         /*
@@ -770,7 +783,7 @@ int ossl_quic_conn_set_initial_peer_addr(SSL *s,
         return 0;
 
     if (ctx.qc->started)
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
                                            NULL);
 
     if (peer_addr == NULL) {
@@ -1123,32 +1136,33 @@ err:
 }
 
 QUIC_NEEDS_LOCK
-static int quic_do_handshake(QUIC_CONNECTION *qc)
+static int quic_do_handshake(QCTX *ctx)
 {
     int ret;
+    QUIC_CONNECTION *qc = ctx->qc;
 
     if (ossl_quic_channel_is_handshake_complete(qc->ch))
         /* Handshake already completed. */
         return 1;
 
     if (ossl_quic_channel_is_term_any(qc->ch))
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
 
     if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
         /* Peer address must have been set. */
-        QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET, NULL);
         return -1; /* Non-protocol error */
     }
 
     if (qc->as_server != qc->as_server_state) {
         /* TODO(QUIC): Must match the method used to create the QCSO */
-        QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_PASSED_INVALID_ARGUMENT, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL);
         return -1; /* Non-protocol error */
     }
 
     if (qc->net_rbio == NULL || qc->net_wbio == NULL) {
         /* Need read and write BIOs. */
-        QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_BIO_NOT_SET, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_BIO_NOT_SET, NULL);
         return -1; /* Non-protocol error */
     }
 
@@ -1157,7 +1171,7 @@ static int quic_do_handshake(QUIC_CONNECTION *qc)
      * non-blocking mode, which is fine.
      */
     if (!ensure_channel_started(qc)) {
-        QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
         return -1; /* Non-protocol error */
     }
 
@@ -1173,10 +1187,10 @@ static int quic_do_handshake(QUIC_CONNECTION *qc)
 
         ret = block_until_pred(qc, quic_handshake_wait, &args, 0);
         if (!ossl_quic_channel_is_active(qc->ch)) {
-            QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+            QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
             return 0; /* Shutdown before completion */
         } else if (ret <= 0) {
-            QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+            QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
             return -1; /* Non-protocol error */
         }
 
@@ -1191,7 +1205,7 @@ static int quic_do_handshake(QUIC_CONNECTION *qc)
             return 1;
 
         /* Otherwise, indicate that the handshake isn't done yet. */
-        QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
+        QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_READ);
         return -1; /* Non-protocol error */
     }
 }
@@ -1207,7 +1221,7 @@ int ossl_quic_do_handshake(SSL *s)
 
     quic_lock(ctx.qc);
 
-    ret = quic_do_handshake(ctx.qc);
+    ret = quic_do_handshake(&ctx);
     quic_unlock(ctx.qc);
     return ret;
 }
@@ -1246,9 +1260,10 @@ int ossl_quic_accept(SSL *s)
  * exists). Note that this is NOT an error condition.
  */
 QUIC_NEEDS_LOCK
-static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc)
+static int qc_try_create_default_xso_for_write(QCTX *ctx)
 {
     uint64_t flags = 0;
+    QUIC_CONNECTION *qc = ctx->qc;
 
     if (qc->default_xso_created
         || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE)
@@ -1256,17 +1271,17 @@ static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc)
          * We only do this once. If the user detaches a previously created
          * default XSO we don't auto-create another one.
          */
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_NO_STREAM, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL);
 
     /* Create a locally-initiated stream. */
     if (qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_AUTO_UNI)
         flags |= SSL_STREAM_FLAG_UNI;
 
-    qc_set_default_xso(qc, (QUIC_XSO *)quic_conn_stream_new(qc, flags,
+    qc_set_default_xso(qc, (QUIC_XSO *)quic_conn_stream_new(ctx, flags,
                                                             /*needs_lock=*/0),
                        /*touch=*/0);
     if (qc->default_xso == NULL)
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
 
     qc_touch_default_xso(qc);
     return 1;
@@ -1275,6 +1290,7 @@ static int qc_try_create_default_xso_for_write(QUIC_CONNECTION *qc)
 struct quic_wait_for_stream_args {
     QUIC_CONNECTION *qc;
     QUIC_STREAM     *qs;
+    QCTX            *ctx;
     uint64_t        expect_id;
 };
 
@@ -1285,7 +1301,7 @@ static int quic_wait_for_stream(void *arg)
 
     if (!ossl_quic_channel_is_active(args->qc->ch)) {
         /* If connection is torn down due to an error while blocking, stop. */
-        QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         return -1;
     }
 
@@ -1302,10 +1318,11 @@ static int quic_wait_for_stream(void *arg)
 }
 
 QUIC_NEEDS_LOCK
-static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc)
+static int qc_wait_for_default_xso_for_read(QCTX *ctx)
 {
     /* Called on a QCSO and we don't currently have a default stream. */
     uint64_t expect_id;
+    QUIC_CONNECTION *qc = ctx->qc;
     QUIC_STREAM *qs;
     int res;
     struct quic_wait_for_stream_args wargs;
@@ -1316,7 +1333,7 @@ static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc)
      */
     if (qc->default_xso_created
         || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE)
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_NO_STREAM, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL);
 
     /*
      * The peer may have opened a stream since we last ticked. So tick and
@@ -1344,16 +1361,17 @@ static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc)
     if (qs == NULL) {
         if (!qc_blocking_mode(qc))
             /* Non-blocking mode, so just bail immediately. */
-            return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
+            return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_READ);
 
         /* Block until we have a stream. */
         wargs.qc        = qc;
         wargs.qs        = NULL;
+        wargs.ctx       = ctx;
         wargs.expect_id = expect_id;
 
         res = block_until_pred(qc, quic_wait_for_stream, &wargs, 0);
         if (res == 0)
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
         else if (res < 0 || wargs.qs == NULL)
             /* quic_wait_for_stream raised error here */
             return 0;
@@ -1367,7 +1385,7 @@ static int qc_wait_for_default_xso_for_read(QUIC_CONNECTION *qc)
      */
     qc_set_default_xso(qc, create_xso_from_stream(qc, qs), /*touch=*/0);
     if (qc->default_xso == NULL)
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
 
     qc_touch_default_xso(qc); /* inhibits default XSO */
     return 1;
@@ -1391,6 +1409,7 @@ static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs)
     xso->conn       = qc;
     xso->blocking   = qc->default_blocking;
     xso->ssl_mode   = qc->default_ssl_mode;
+    xso->last_error = SSL_ERROR_NONE;
 
     xso->stream     = qs;
 
@@ -1403,9 +1422,9 @@ err:
 }
 
 /* locking depends on need_lock */
-static SSL *quic_conn_stream_new(QUIC_CONNECTION *qc, uint64_t flags,
-                                 int need_lock)
+static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock)
 {
+    QUIC_CONNECTION *qc = ctx->qc;
     QUIC_XSO *xso = NULL;
     QUIC_STREAM *qs = NULL;
     int is_uni = ((flags & SSL_STREAM_FLAG_UNI) != 0);
@@ -1414,7 +1433,7 @@ static SSL *quic_conn_stream_new(QUIC_CONNECTION *qc, uint64_t flags,
         quic_lock(qc);
 
     if (ossl_quic_channel_is_term_any(qc->ch)) {
-        QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         goto err;
     }
 
@@ -1450,7 +1469,7 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
     if (!expect_quic_conn_only(s, &ctx))
         return NULL;
 
-    return quic_conn_stream_new(ctx.qc, flags, /*need_lock=*/1);
+    return quic_conn_stream_new(&ctx, flags, /*need_lock=*/1);
 }
 
 /*
@@ -1479,7 +1498,7 @@ int ossl_quic_get_error(const SSL *s, int i)
     if (!expect_quic(s, &ctx))
         return 0;
 
-    return ctx.qc->last_error;
+    return ctx.is_stream ? ctx.xso->last_error : ctx.qc->last_error;
 }
 
 /*
@@ -1554,10 +1573,11 @@ static int quic_write_again(void *arg)
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_blocking(QUIC_XSO *xso, const void *buf, size_t len,
+static int quic_write_blocking(QCTX *ctx, const void *buf, size_t len,
                                size_t *written)
 {
     int res;
+    QUIC_XSO *xso = ctx->xso;
     struct quic_write_again_args args;
     size_t actual_written = 0;
 
@@ -1566,7 +1586,7 @@ static int quic_write_blocking(QUIC_XSO *xso, const void *buf, size_t len,
                                   &actual_written)) {
         /* Stream already finished or allocation error. */
         *written = 0;
-        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     quic_post_write(xso, actual_written > 0, 1);
@@ -1590,9 +1610,9 @@ static int quic_write_blocking(QUIC_XSO *xso, const void *buf, size_t len,
     res = block_until_pred(xso->conn, quic_write_again, &args, 0);
     if (res <= 0) {
         if (!ossl_quic_channel_is_active(xso->conn->ch))
-            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         else
-            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     *written = args.total_written;
@@ -1623,9 +1643,10 @@ static void aon_write_finish(QUIC_XSO *xso)
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
+static int quic_write_nonblocking_aon(QCTX *ctx, const void *buf,
                                       size_t len, size_t *written)
 {
+    QUIC_XSO *xso = ctx->xso;
     const void *actual_buf;
     size_t actual_len, actual_written = 0;
     int accept_moving_buffer
@@ -1643,7 +1664,7 @@ static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
              * Pointer must not have changed if we are not in accept moving
              * buffer mode. Length must never change.
              */
-            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, SSL_R_BAD_WRITE_RETRY, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_BAD_WRITE_RETRY, NULL);
 
         actual_buf = (unsigned char *)buf + xso->aon_buf_pos;
         actual_len = len - xso->aon_buf_pos;
@@ -1658,7 +1679,7 @@ static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
                                   &actual_written)) {
         /* Stream already finished or allocation error. */
         *written = 0;
-        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     quic_post_write(xso, actual_written > 0, 1);
@@ -1690,7 +1711,7 @@ static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
          */
         xso->aon_buf_pos += actual_written;
         assert(xso->aon_buf_pos < xso->aon_buf_len);
-        return QUIC_RAISE_NORMAL_ERROR(xso->conn, SSL_ERROR_WANT_WRITE);
+        return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_WRITE);
     }
 
     /*
@@ -1706,18 +1727,20 @@ static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
      * completes.
      */
     *written = 0;
-    return QUIC_RAISE_NORMAL_ERROR(xso->conn, SSL_ERROR_WANT_WRITE);
+    return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_WRITE);
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_nonblocking_epw(QUIC_XSO *xso, const void *buf, size_t len,
+static int quic_write_nonblocking_epw(QCTX *ctx, const void *buf, size_t len,
                                       size_t *written)
 {
+    QUIC_XSO *xso = ctx->xso;
+
     /* Simple best effort operation. */
     if (!ossl_quic_sstream_append(xso->stream->sstream, buf, len, written)) {
         /* Stream already finished or allocation error. */
         *written = 0;
-        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     quic_post_write(xso, *written > 0, 1);
@@ -1742,7 +1765,7 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
     partial_write = ((ctx.xso->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0);
 
     if (ossl_quic_channel_is_term_any(ctx.qc->ch)) {
-        ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         goto out;
     }
 
@@ -1750,22 +1773,22 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
      * If we haven't finished the handshake, try to advance it.
      * We don't accept writes until the handshake is completed.
      */
-    if (quic_do_handshake(ctx.qc) < 1) {
+    if (quic_do_handshake(&ctx) < 1) {
         ret = 0;
         goto out;
     }
 
     if (ctx.xso->stream == NULL || ctx.xso->stream->sstream == NULL) {
-        ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
+        ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
         goto out;
     }
 
     if (xso_blocking_mode(ctx.xso))
-        ret = quic_write_blocking(ctx.xso, buf, len, written);
+        ret = quic_write_blocking(&ctx, buf, len, written);
     else if (partial_write)
-        ret = quic_write_nonblocking_epw(ctx.xso, buf, len, written);
+        ret = quic_write_nonblocking_epw(&ctx, buf, len, written);
     else
-        ret = quic_write_nonblocking_aon(ctx.xso, buf, len, written);
+        ret = quic_write_nonblocking_aon(&ctx, buf, len, written);
 
 out:
     quic_unlock(ctx.qc);
@@ -1777,7 +1800,7 @@ out:
  * --------
  */
 struct quic_read_again_args {
-    QUIC_CONNECTION *qc;
+    QCTX            *ctx;
     QUIC_STREAM     *stream;
     void            *buf;
     size_t          len;
@@ -1786,30 +1809,31 @@ struct quic_read_again_args {
 };
 
 QUIC_NEEDS_LOCK
-static int quic_read_actual(QUIC_CONNECTION *qc,
+static int quic_read_actual(QCTX *ctx,
                             QUIC_STREAM *stream,
                             void *buf, size_t buf_len,
                             size_t *bytes_read,
                             int peek)
 {
     int is_fin = 0;
+    QUIC_CONNECTION *qc = ctx->qc;
 
     /* If the receive part of the stream is over, issue EOF. */
     if (stream->recv_fin_retired)
-        return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_ZERO_RETURN);
+        return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_ZERO_RETURN);
 
     if (stream->rstream == NULL)
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
 
     if (peek) {
         if (!ossl_quic_rstream_peek(stream->rstream, buf, buf_len,
                                     bytes_read, &is_fin))
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
 
     } else {
         if (!ossl_quic_rstream_read(stream->rstream, buf, buf_len,
                                     bytes_read, &is_fin))
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     if (!peek) {
@@ -1826,7 +1850,7 @@ static int quic_read_actual(QUIC_CONNECTION *qc,
 
             if (!ossl_quic_rxfc_on_retire(&stream->rxfc, *bytes_read,
                                           rtt_info.smoothed_rtt))
-                return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+                return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
         }
 
         if (is_fin)
@@ -1845,13 +1869,13 @@ static int quic_read_again(void *arg)
 {
     struct quic_read_again_args *args = arg;
 
-    if (!ossl_quic_channel_is_active(args->qc->ch)) {
+    if (!ossl_quic_channel_is_active(args->ctx->qc->ch)) {
         /* If connection is torn down due to an error while blocking, stop. */
-        QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         return -1;
     }
 
-    if (!quic_read_actual(args->qc, args->stream,
+    if (!quic_read_actual(args->ctx, args->stream,
                           args->buf, args->len, args->bytes_read,
                           args->peek))
         return -1;
@@ -1878,12 +1902,12 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
     quic_lock(ctx.qc);
 
     if (ossl_quic_channel_is_term_any(ctx.qc->ch)) {
-        ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         goto out;
     }
 
     /* If we haven't finished the handshake, try to advance it. */
-    if (quic_do_handshake(ctx.qc) < 1) {
+    if (quic_do_handshake(&ctx) < 1) {
         ret = 0; /* ossl_quic_do_handshake raised error here */
         goto out;
     }
@@ -1895,7 +1919,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
          * Wait until we get a stream initiated by the peer (blocking mode) or
          * fail if we don't have one yet (non-blocking mode).
          */
-        if (!qc_wait_for_default_xso_for_read(ctx.qc)) {
+        if (!qc_wait_for_default_xso_for_read(&ctx)) {
             ret = 0; /* error already raised here */
             goto out;
         }
@@ -1904,11 +1928,11 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
     }
 
     if (ctx.xso->stream == NULL) {
-        ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
+        ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
         goto out;
     }
 
-    if (!quic_read_actual(ctx.qc, ctx.xso->stream, buf, len, bytes_read, peek)) {
+    if (!quic_read_actual(&ctx, ctx.xso->stream, buf, len, bytes_read, peek)) {
         ret = 0; /* quic_read_actual raised error here */
         goto out;
     }
@@ -1926,7 +1950,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
          * buffer is empty. This means we need to block until we get
          * at least one byte.
          */
-        args.qc         = ctx.qc;
+        args.ctx        = &ctx;
         args.stream     = ctx.xso->stream;
         args.buf        = buf;
         args.len        = len;
@@ -1935,7 +1959,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
 
         res = block_until_pred(ctx.qc, quic_read_again, &args, 0);
         if (res == 0) {
-            ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
+            ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
             goto out;
         } else if (res < 0) {
             ret = 0; /* quic_read_again raised error here */
@@ -1945,7 +1969,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
         ret = 1;
     } else {
         /* We did not get any bytes and are not in blocking mode. */
-        ret = QUIC_RAISE_NORMAL_ERROR(ctx.qc, SSL_ERROR_WANT_READ);
+        ret = QUIC_RAISE_NORMAL_ERROR(&ctx, SSL_ERROR_WANT_READ);
     }
 
 out:
@@ -2140,7 +2164,7 @@ int ossl_quic_set_default_stream_mode(SSL *s, uint32_t mode)
     quic_lock(ctx.qc);
 
     if (ctx.qc->default_xso_created)
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
                                            "too late to change default stream mode");
 
     switch (mode) {
@@ -2151,7 +2175,7 @@ int ossl_quic_set_default_stream_mode(SSL *s, uint32_t mode)
         break;
     default:
         quic_unlock(ctx.qc);
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_PASSED_INVALID_ARGUMENT,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT,
                                            "bad default stream type");
     }
 
@@ -2198,7 +2222,7 @@ int ossl_quic_attach_stream(SSL *conn, SSL *stream)
         return 0;
 
     if (stream == NULL || stream->type != SSL_TYPE_QUIC_XSO)
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_PASSED_NULL_PARAMETER,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_NULL_PARAMETER,
                                            "stream to attach must be a valid QUIC stream");
 
     xso = (QUIC_XSO *)stream;
@@ -2207,7 +2231,7 @@ int ossl_quic_attach_stream(SSL *conn, SSL *stream)
 
     if (ctx.qc->default_xso != NULL) {
         quic_unlock(ctx.qc);
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
                                            "connection already has a default stream");
     }
 
@@ -2217,13 +2241,13 @@ int ossl_quic_attach_stream(SSL *conn, SSL *stream)
      */
     if (!CRYPTO_GET_REF(&xso->ssl.references, &nref, &xso->ssl.lock)) {
         quic_unlock(ctx.qc);
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR,
                                            "ref");
     }
 
     if (nref != 1) {
         quic_unlock(ctx.qc);
-        return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_PASSED_INVALID_ARGUMENT,
+        return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT,
                                            "stream being attached must have "
                                            "only 1 reference");
     }
@@ -2302,7 +2326,7 @@ int ossl_quic_set_incoming_stream_policy(SSL *s, int policy,
  * -----------------
  */
 struct wait_for_incoming_stream_args {
-    QUIC_CONNECTION *qc;
+    QCTX            *ctx;
     QUIC_STREAM     *qs;
 };
 
@@ -2310,11 +2334,12 @@ QUIC_NEEDS_LOCK
 static int wait_for_incoming_stream(void *arg)
 {
     struct wait_for_incoming_stream_args *args = arg;
-    QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(args->qc->ch);
+    QUIC_CONNECTION *qc = args->ctx->qc;
+    QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(qc->ch);
 
-    if (!ossl_quic_channel_is_active(args->qc->ch)) {
+    if (!ossl_quic_channel_is_active(qc->ch)) {
         /* If connection is torn down due to an error while blocking, stop. */
-        QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         return -1;
     }
 
@@ -2353,12 +2378,12 @@ SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags)
             && (flags & SSL_ACCEPT_STREAM_NO_BLOCK) == 0) {
             struct wait_for_incoming_stream_args args;
 
-            args.qc = ctx.qc;
+            args.ctx = &ctx;
             args.qs = NULL;
 
             ret = block_until_pred(ctx.qc, wait_for_incoming_stream, &args, 0);
             if (ret == 0) {
-                QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
+                QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
                 goto out;
             } else if (ret < 0 || args.qs == NULL) {
                 goto out;
index a23d039e9c5dc8a2d5f5b4c1107eb565a0a09fe2..6ba0f1ee780e270d47cdffa16403a1f7d70fc191 100644 (file)
@@ -84,6 +84,13 @@ struct quic_xso_st {
 
     /* SSL_set_mode */
     uint32_t                        ssl_mode;
+
+    /*
+     * Last 'normal' error during an app-level I/O operation, used by
+     * SSL_get_error(); used to track data-path errors like SSL_ERROR_WANT_READ
+     * and SSL_ERROR_WANT_WRITE.
+     */
+    int                             last_error;
 };
 
 struct quic_conn_st {