QUIC APL: Refactor stream-related code into QUIC_XSO object
authorHugo Landau <hlandau@openssl.org>
Tue, 18 Apr 2023 18:30:54 +0000 (19:30 +0100)
committerHugo Landau <hlandau@openssl.org>
Fri, 12 May 2023 13:47:11 +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/quic/quic_local.h
ssl/ssl_lib.c
util/libssl.num

index b0b1b7fb1962c6cfcd01f1e3b222f11228bca9fe..97ee2c921874d717ab91ac39a07aac0e2628ed6a 100644 (file)
@@ -65,6 +65,7 @@ BIO *ossl_quic_conn_get_net_rbio(const SSL *s);
 BIO *ossl_quic_conn_get_net_wbio(const SSL *s);
 __owur int ossl_quic_conn_set_initial_peer_addr(SSL *s,
                                                 const BIO_ADDR *peer_addr);
+__owur SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags);
 
 /*
  * Used to override ossl_time_now() for debug purposes. Must be called before
index 38dc3e51720375f91cdf2132dc88e52c679fa97c..9593e6bfed29c8068d326dc85b58110c672cdc53 100644 (file)
@@ -2266,6 +2266,10 @@ __owur int SSL_net_write_desired(SSL *s);
 __owur int SSL_set_blocking_mode(SSL *s, int blocking);
 __owur int SSL_get_blocking_mode(SSL *s);
 __owur int SSL_set_initial_peer_addr(SSL *s, const BIO_ADDR *peer_addr);
+
+#define SSL_STREAM_FLAG_UNI     (1U << 0)
+__owur SSL *SSL_new_stream(SSL *s, uint64_t flags);
+
 # ifndef OPENSSL_NO_QUIC
 __owur int SSL_inject_net_dgram(SSL *s, const unsigned char *buf,
                                 size_t buf_len,
index ac472d5a2349dae078e187aa7bfc0bbe28be5f1d..c02efed5f85d3f4e64811afea33e0b0bff6e8bc4 100644 (file)
@@ -17,7 +17,7 @@
 #include "internal/quic_error.h"
 #include "internal/time.h"
 
-static void aon_write_finish(QUIC_CONNECTION *qc);
+static void aon_write_finish(QUIC_XSO *xso);
 static int create_channel(QUIC_CONNECTION *qc);
 
 /*
@@ -140,13 +140,13 @@ static int expect_quic(const SSL *s, QCTX *ctx)
     case SSL_TYPE_QUIC_CONNECTION:
         qc              = (QUIC_CONNECTION *)s;
         ctx->qc         = qc;
-        ctx->xso        = NULL; /* TODO XXX (Filled by subsequent commit) */
+        ctx->xso        = qc->default_xso;
         ctx->is_stream  = 0;
         return 1;
 
     case SSL_TYPE_QUIC_XSO:
         xso             = (QUIC_XSO *)s;
-        ctx->qc         = NULL; /* TODO XXX (Filled by subsequent commit) */
+        ctx->qc         = xso->conn;
         ctx->xso        = xso;
         ctx->is_stream  = 1;
         return 1;
@@ -250,9 +250,9 @@ SSL *ossl_quic_new(SSL_CTX *ctx)
     qc->as_server       = 0; /* TODO(QUIC): server support */
     qc->as_server_state = qc->as_server;
 
-    qc->ssl_mode   = qc->ssl.ctx->mode;
-    qc->last_error = SSL_ERROR_NONE;
-    qc->blocking   = 1;
+    qc->default_ssl_mode    = qc->ssl.ctx->mode;
+    qc->default_blocking    = 1;
+    qc->last_error          = SSL_ERROR_NONE;
 
     if (!create_channel(qc))
         goto err;
@@ -448,7 +448,8 @@ void ossl_quic_conn_set0_net_rbio(SSL *s, BIO *net_rbio)
 
         if (!BIO_get_rpoll_descriptor(net_rbio, &d)
             || d.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
-            ctx.qc->blocking = 0;
+            ctx.qc->blocking          = 0;
+            ctx.qc->default_blocking  = 0;
             ctx.qc->can_poll_net_rbio = 0;
         } else {
             ctx.qc->can_poll_net_rbio = 1;
@@ -477,7 +478,8 @@ void ossl_quic_conn_set0_net_wbio(SSL *s, BIO *net_wbio)
 
         if (!BIO_get_wpoll_descriptor(net_wbio, &d)
             || d.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
-            ctx.qc->blocking = 0;
+            ctx.qc->blocking          = 0;
+            ctx.qc->default_blocking  = 0;
             ctx.qc->can_poll_net_wbio = 0;
         } else {
             ctx.qc->can_poll_net_wbio = 1;
@@ -526,6 +528,9 @@ int ossl_quic_conn_get_blocking_mode(const SSL *s)
     if (!expect_quic(s, &ctx))
         return 0;
 
+    if (ctx.is_stream)
+        return ctx.xso->blocking;
+
     return ctx.qc->blocking;
 }
 
@@ -541,7 +546,22 @@ int ossl_quic_conn_set_blocking_mode(SSL *s, int blocking)
         (!ctx.qc->can_poll_net_rbio || !ctx.qc->can_poll_net_wbio))
         return QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_UNSUPPORTED, NULL);
 
-    ctx.qc->blocking = (blocking != 0);
+    if (!ctx.is_stream) {
+        /*
+         * If called on a QCSO, update default and connection-level blocking
+         * modes.
+         */
+        ctx.qc->blocking         = (blocking != 0);
+        ctx.qc->default_blocking = ctx.qc->blocking;
+    }
+
+    if (ctx.xso != NULL)
+        /*
+         * If called on  a QSSO or QCSO with a default XSO, update blocking
+         * mode.
+         */
+        ctx.xso->blocking = (blocking != 0);
+
     return 1;
 }
 
@@ -577,11 +597,18 @@ int ossl_quic_conn_set_initial_peer_addr(SSL *s,
  */
 
 /* Returns 1 if the connection is being used in blocking mode. */
-static int blocking_mode(const QUIC_CONNECTION *qc)
+static int qc_blocking_mode(const QUIC_CONNECTION *qc)
 {
     return qc->blocking;
 }
 
+static int xso_blocking_mode(const QUIC_XSO *xso)
+{
+    return xso->blocking
+        && xso->conn->can_poll_net_rbio
+        && xso->conn->can_poll_net_wbio;
+}
+
 /* SSL_tick; ticks the reactor. */
 QUIC_TAKES_LOCK
 int ossl_quic_tick(SSL *s)
@@ -722,6 +749,10 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
     if (!expect_quic(s, &ctx))
         return 0;
 
+    if (ctx.is_stream)
+        /* TODO(QUIC): Semantics currently undefined for QSSOs */
+        return -1;
+
     quic_lock(ctx.qc);
 
     ossl_quic_channel_local_close(ctx.qc->ch,
@@ -734,7 +765,7 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
         return 1;
     }
 
-    if (blocking_mode(ctx.qc) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0)
+    if (qc_blocking_mode(ctx.qc) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0)
         block_until_pred(ctx.qc, quic_shutdown_wait, ctx.qc, 0);
     else
         ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(ctx.qc->ch), 0);
@@ -754,15 +785,34 @@ long ossl_quic_ctrl(SSL *s, int cmd, long larg, void *parg)
 
     switch (cmd) {
     case SSL_CTRL_MODE:
-        /* Cannot enable EPW while AON write in progress. */
-        if (ctx.qc->aon_write_in_progress)
-            larg &= ~SSL_MODE_ENABLE_PARTIAL_WRITE;
+        /* If called on a QCSO, update the default mode. */
+        if (!ctx.is_stream)
+            ctx.qc->default_ssl_mode |= (uint32_t)larg;
+
+        /*
+         * If we were called on a QSSO or have a default stream, we also update
+         * that.
+         */
+        if (ctx.xso != NULL) {
+            /* Cannot enable EPW while AON write in progress. */
+            if (ctx.xso->aon_write_in_progress)
+                larg &= ~SSL_MODE_ENABLE_PARTIAL_WRITE;
+
+            ctx.xso->ssl_mode |= (uint32_t)larg;
+            return ctx.xso->ssl_mode;
+        }
 
-        ctx.qc->ssl_mode |= (uint32_t)larg;
-        return ctx.qc->ssl_mode;
+        return ctx.qc->default_ssl_mode;
     case SSL_CTRL_CLEAR_MODE:
-        ctx.qc->ssl_mode &= ~(uint32_t)larg;
-        return ctx.qc->ssl_mode;
+        if (!ctx.is_stream)
+            ctx.qc->default_ssl_mode &= ~(uint32_t)larg;
+
+        if (ctx.xso != NULL) {
+            ctx.xso->ssl_mode &= ~(uint32_t)larg;
+            return ctx.xso->ssl_mode;
+        }
+
+        return ctx.qc->default_ssl_mode;
     default:
         /* Probably a TLS related ctrl. Defer to our internal SSL object */
         return SSL_ctrl(ctx.qc->tls, cmd, larg, parg);
@@ -778,7 +828,7 @@ void ossl_quic_set_connect_state(SSL *s)
         return;
 
     /* Cannot be changed after handshake started */
-    if (ctx.qc->started)
+    if (ctx.qc->started || ctx.is_stream)
         return;
 
     ctx.qc->as_server_state = 0;
@@ -793,7 +843,7 @@ void ossl_quic_set_accept_state(SSL *s)
         return;
 
     /* Cannot be changed after handshake started */
-    if (ctx.qc->started)
+    if (ctx.qc->started || ctx.is_stream)
         return;
 
     ctx.qc->as_server_state = 1;
@@ -862,10 +912,6 @@ static int ensure_channel_started(QUIC_CONNECTION *qc)
             || !ossl_quic_channel_start(qc->ch))
             goto err;
 
-        qc->stream0 = ossl_quic_channel_get_stream_by_id(qc->ch, 0);
-        if (qc->stream0 == NULL)
-            goto err;
-
         if (qc->is_thread_assisted)
             if (!ossl_quic_thread_assist_init_start(&qc->thread_assist, qc->ch))
                 goto err;
@@ -923,7 +969,7 @@ static int quic_do_handshake(QUIC_CONNECTION *qc)
         /* The handshake is now done. */
         return 1;
 
-    if (blocking_mode(qc)) {
+    if (qc_blocking_mode(qc)) {
         /* In blocking mode, wait for the handshake to complete. */
         struct quic_handshake_wait_args args;
 
@@ -990,6 +1036,39 @@ int ossl_quic_accept(SSL *s)
     return ossl_quic_do_handshake(s);
 }
 
+/*
+ * QUIC Front-End I/O API: Stream Lifecycle Operations
+ * ===================================================
+ *
+ *         SSL_stream_new       => ossl_quic_conn_stream_new
+ *
+ */
+SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
+{
+    QCTX ctx;
+    QUIC_XSO *xso = NULL;
+
+    if (!expect_quic_conn_only(s, &ctx))
+        return NULL;
+
+    if ((xso = OPENSSL_zalloc(sizeof(*xso))) == NULL)
+        return NULL;
+
+    if (!ossl_ssl_init(&xso->ssl, s->ctx, s->method, SSL_TYPE_QUIC_XSO))
+        goto err;
+
+    xso->conn       = ctx.qc;
+    xso->blocking   = ctx.qc->default_blocking;
+    xso->ssl_mode   = ctx.qc->default_ssl_mode;
+
+    xso->stream     = NULL; /* TODO XXX ossl_quic_channel_new_stream */
+    return &xso->ssl;
+
+err:
+    OPENSSL_free(xso);
+    return NULL;
+}
+
 /*
  * QUIC Front-End I/O API: Steady-State Operations
  * ===============================================
@@ -1035,15 +1114,15 @@ int ossl_quic_get_error(const SSL *s, int i)
  *
  */
 QUIC_NEEDS_LOCK
-static void quic_post_write(QUIC_CONNECTION *qc, int did_append, int do_tick)
+static void quic_post_write(QUIC_XSO *xso, int did_append, int do_tick)
 {
     /*
      * We have appended at least one byte to the stream.
      * Potentially mark stream as active, depending on FC.
      */
     if (did_append)
-        ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch),
-                                          qc->stream0);
+        ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(xso->conn->ch),
+                                          xso->stream);
 
     /*
      * Try and send.
@@ -1052,11 +1131,11 @@ static void quic_post_write(QUIC_CONNECTION *qc, int did_append, int do_tick)
      * plus we should eventually consider Nagle's algorithm.
      */
     if (do_tick)
-        ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch), 0);
+        ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(xso->conn->ch), 0);
 }
 
 struct quic_write_again_args {
-    QUIC_CONNECTION     *qc;
+    QUIC_XSO            *xso;
     const unsigned char *buf;
     size_t              len;
     size_t              total_written;
@@ -1068,15 +1147,15 @@ static int quic_write_again(void *arg)
     struct quic_write_again_args *args = arg;
     size_t actual_written = 0;
 
-    if (!ossl_quic_channel_is_active(args->qc->ch))
+    if (!ossl_quic_channel_is_active(args->xso->conn->ch))
         /* If connection is torn down due to an error while blocking, stop. */
         return -2;
 
-    if (!ossl_quic_sstream_append(args->qc->stream0->sstream,
+    if (!ossl_quic_sstream_append(args->xso->stream->sstream,
                                   args->buf, args->len, &actual_written))
         return -2;
 
-    quic_post_write(args->qc, actual_written > 0, 0);
+    quic_post_write(args->xso, actual_written > 0, 0);
 
     args->buf           += actual_written;
     args->len           -= actual_written;
@@ -1091,7 +1170,7 @@ static int quic_write_again(void *arg)
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_blocking(QUIC_CONNECTION *qc, const void *buf, size_t len,
+static int quic_write_blocking(QUIC_XSO *xso, const void *buf, size_t len,
                                size_t *written)
 {
     int res;
@@ -1099,14 +1178,14 @@ static int quic_write_blocking(QUIC_CONNECTION *qc, const void *buf, size_t len,
     size_t actual_written = 0;
 
     /* First make a best effort to append as much of the data as possible. */
-    if (!ossl_quic_sstream_append(qc->stream0->sstream, buf, len,
+    if (!ossl_quic_sstream_append(xso->stream->sstream, buf, len,
                                   &actual_written)) {
         /* Stream already finished or allocation error. */
         *written = 0;
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
     }
 
-    quic_post_write(qc, actual_written > 0, 1);
+    quic_post_write(xso, actual_written > 0, 1);
 
     if (actual_written == len) {
         /* Managed to append everything on the first try. */
@@ -1119,17 +1198,17 @@ static int quic_write_blocking(QUIC_CONNECTION *qc, const void *buf, size_t len,
      * buffer has probably filled up. This means we need to block until some of
      * it is freed up.
      */
-    args.qc             = qc;
+    args.xso            = xso;
     args.buf            = (const unsigned char *)buf + actual_written;
     args.len            = len - actual_written;
     args.total_written  = 0;
 
-    res = block_until_pred(qc, quic_write_again, &args, 0);
+    res = block_until_pred(xso->conn, quic_write_again, &args, 0);
     if (res <= 0) {
-        if (!ossl_quic_channel_is_active(qc->ch))
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+        if (!ossl_quic_channel_is_active(xso->conn->ch))
+            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
         else
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
     }
 
     *written = args.total_written;
@@ -1140,50 +1219,50 @@ static int quic_write_blocking(QUIC_CONNECTION *qc, const void *buf, size_t len,
  * Functions to manage All-or-Nothing (AON) (that is, non-ENABLE_PARTIAL_WRITE)
  * write semantics.
  */
-static void aon_write_begin(QUIC_CONNECTION *qc, const unsigned char *buf,
+static void aon_write_begin(QUIC_XSO *xso, const unsigned char *buf,
                             size_t buf_len, size_t already_sent)
 {
-    assert(!qc->aon_write_in_progress);
+    assert(!xso->aon_write_in_progress);
 
-    qc->aon_write_in_progress = 1;
-    qc->aon_buf_base          = buf;
-    qc->aon_buf_pos           = already_sent;
-    qc->aon_buf_len           = buf_len;
+    xso->aon_write_in_progress = 1;
+    xso->aon_buf_base          = buf;
+    xso->aon_buf_pos           = already_sent;
+    xso->aon_buf_len           = buf_len;
 }
 
-static void aon_write_finish(QUIC_CONNECTION *qc)
+static void aon_write_finish(QUIC_XSO *xso)
 {
-    qc->aon_write_in_progress   = 0;
-    qc->aon_buf_base            = NULL;
-    qc->aon_buf_pos             = 0;
-    qc->aon_buf_len             = 0;
+    xso->aon_write_in_progress   = 0;
+    xso->aon_buf_base            = NULL;
+    xso->aon_buf_pos             = 0;
+    xso->aon_buf_len             = 0;
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_nonblocking_aon(QUIC_CONNECTION *qc, const void *buf,
+static int quic_write_nonblocking_aon(QUIC_XSO *xso, const void *buf,
                                       size_t len, size_t *written)
 {
     const void *actual_buf;
     size_t actual_len, actual_written = 0;
     int accept_moving_buffer
-        = ((qc->ssl_mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) != 0);
+        = ((xso->ssl_mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) != 0);
 
-    if (qc->aon_write_in_progress) {
+    if (xso->aon_write_in_progress) {
         /*
          * We are in the middle of an AON write (i.e., a previous write did not
          * manage to append all data to the SSTREAM and we have Enable Partial
          * Write (EPW) mode disabled.)
          */
-        if ((!accept_moving_buffer && qc->aon_buf_base != buf)
-            || len != qc->aon_buf_len)
+        if ((!accept_moving_buffer && xso->aon_buf_base != buf)
+            || len != xso->aon_buf_len)
             /*
              * Pointer must not have changed if we are not in accept moving
              * buffer mode. Length must never change.
              */
-            return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_BAD_WRITE_RETRY, NULL);
+            return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, SSL_R_BAD_WRITE_RETRY, NULL);
 
-        actual_buf = (unsigned char *)buf + qc->aon_buf_pos;
-        actual_len = len - qc->aon_buf_pos;
+        actual_buf = (unsigned char *)buf + xso->aon_buf_pos;
+        actual_len = len - xso->aon_buf_pos;
         assert(actual_len > 0);
     } else {
         actual_buf = buf;
@@ -1191,26 +1270,26 @@ static int quic_write_nonblocking_aon(QUIC_CONNECTION *qc, const void *buf,
     }
 
     /* First make a best effort to append as much of the data as possible. */
-    if (!ossl_quic_sstream_append(qc->stream0->sstream, actual_buf, actual_len,
+    if (!ossl_quic_sstream_append(xso->stream->sstream, actual_buf, actual_len,
                                   &actual_written)) {
         /* Stream already finished or allocation error. */
         *written = 0;
-        return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
     }
 
-    quic_post_write(qc, actual_written > 0, 1);
+    quic_post_write(xso, actual_written > 0, 1);
 
     if (actual_written == actual_len) {
         /* We have sent everything. */
-        if (qc->aon_write_in_progress) {
+        if (xso->aon_write_in_progress) {
             /*
              * We have sent everything, and we were in the middle of an AON
              * write. The output write length is the total length of the AON
              * buffer, not however many bytes we managed to write to the stream
              * in this call.
              */
-            *written = qc->aon_buf_len;
-            aon_write_finish(qc);
+            *written = xso->aon_buf_len;
+            aon_write_finish(xso);
         } else {
             *written = actual_written;
         }
@@ -1218,16 +1297,16 @@ static int quic_write_nonblocking_aon(QUIC_CONNECTION *qc, const void *buf,
         return 1;
     }
 
-    if (qc->aon_write_in_progress) {
+    if (xso->aon_write_in_progress) {
         /*
          * AON write is in progress but we have not written everything yet. We
          * may have managed to send zero bytes, or some number of bytes less
          * than the total remaining which need to be appended during this
          * AON operation.
          */
-        qc->aon_buf_pos += actual_written;
-        assert(qc->aon_buf_pos < qc->aon_buf_len);
-        return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_WRITE);
+        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);
     }
 
     /*
@@ -1236,28 +1315,28 @@ static int quic_write_nonblocking_aon(QUIC_CONNECTION *qc, const void *buf,
      * actually append anything.
      */
     if (actual_written > 0)
-        aon_write_begin(qc, buf, len, actual_written);
+        aon_write_begin(xso, buf, len, actual_written);
 
     /*
      * AON - We do not publicly admit to having appended anything until AON
      * completes.
      */
     *written = 0;
-    return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_WRITE);
+    return QUIC_RAISE_NORMAL_ERROR(xso->conn, SSL_ERROR_WANT_WRITE);
 }
 
 QUIC_NEEDS_LOCK
-static int quic_write_nonblocking_epw(QUIC_CONNECTION *qc, const void *buf, size_t len,
+static int quic_write_nonblocking_epw(QUIC_XSO *xso, const void *buf, size_t len,
                                       size_t *written)
 {
     /* Simple best effort operation. */
-    if (!ossl_quic_sstream_append(qc->stream0->sstream, buf, len, written)) {
+    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(qc, ERR_R_INTERNAL_ERROR, NULL);
+        return QUIC_RAISE_NON_NORMAL_ERROR(xso->conn, ERR_R_INTERNAL_ERROR, NULL);
     }
 
-    quic_post_write(qc, *written > 0, 1);
+    quic_post_write(xso, *written > 0, 1);
     return 1;
 }
 
@@ -1270,12 +1349,12 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
 
     *written = 0;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_with_stream(s, &ctx))
         return 0;
 
     quic_lock(ctx.qc);
 
-    partial_write = ((ctx.qc->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0);
+    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);
@@ -1291,17 +1370,17 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
         goto out;
     }
 
-    if (ctx.qc->stream0 == NULL || ctx.qc->stream0->sstream == NULL) {
+    if (ctx.xso->stream == NULL || ctx.xso->stream->sstream == NULL) {
         ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
         goto out;
     }
 
-    if (blocking_mode(ctx.qc))
-        ret = quic_write_blocking(ctx.qc, buf, len, written);
+    if (xso_blocking_mode(ctx.xso))
+        ret = quic_write_blocking(ctx.xso, buf, len, written);
     else if (partial_write)
-        ret = quic_write_nonblocking_epw(ctx.qc, buf, len, written);
+        ret = quic_write_nonblocking_epw(ctx.xso, buf, len, written);
     else
-        ret = quic_write_nonblocking_aon(ctx.qc, buf, len, written);
+        ret = quic_write_nonblocking_aon(ctx.xso, buf, len, written);
 
 out:
     quic_unlock(ctx.qc);
@@ -1360,7 +1439,7 @@ static int quic_read_actual(QUIC_CONNECTION *qc,
 
             ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(qc->ch), &rtt_info);
 
-            if (!ossl_quic_rxfc_on_retire(&qc->stream0->rxfc, *bytes_read,
+            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);
         }
@@ -1370,7 +1449,7 @@ static int quic_read_actual(QUIC_CONNECTION *qc,
 
         if (*bytes_read > 0)
             ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch),
-                                              qc->stream0);
+                                              stream);
     }
 
     return 1;
@@ -1408,7 +1487,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
 
     *bytes_read = 0;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_with_stream(s, &ctx))
         return 0;
 
     quic_lock(ctx.qc);
@@ -1424,12 +1503,12 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
         goto out;
     }
 
-    if (ctx.qc->stream0 == NULL) {
+    if (ctx.xso->stream == NULL) {
         ret = QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, ERR_R_INTERNAL_ERROR, NULL);
         goto out;
     }
 
-    if (!quic_read_actual(ctx.qc, ctx.qc->stream0, buf, len, bytes_read, peek)) {
+    if (!quic_read_actual(ctx.qc, ctx.xso->stream, buf, len, bytes_read, peek)) {
         ret = 0; /* quic_read_actual raised error here */
         goto out;
     }
@@ -1441,14 +1520,14 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
          */
         ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(ctx.qc->ch), 0);
         ret = 1;
-    } else if (blocking_mode(ctx.qc)) {
+    } else if (xso_blocking_mode(ctx.xso)) {
         /*
          * We were not able to read anything immediately, so our stream
          * buffer is empty. This means we need to block until we get
          * at least one byte.
          */
         args.qc         = ctx.qc;
-        args.stream     = ctx.qc->stream0;
+        args.stream     = ctx.xso->stream;
         args.buf        = buf;
         args.len        = len;
         args.bytes_read = bytes_read;
@@ -1495,16 +1574,16 @@ static size_t ossl_quic_pending_int(const SSL *s)
     size_t avail = 0;
     int fin = 0;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_with_stream(s, &ctx))
         return 0;
 
     quic_lock(ctx.qc);
 
-    if (ctx.qc->stream0 == NULL || ctx.qc->stream0->rstream == NULL)
+    if (ctx.xso->stream == NULL || ctx.xso->stream->rstream == NULL)
         /* Cannot raise errors here because we are const, just fail. */
         goto out;
 
-    if (!ossl_quic_rstream_available(ctx.qc->stream0->rstream, &avail, &fin))
+    if (!ossl_quic_rstream_available(ctx.xso->stream->rstream, &avail, &fin))
         avail = 0;
 
 out:
@@ -1532,12 +1611,12 @@ int ossl_quic_conn_stream_conclude(SSL *s)
     QCTX ctx;
     QUIC_STREAM *qs;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_with_stream(s, &ctx))
         return 0;
 
     quic_lock(ctx.qc);
 
-    qs = ctx.qc->stream0;
+    qs = ctx.xso->stream;
 
     if (qs == NULL || qs->sstream == NULL) {
         quic_unlock(ctx.qc);
@@ -1551,7 +1630,7 @@ int ossl_quic_conn_stream_conclude(SSL *s)
     }
 
     ossl_quic_sstream_fin(qs->sstream);
-    quic_post_write(ctx.qc, 1, 1);
+    quic_post_write(ctx.xso, 1, 1);
     quic_unlock(ctx.qc);
     return 1;
 }
index 903e681008f8b51eecfcbc6d3800b56a5ed44f62..3906a20357e8bfe3716f52a3f40bd09dbc3557c5 100644 (file)
 
 # ifndef OPENSSL_NO_QUIC
 
+/*
+ * QUIC stream SSL object (QCSO) type. This implements the API personality layer
+ * for QSSO objects, wrapping the QUIC-native QUIC_STREAM object and tracking
+ * state required by the libssl API personality.
+ */
+struct quic_xso_st {
+    /* SSL object common header. */
+    struct ssl_st                   ssl;
+
+    /* The connection this stream is associated with. Always non-NULL. */
+    QUIC_CONNECTION                 *conn;
+
+    /* The stream object. Always non-NULL for as long as the XSO exists. */
+    QUIC_STREAM                     *stream;
+
+    /* Is this stream in blocking mode? */
+    unsigned int                    blocking                : 1;
+
+    /*
+     * This state tracks SSL_write all-or-nothing (AON) write semantics
+     * emulation.
+     *
+     * Example chronology:
+     *
+     *   t=0:  aon_write_in_progress=0
+     *   t=1:  SSL_write(ssl, b1, l1) called;
+     *         too big to enqueue into sstream at once, SSL_ERROR_WANT_WRITE;
+     *         aon_write_in_progress=1; aon_buf_base=b1; aon_buf_len=l1;
+     *         aon_buf_pos < l1 (depends on how much room was in sstream);
+     *   t=2:  SSL_write(ssl, b2, l2);
+     *         b2 must equal b1 (validated unless ACCEPT_MOVING_WRITE_BUFFER)
+     *         l2 must equal l1 (always validated)
+     *         append into sstream from [b2 + aon_buf_pos, b2 + aon_buf_len)
+     *         if done, aon_write_in_progess=0
+     *
+     */
+    /* Is an AON write in progress? */
+    unsigned int                    aon_write_in_progress   : 1;
+    /*
+     * The base buffer pointer the caller passed us for the initial AON write
+     * call. We use this for validation purposes unless
+     * ACCEPT_MOVING_WRITE_BUFFER is enabled.
+     *
+     * NOTE: We never dereference this, as the caller might pass a different
+     * (but identical) buffer if using ACCEPT_MOVING_WRITE_BUFFER. It is for
+     * validation by pointer comparison only.
+     */
+    const unsigned char             *aon_buf_base;
+    /* The total length of the AON buffer being sent, in bytes. */
+    size_t                          aon_buf_len;
+    /*
+     * The position in the AON buffer up to which we have successfully sent data
+     * so far.
+     */
+    size_t                          aon_buf_pos;
+
+    /* SSL_set_mode */
+    uint32_t                        ssl_mode;
+};
+
 struct quic_conn_st {
     /*
      * ssl_st is a common header for ordinary SSL objects, QUIC connection
@@ -57,8 +117,11 @@ struct quic_conn_st {
      */
     CRYPTO_MUTEX                    *mutex;
 
-    /* Our single bidirectional application data stream. */
-    QUIC_STREAM                     *stream0;
+    /*
+     * If we have a default stream attached, this is the internal XSO
+     * object. If there is no default stream, this is NULL.
+     */
+    QUIC_XSO                        *default_xso;
 
     /* The network read and write BIOs. */
     BIO                             *net_rbio, *net_wbio;
@@ -78,9 +141,6 @@ struct quic_conn_st {
     /* Have we started? */
     unsigned int                    started                 : 1;
 
-    /* Are we in blocking mode? */
-    unsigned int                    blocking                : 1;
-
     /* Can the read and write network BIOs support blocking? */
     unsigned int                    can_poll_net_rbio       : 1;
     unsigned int                    can_poll_net_wbio       : 1;
@@ -100,46 +160,14 @@ struct quic_conn_st {
     /* Are we using thread assisted mode? Never changes after init. */
     unsigned int                    is_thread_assisted      : 1;
 
-    /*
-     * This state tracks SSL_write all-or-nothing (AON) write semantics
-     * emulation.
-     *
-     * Example chronology:
-     *
-     *   t=0:  aon_write_in_progress=0
-     *   t=1:  SSL_write(ssl, b1, l1) called;
-     *         too big to enqueue into sstream at once, SSL_ERROR_WANT_WRITE;
-     *         aon_write_in_progress=1; aon_buf_base=b1; aon_buf_len=l1;
-     *         aon_buf_pos < l1 (depends on how much room was in sstream);
-     *   t=2:  SSL_write(ssl, b2, l2);
-     *         b2 must equal b1 (validated unless ACCEPT_MOVING_WRITE_BUFFER)
-     *         l2 must equal l1 (always validated)
-     *         append into sstream from [b2 + aon_buf_pos, b2 + aon_buf_len)
-     *         if done, aon_write_in_progess=0
-     *
-     */
-    /* Is an AON write in progress? */
-    unsigned int                    aon_write_in_progress   : 1;
-    /*
-     * The base buffer pointer the caller passed us for the initial AON write
-     * call. We use this for validation purposes unless
-     * ACCEPT_MOVING_WRITE_BUFFER is enabled.
-     *
-     * NOTE: We never dereference this, as the caller might pass a different
-     * (but identical) buffer if using ACCEPT_MOVING_WRITE_BUFFER. It is for
-     * validation by pointer comparison only.
-     */
-    const unsigned char             *aon_buf_base;
-    /* The total length of the AON buffer being sent, in bytes. */
-    size_t                          aon_buf_len;
-    /*
-     * The position in the AON buffer up to which we have successfully sent data
-     * so far.
-     */
-    size_t                          aon_buf_pos;
+    /* Do connection-level operations (e.g. handshakes) run in blocking mode? */
+    unsigned int                    blocking                : 1;
 
-    /* SSL_set_mode */
-    uint32_t                        ssl_mode;
+    /* Do newly created streams start in blocking mode? Inherited by new XSOs. */
+    unsigned int                    default_blocking        : 1;
+
+    /* SSL_set_mode. This is not used directly but inherited by new XSOs. */
+    uint32_t                        default_ssl_mode;
 
     /*
      * Last 'normal' error during an app-level I/O operation, used by
@@ -173,12 +201,14 @@ void ossl_quic_conn_on_remote_conn_close(QUIC_CONNECTION *qc,
          ? (c QUIC_CONNECTION *)(ssl)            \
          : NULL))
 
-#  define QUIC_XSO_FROM_SSL_int(ssl, c)          \
-     ((ssl) == NULL ? NULL                       \
-      : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \
-          || (ssl)->type == SSL_TYPE_QUIC_XSO    \
-         ? (c QUIC_XSO *)(ssl)                   \
-         : NULL))
+#  define QUIC_XSO_FROM_SSL_int(ssl, c)                             \
+    ((ssl) == NULL                                                  \
+     ? NULL                                                         \
+     : (((ssl)->type == SSL_TYPE_QUIC_XSO                           \
+        ? (c QUIC_XSO *)(ssl)                                       \
+        : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION                  \
+           ? (c QUIC_XSO *)((QUIC_CONNECTION *)(ssl))->default_xso  \
+           : NULL))))
 
 #  define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c)               \
      ((ssl) == NULL ? NULL                                       \
index 8efd009cef2d3f7fd632cadbf8fbe7862e12dce3..56cbbac5f89d5d9f7d774683c9a5341e6632fcf3 100644 (file)
@@ -7299,6 +7299,18 @@ int SSL_stream_conclude(SSL *ssl, uint64_t flags)
 #endif
 }
 
+SSL *SSL_new_stream(SSL *s, uint64_t flags)
+{
+#ifndef OPENSSL_NO_QUIC
+    if (!IS_QUIC(s))
+        return NULL;
+
+    return ossl_quic_conn_stream_new(s, flags);
+#else
+    return NULL;
+#endif
+}
+
 int SSL_add_expected_rpk(SSL *s, EVP_PKEY *rpk)
 {
     unsigned char *data = NULL;
index 6e60aa8e9383c9ca0f3989002335ec890ea8c34a..635835666319ba7e357c89d8b7b95b247b8c50d1 100644 (file)
@@ -560,3 +560,4 @@ SSL_add_expected_rpk                    ?   3_2_0   EXIST::FUNCTION:
 d2i_SSL_SESSION_ex                      ?      3_2_0   EXIST::FUNCTION:
 SSL_is_tls                              ?      3_2_0   EXIST::FUNCTION:
 SSL_is_quic                             ?      3_2_0   EXIST::FUNCTION:
+SSL_new_stream                          ?      3_2_0   EXIST::FUNCTION: