]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC APL: Implement base listener API, move addressing mode handling into PORT
authorHugo Landau <hlandau@openssl.org>
Thu, 11 Jan 2024 13:20:05 +0000 (13:20 +0000)
committerViktor Dukhovni <openssl-users@dukhovni.org>
Wed, 11 Sep 2024 07:32:29 +0000 (17:32 +1000)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23334)

12 files changed:
include/internal/quic_channel.h
include/internal/quic_port.h
include/internal/ssl_unwrap.h
include/openssl/quic.h
ssl/quic/quic_channel.c
ssl/quic/quic_channel_local.h
ssl/quic/quic_engine.c
ssl/quic/quic_impl.c
ssl/quic/quic_local.h
ssl/quic/quic_method.c
ssl/quic/quic_port.c
ssl/quic/quic_port_local.h

index 3b373ab680815efe297d4417113f29e021f8de67..2a741f52cf3271c937099d1930362a495219408c 100644 (file)
@@ -301,6 +301,9 @@ QUIC_STREAM_MAP *ossl_quic_channel_get_qsm(QUIC_CHANNEL *ch);
 /* Gets the statistics manager used with the channel. */
 OSSL_STATM *ossl_quic_channel_get_statm(QUIC_CHANNEL *ch);
 
+/* Gets the TLS handshake layer used with the channel. */
+SSL *ossl_quic_channel_get0_tls(QUIC_CHANNEL *ch);
+
 /*
  * Gets/sets the current peer address. Generally this should be used before
  * starting a channel in client mode.
index bcb578c3f8d21efbd5f03e1b60f9650fa41ff946..eb060581e017a711c5e8d6e7b31608d04da40097 100644 (file)
@@ -76,6 +76,12 @@ QUIC_CHANNEL *ossl_quic_port_create_outgoing(QUIC_PORT *port, SSL *tls);
  */
 QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls);
 
+/*
+ * Pop an incoming channel from the incoming channel queue. Returns NULL if
+ * there are no pending incoming channels.
+ */
+QUIC_CHANNEL *ossl_quic_port_pop_incoming(QUIC_PORT *port);
+
 /*
  * Queries and Accessors
  * =====================
@@ -124,6 +130,27 @@ void ossl_quic_port_restore_err_state(const QUIC_PORT *port);
 void ossl_quic_port_subtick(QUIC_PORT *port, QUIC_TICK_RESULT *r,
                             uint32_t flags);
 
+/* Returns the number of queued incoming channels. */
+size_t ossl_quic_port_get_num_incoming_channels(const QUIC_PORT *port);
+
+/* Returns 1 if incoming connections should currently be allowed. */
+void ossl_quic_port_set_allow_incoming(QUIC_PORT *port, int allow_incoming);
+
+/* Returns 1 if we are using addressed mode on the read side. */
+int ossl_quic_port_is_addressed_r(const QUIC_PORT *port);
+
+/* Returns 1 if we are using addressed mode on the write side. */
+int ossl_quic_port_is_addressed_w(const QUIC_PORT *port);
+
+/* Returns 1 if we are using addressed mode. */
+int ossl_quic_port_is_addressed(const QUIC_PORT *port);
+
+/*
+ * Returns the current network BIO epoch. This increments whenever the network
+ * BIO configuration changes.
+ */
+uint64_t ossl_quic_port_get_net_bio_epoch(const QUIC_PORT *port);
+
 /*
  * Events
  * ======
index c9a131f1d7fe5fb4775f8d87895c696800a1e2a3..9385e5d52c024e49e79efaffc4c83c5c58e36ddc 100644 (file)
@@ -49,7 +49,8 @@ struct ssl_connection_st *ossl_quic_obj_get0_handshake_layer(QUIC_OBJ *obj);
 
 #  define IS_QUIC_METHOD(m) \
     ((m) == OSSL_QUIC_client_method() || \
-     (m) == OSSL_QUIC_client_thread_method())
+     (m) == OSSL_QUIC_client_thread_method() || \
+     (m) == OSSL_QUIC_server_method())
 
 #  define IS_QUIC_CTX(ctx)          IS_QUIC_METHOD((ctx)->method)
 
index 3dc2f5e747b42bcddb3da10e63ef241e6e0a42c3..318e356eae0463a7a4f0ca12a30344e4573cf138 100644 (file)
@@ -62,6 +62,11 @@ __owur const SSL_METHOD *OSSL_QUIC_client_thread_method(void);
 #  define OSSL_QUIC_LOCAL_ERR_IDLE_TIMEOUT        \
     ((uint64_t)0xFFFFFFFFFFFFFFFFULL)
 
+/*
+ * Method used for QUIC server operation.
+ */
+__owur const SSL_METHOD *OSSL_QUIC_server_method(void);
+
 #  ifdef __cplusplus
 }
 #  endif
index 7c9fa2f81afdb86e2442a228bedbb4d20bbb06e8..2daf112f08b4483009c3b20bda25bed18ad4219d 100644 (file)
@@ -512,6 +512,11 @@ OSSL_STATM *ossl_quic_channel_get_statm(QUIC_CHANNEL *ch)
     return &ch->statm;
 }
 
+SSL *ossl_quic_channel_get0_tls(QUIC_CHANNEL *ch)
+{
+    return ch->tls;
+}
+
 QUIC_STREAM *ossl_quic_channel_get_stream_by_id(QUIC_CHANNEL *ch,
                                                 uint64_t stream_id)
 {
index 16d96ef7d995a3966aeeffc40808a663d99acba5..7cafa109c4998d7e7ad621cf0edbe939dc90278a 100644 (file)
@@ -34,7 +34,8 @@ struct quic_channel_st {
      * QUIC_PORT keeps the channels which belong to it on a list for bookkeeping
      * purposes.
      */
-    OSSL_LIST_MEMBER(ch, struct quic_channel_st);
+    OSSL_LIST_MEMBER(ch,            QUIC_CHANNEL);
+    OSSL_LIST_MEMBER(incoming_ch,   QUIC_CHANNEL);
 
     /*
      * The associated TLS 1.3 connection data. Used to provide the handshake
index 46c45b154bd7469ca8cca9f7ee20053b74005bd3..932ac2a7f047b30d6c5cc313d4b2640493bbaa63 100644 (file)
@@ -120,7 +120,7 @@ QUIC_PORT *ossl_quic_engine_create_port(QUIC_ENGINE *qeng,
 
 /*
  * QUIC Engine: Ticker-Mutator
- * ==========================
+ * ===========================
  */
 
 /*
index 338667511796cdd15f9df2e732f3b0e72062ee3d..3efd820219a759e73365573cacdf957f9bb16183 100644 (file)
@@ -26,6 +26,7 @@ static void qc_cleanup(QUIC_CONNECTION *qc, int have_lock);
 static void aon_write_finish(QUIC_XSO *xso);
 static int create_channel(QUIC_CONNECTION *qc, SSL_CTX *ctx);
 static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs);
+static QUIC_CONNECTION *create_qc_from_incoming_conn(QUIC_LISTENER *ql, QUIC_CHANNEL *ch);
 static int qc_try_create_default_xso_for_write(QCTX *ctx);
 static int qc_wait_for_default_xso_for_read(QCTX *ctx, int peek);
 static void qctx_lock(QCTX *qctx);
@@ -494,6 +495,7 @@ SSL *ossl_quic_new(SSL_CTX *ctx)
     qc->blocking                = 0;
     qc->incoming_stream_policy  = SSL_INCOMING_STREAM_POLICY_AUTO;
     qc->last_error              = SSL_ERROR_NONE;
+    qc->last_net_bio_epoch      = UINT64_MAX;
 
     qc_update_reject_policy(qc);
 
@@ -529,12 +531,6 @@ static void qc_cleanup(QUIC_CONNECTION *qc, int have_lock)
     ossl_quic_engine_free(qc->engine);
     qc->engine = NULL;
 
-    BIO_free_all(qc->net_rbio);
-    qc->net_rbio = NULL;
-
-    BIO_free_all(qc->net_wbio);
-    qc->net_wbio = NULL;
-
     SSL_free(qc->tls);
     qc->tls = NULL;
 
@@ -942,78 +938,96 @@ static void qc_update_blocking_mode(QUIC_CONNECTION *qc)
     qc->blocking = qc->desires_blocking && qc_can_support_blocking_cached(qc);
 }
 
-void ossl_quic_conn_set0_net_rbio(SSL *s, BIO *net_rbio)
+static int
+quic_set0_net_rbio(QUIC_OBJ *obj, BIO *net_rbio)
 {
-    QCTX ctx;
-
-    if (!expect_quic(s, &ctx))
-        return;
+    QUIC_PORT *port;
+    BIO *old_rbio = NULL;
 
-    if (ctx.qc->net_rbio == net_rbio)
-        return;
-
-    if (!ossl_quic_port_set_net_rbio(ctx.qc->port, net_rbio))
-        return;
+    port = ossl_quic_obj_get0_port(obj);
+    old_rbio = ossl_quic_port_get_net_rbio(port);
+    if (old_rbio == net_rbio)
+        return 0;
 
-    BIO_free_all(ctx.qc->net_rbio);
-    ctx.qc->net_rbio = net_rbio;
+    if (!ossl_quic_port_set_net_rbio(port, net_rbio))
+        return 0;
 
+    BIO_free_all(old_rbio);
     if (net_rbio != NULL)
         BIO_set_nbio(net_rbio, 1); /* best effort autoconfig */
 
-    /*
-     * Determine if the current pair of read/write BIOs now set allows blocking
-     * mode to be supported.
-     */
-    qc_update_can_support_blocking(ctx.qc);
-    qc_update_blocking_mode(ctx.qc);
+    return 1;
 }
 
-void ossl_quic_conn_set0_net_wbio(SSL *s, BIO *net_wbio)
+static int
+quic_set0_net_wbio(QUIC_OBJ *obj, BIO *net_wbio)
 {
-    QCTX ctx;
+    QUIC_PORT *port;
+    BIO *old_wbio = NULL;
 
-    if (!expect_quic(s, &ctx))
-        return;
+    port = ossl_quic_obj_get0_port(obj);
+    old_wbio = ossl_quic_port_get_net_wbio(port);
+    if (old_wbio == net_wbio)
+        return 0;
+
+    if (!ossl_quic_port_set_net_wbio(port, net_wbio))
+        return 0;
+
+    BIO_free_all(old_wbio);
+    if (net_wbio != NULL)
+        BIO_set_nbio(net_wbio, 1); /* best effort autoconfig */
 
-    if (ctx.qc->net_wbio == net_wbio)
+    return 1;
+}
+
+void ossl_quic_conn_set0_net_rbio(SSL *s, BIO *net_rbio)
+{
+    QCTX ctx;
+
+    if (!expect_quic_any(s, &ctx))
         return;
 
-    if (!ossl_quic_port_set_net_wbio(ctx.qc->port, net_wbio))
+    /* Returns 0 if no change. */
+    if (!quic_set0_net_rbio(ctx.obj, net_rbio))
         return;
+}
 
-    BIO_free_all(ctx.qc->net_wbio);
-    ctx.qc->net_wbio = net_wbio;
+void ossl_quic_conn_set0_net_wbio(SSL *s, BIO *net_wbio)
+{
+    QCTX ctx;
 
-    if (net_wbio != NULL)
-        BIO_set_nbio(net_wbio, 1); /* best effort autoconfig */
+    if (!expect_quic_any(s, &ctx))
+        return;
 
-    /*
-     * Determine if the current pair of read/write BIOs now set allows blocking
-     * mode to be supported.
-     */
-    qc_update_can_support_blocking(ctx.qc);
-    qc_update_blocking_mode(ctx.qc);
+    /* Returns 0 if no change. */
+    if (!quic_set0_net_wbio(ctx.obj, net_wbio))
+        return;
 }
 
 BIO *ossl_quic_conn_get_net_rbio(const SSL *s)
 {
     QCTX ctx;
+    QUIC_PORT *port;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_any(s, &ctx))
         return NULL;
 
-    return ctx.qc->net_rbio;
+    port = ossl_quic_obj_get0_port(ctx.obj);
+    assert(port != NULL);
+    return ossl_quic_port_get_net_rbio(port);
 }
 
 BIO *ossl_quic_conn_get_net_wbio(const SSL *s)
 {
     QCTX ctx;
+    QUIC_PORT *port;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_any(s, &ctx))
         return NULL;
 
-    return ctx.qc->net_wbio;
+    port = ossl_quic_obj_get0_port(ctx.obj);
+    assert(port != NULL);
+    return ossl_quic_port_get_net_wbio(port);
 }
 
 int ossl_quic_conn_get_blocking_mode(const SSL *s)
@@ -1109,10 +1123,23 @@ int ossl_quic_conn_set_initial_peer_addr(SSL *s,
  *   (BIO/)SSL_get_poll_fd          => ossl_quic_get_poll_fd
  *
  */
+static void qc_try_update_blocking(QUIC_CONNECTION *qc)
+{
+    uint64_t cur_epoch;
+
+    cur_epoch = ossl_quic_port_get_net_bio_epoch(qc->port);
+    if (qc->last_net_bio_epoch == cur_epoch)
+        return;
+
+    qc_update_can_support_blocking(qc);
+    qc_update_blocking_mode(qc);
+    qc->last_net_bio_epoch = cur_epoch;
+}
 
 /* Returns 1 if the connection is being used in blocking mode. */
 static int qc_blocking_mode(const QUIC_CONNECTION *qc)
 {
+    qc_try_update_blocking((QUIC_CONNECTION *)qc);
     return qc->blocking;
 }
 
@@ -1187,30 +1214,38 @@ int ossl_quic_get_event_timeout(SSL *s, struct timeval *tv, int *is_infinite)
 int ossl_quic_get_rpoll_descriptor(SSL *s, BIO_POLL_DESCRIPTOR *desc)
 {
     QCTX ctx;
+    QUIC_PORT *port = NULL;
+    BIO *net_rbio;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_any(s, &ctx))
         return 0;
 
-    if (desc == NULL || ctx.qc->net_rbio == NULL)
+    port = ossl_quic_obj_get0_port(ctx.obj);
+    net_rbio = ossl_quic_port_get_net_rbio(port);
+    if (desc == NULL || net_rbio == NULL)
         return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT,
                                        NULL);
 
-    return BIO_get_rpoll_descriptor(ctx.qc->net_rbio, desc);
+    return BIO_get_rpoll_descriptor(net_rbio, desc);
 }
 
 /* SSL_get_wpoll_descriptor */
 int ossl_quic_get_wpoll_descriptor(SSL *s, BIO_POLL_DESCRIPTOR *desc)
 {
     QCTX ctx;
+    QUIC_PORT *port = NULL;
+    BIO *net_wbio;
 
-    if (!expect_quic(s, &ctx))
+    if (!expect_quic_any(s, &ctx))
         return 0;
 
-    if (desc == NULL || ctx.qc->net_wbio == NULL)
+    port = ossl_quic_obj_get0_port(ctx.obj);
+    net_wbio = ossl_quic_port_get_net_wbio(port);
+    if (desc == NULL || net_wbio == NULL)
         return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT,
                                        NULL);
 
-    return BIO_get_wpoll_descriptor(ctx.qc->net_wbio, desc);
+    return BIO_get_wpoll_descriptor(net_wbio, desc);
 }
 
 /* SSL_net_read_desired */
@@ -1558,9 +1593,7 @@ static int configure_channel(QUIC_CONNECTION *qc)
 {
     assert(qc->ch != NULL);
 
-    if (!ossl_quic_port_set_net_rbio(qc->port, qc->net_rbio)
-        || !ossl_quic_port_set_net_wbio(qc->port, qc->net_wbio)
-        || !ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr))
+    if (!ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr))
         return 0;
 
     return 1;
@@ -1646,6 +1679,8 @@ static int quic_do_handshake(QCTX *ctx)
 {
     int ret;
     QUIC_CONNECTION *qc = ctx->qc;
+    QUIC_PORT *port;
+    BIO *net_rbio, *net_wbio;
 
     if (ossl_quic_channel_is_handshake_complete(qc->ch))
         /* Handshake already completed. */
@@ -1659,55 +1694,16 @@ static int quic_do_handshake(QCTX *ctx)
         return -1; /* Non-protocol error */
     }
 
-    if (qc->net_rbio == NULL || qc->net_wbio == NULL) {
+    port = ossl_quic_obj_get0_port(ctx->obj);
+    net_rbio = ossl_quic_port_get_net_rbio(port);
+    net_wbio = ossl_quic_port_get_net_wbio(port);
+    if (net_rbio == NULL || net_wbio == NULL) {
         /* Need read and write BIOs. */
         QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_BIO_NOT_SET, NULL);
         return -1; /* Non-protocol error */
     }
 
-    /*
-     * We need to determine our addressing mode. There are basically two
-     * ways we can use L4 addresses:
-     *
-     *   - Addressed mode, in which our BIO_sendmmsg calls have destination
-     *     addresses attached to them which we expect the underlying network BIO
-     *     to handle;
-     *
-     *   - Unaddressed mode, in which the BIO provided to us on the
-     *     network side neither provides us with L4 addresses nor is capable of
-     *     honouring ones we provide. We don't know where the QUIC traffic we
-     *     send ends up exactly and trust the application to know what it is
-     *     doing.
-     *
-     * Addressed mode is preferred because it enables support for connection
-     * migration, multipath, etc. in the future. Addressed mode is automatically
-     * enabled if we are using e.g. BIO_s_datagram, with or without
-     * BIO_s_connect.
-     *
-     * If we are passed a BIO_s_dgram_pair (or some custom BIO) we may have to
-     * use unaddressed mode unless that BIO supports capability flags indicating
-     * it can provide and honour L4 addresses.
-     *
-     * Our strategy for determining address mode is simple: we probe the
-     * underlying network BIOs for their capabilities. If the network BIOs
-     * support what we need, we use addressed mode. Otherwise, we use
-     * unaddressed mode.
-     *
-     * If addressed mode is chosen, we require an initial peer address to be
-     * set. If this is not set, we fail. If unaddressed mode is used, we do not
-     * require this, as such an address is superfluous, though it can be set if
-     * desired.
-     */
-    if (!qc->started && !qc->addressing_probe_done) {
-        long rcaps = BIO_dgram_get_effective_caps(qc->net_rbio);
-        long wcaps = BIO_dgram_get_effective_caps(qc->net_wbio);
-
-        qc->addressed_mode_r = ((rcaps & BIO_DGRAM_CAP_PROVIDES_SRC_ADDR) != 0);
-        qc->addressed_mode_w = ((wcaps & BIO_DGRAM_CAP_HANDLES_DST_ADDR) != 0);
-        qc->addressing_probe_done = 1;
-    }
-
-    if (!qc->started && qc->addressed_mode_w
+    if (!qc->started && ossl_quic_port_is_addressed_w(port)
         && BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
         /*
          * We are trying to connect and are using addressed mode, which means we
@@ -1721,7 +1717,7 @@ static int quic_do_handshake(QCTX *ctx)
          * (e.g. with setter calls), which might happen after SSL_set_bio is
          * called.
          */
-        if (!csm_analyse_init_peer_addr(qc->net_wbio, &qc->init_peer_addr))
+        if (!csm_analyse_init_peer_addr(net_wbio, &qc->init_peer_addr))
             /* best effort */
             BIO_ADDR_clear(&qc->init_peer_addr);
         else
@@ -1729,7 +1725,7 @@ static int quic_do_handshake(QCTX *ctx)
     }
 
     if (!qc->started
-        && qc->addressed_mode_w
+        && ossl_quic_port_is_addressed_w(port)
         && BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
         /*
          * If we still don't have a peer address in addressed mode, we can't do
@@ -4017,17 +4013,17 @@ SSL *ossl_quic_new_listener(SSL_CTX *ctx, uint64_t flags)
     QUIC_ENGINE_ARGS engine_args = {0};
     QUIC_PORT_ARGS port_args = {0};
 
-#if defined(OPENSSL_THREADS)
-    if ((ql->mutex = ossl_crypto_mutex_new()) == NULL) {
+    if ((ql = OPENSSL_zalloc(sizeof(*ql))) == NULL) {
         QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL);
         goto err;
     }
-#endif
 
-    if ((ql = OPENSSL_zalloc(sizeof(*ql))) == NULL) {
+#if defined(OPENSSL_THREADS)
+    if ((ql->mutex = ossl_crypto_mutex_new()) == NULL) {
         QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL);
         goto err;
     }
+#endif
 
     engine_args.libctx  = ctx->libctx;
     engine_args.propq   = ctx->propq;
@@ -4037,13 +4033,16 @@ SSL *ossl_quic_new_listener(SSL_CTX *ctx, uint64_t flags)
         goto err;
     }
 
-    port_args.channel_ctx = ctx;
+    port_args.channel_ctx       = ctx;
+    port_args.is_multi_conn     = 1;
     ql->port = ossl_quic_engine_create_port(ql->engine, &port_args);
     if (ql->port == NULL) {
         QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
         goto err;
     }
 
+    ossl_quic_port_set_allow_incoming(ql->port, 1);
+
     /* Initialise the QUIC_LISTENER'S object header. */
     if (!ossl_quic_obj_init(&ql->obj, ctx, SSL_TYPE_QUIC_LISTENER, NULL,
                             ql->engine, ql->port))
@@ -4059,19 +4058,138 @@ err:
     return NULL;
 }
 
+/*
+ * SSL_listen
+ * ----------
+ */
+QUIC_NEEDS_LOCK
+static int ql_listen(QUIC_LISTENER *ql)
+{
+    if (ql->listening)
+        return 1;
+
+    ql->listening = 1;
+    return 1;
+}
+
+QUIC_TAKES_LOCK
+int ossl_quic_listen(SSL *ssl)
+{
+    QCTX ctx;
+    int ret;
+
+    if (!expect_quic_listener(ssl, &ctx))
+        return 0;
+
+    qctx_lock(&ctx);
+
+    ret = ql_listen(ctx.ql);
+
+    qctx_unlock(&ctx);
+    return ret;
+}
+
+/*
+ * SSL_accept_connection
+ * ---------------------
+ */
+QUIC_TAKES_LOCK
 SSL *ossl_quic_accept_connection(SSL *ssl, uint64_t flags)
 {
-    return NULL; // TODO XXX
+    QCTX ctx;
+    QUIC_CONNECTION *qc = NULL;
+    QUIC_CHANNEL *new_ch = NULL;
+
+    if (!expect_quic_listener(ssl, &ctx))
+        return NULL;
+
+    qctx_lock(&ctx);
+
+    if (!ql_listen(ctx.ql))
+        goto out;
+
+    /* TODO autotick/block */
+
+    new_ch = ossl_quic_port_pop_incoming(ctx.ql->port);
+    if (new_ch == NULL) {
+        /* No connections already queued. */
+        ossl_quic_reactor_tick(ossl_quic_engine_get0_reactor(ctx.ql->engine), 0);
+
+        new_ch = ossl_quic_port_pop_incoming(ctx.ql->port);
+        if (new_ch == NULL)
+            goto out;
+    }
+
+    qc = create_qc_from_incoming_conn(ctx.ql, new_ch);
+    if (qc == NULL) {
+        ossl_quic_channel_free(new_ch);
+        goto out;
+    }
+
+out:
+    qctx_unlock(&ctx);
+    return qc != NULL ? &qc->obj.ssl : NULL;
 }
 
-size_t ossl_quic_get_accept_connection_queue_len(SSL *ssl)
+static QUIC_CONNECTION *create_qc_from_incoming_conn(QUIC_LISTENER *ql, QUIC_CHANNEL *ch)
 {
-    return 0; // TODO XXX
+    QUIC_CONNECTION *qc = NULL;
+
+    if ((qc = OPENSSL_zalloc(sizeof(*qc))) == NULL) {
+        QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL);
+        goto err;
+    }
+
+    if (!ossl_quic_obj_init(&qc->obj, ql->obj.ssl.ctx,
+                            SSL_TYPE_QUIC_CONNECTION,
+                            &ql->obj.ssl, NULL, NULL)) {
+        QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
+        goto err;
+    }
+
+    ossl_quic_channel_get_peer_addr(ch, &qc->init_peer_addr); /* best effort */
+    qc->listener                = ql;
+    qc->engine                  = ql->engine;
+    qc->port                    = ql->port;
+    qc->ch                      = ch;
+    qc->mutex                   = ql->mutex;
+    qc->tls                     = ossl_quic_channel_get0_tls(ch);
+    qc->last_net_bio_epoch      = UINT64_MAX;
+    qc->started                 = 1;
+    qc->as_server               = 1;
+    qc->as_server_state         = 1;
+    qc->desires_blocking        = 1;
+    qc->default_stream_mode     = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
+    qc->default_ssl_options     = ql->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
+    qc->incoming_stream_policy  = SSL_INCOMING_STREAM_POLICY_AUTO;
+    qc->last_error              = SSL_ERROR_NONE;
+    qc_update_reject_policy(qc);
+    return qc;
+
+err:
+    OPENSSL_free(qc);
+    return NULL;
 }
 
-int ossl_quic_listen(SSL *ssl)
+/*
+ * SSL_get_accept_connection_queue_len
+ * -----------------------------------
+ */
+QUIC_TAKES_LOCK
+size_t ossl_quic_get_accept_connection_queue_len(SSL *ssl)
 {
-    return 0; // TODO XXX
+    QCTX ctx;
+    int ret;
+
+    if (!expect_quic_listener(ssl, &ctx))
+        return 0;
+
+    qctx_lock(&ctx);
+
+    ret = ossl_quic_port_get_num_incoming_channels(ctx.ql->port);
+
+    qctx_unlock(&ctx);
+    return ret;
 }
 
 /*
index 2c3bd73f1455852f5e2711dda28c6be109e63793..059251506e49c2b564905ea09f4e19cee1376951 100644 (file)
@@ -170,9 +170,6 @@ struct quic_conn_st {
      */
     QUIC_XSO                        *default_xso;
 
-    /* The network read and write BIOs. */
-    BIO                             *net_rbio, *net_wbio;
-
     /* Initial peer L4 address. */
     BIO_ADDR                        init_peer_addr;
 
@@ -245,6 +242,11 @@ struct quic_conn_st {
     int                             incoming_stream_policy;
     uint64_t                        incoming_stream_aec;
 
+    /*
+     * Last network BIO epoch at which blocking mode compatibility was checked.
+     */
+    uint64_t                        last_net_bio_epoch;
+
     /*
      * 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
@@ -272,6 +274,9 @@ struct quic_listener_st {
      * provide it to the engine.
      */
     CRYPTO_MUTEX                    *mutex;
+
+    /* Have we started listening yet? */
+    unsigned int                    listening               : 1;
 };
 
 /* Internal calls to the QUIC CSM which come from various places. */
index 2882a40f3f482a63a627c741ee8e5e112e53f7ee..7c86bcd77741573310216a7b0ef6838813855807 100644 (file)
@@ -20,3 +20,8 @@ IMPLEMENT_quic_meth_func(OSSL_QUIC_ANY_VERSION,
                          OSSL_QUIC_client_thread_method,
                          ssl_undefined_function,
                          ossl_quic_connect, ssl3_undef_enc_method)
+
+IMPLEMENT_quic_meth_func(OSSL_QUIC_ANY_VERSION,
+                         OSSL_QUIC_server_method,
+                         ssl_undefined_function,
+                         ossl_quic_connect, ssl3_undef_enc_method)
index c26164c9c258ffdb36c4ea12d6b6221f9ea424cc..ad82ba6d989e06b74fa6ea1f09214fddf2156296 100644 (file)
@@ -31,6 +31,7 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
 static void port_rx_pre(QUIC_PORT *port);
 
 DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
+DEFINE_LIST_OF_IMPL(incoming_ch, QUIC_CHANNEL);
 DEFINE_LIST_OF_IMPL(port, QUIC_PORT);
 
 QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args)
@@ -176,6 +177,11 @@ int ossl_quic_port_get_tx_init_dcid_len(const QUIC_PORT *port)
     return port->tx_init_dcid_len;
 }
 
+size_t ossl_quic_port_get_num_incoming_channels(const QUIC_PORT *port)
+{
+    return ossl_list_incoming_ch_num(&port->incoming_channel_list);
+}
+
 /*
  * QUIC Port: Network BIO Configuration
  * ====================================
@@ -245,6 +251,65 @@ int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port)
     return ok;
 }
 
+/*
+ * We need to determine our addressing mode. There are basically two ways we can
+ * use L4 addresses:
+ *
+ *   - Addressed mode, in which our BIO_sendmmsg calls have destination
+ *     addresses attached to them which we expect the underlying network BIO to
+ *     handle;
+ *
+ *   - Unaddressed mode, in which the BIO provided to us on the network side
+ *     neither provides us with L4 addresses nor is capable of honouring ones we
+ *     provide. We don't know where the QUIC traffic we send ends up exactly and
+ *     trust the application to know what it is doing.
+ *
+ * Addressed mode is preferred because it enables support for connection
+ * migration, multipath, etc. in the future. Addressed mode is automatically
+ * enabled if we are using e.g. BIO_s_datagram, with or without BIO_s_connect.
+ *
+ * If we are passed a BIO_s_dgram_pair (or some custom BIO) we may have to use
+ * unaddressed mode unless that BIO supports capability flags indicating it can
+ * provide and honour L4 addresses.
+ *
+ * Our strategy for determining address mode is simple: we probe the underlying
+ * network BIOs for their capabilities. If the network BIOs support what we
+ * need, we use addressed mode. Otherwise, we use unaddressed mode.
+ *
+ * If addressed mode is chosen, we require an initial peer address to be set. If
+ * this is not set, we fail. If unaddressed mode is used, we do not require
+ * this, as such an address is superfluous, though it can be set if desired.
+ */
+static void port_update_addressing_mode(QUIC_PORT *port)
+{
+    long rcaps = 0, wcaps = 0;
+
+    if (port->net_rbio != NULL)
+        rcaps = BIO_dgram_get_effective_caps(port->net_rbio);
+
+    if (port->net_wbio != NULL)
+        wcaps = BIO_dgram_get_effective_caps(port->net_wbio);
+
+    port->addressed_mode_r = ((rcaps & BIO_DGRAM_CAP_PROVIDES_SRC_ADDR) != 0);
+    port->addressed_mode_w = ((wcaps & BIO_DGRAM_CAP_HANDLES_DST_ADDR) != 0);
+    ++port->net_bio_epoch;
+}
+
+int ossl_quic_port_is_addressed_r(const QUIC_PORT *port)
+{
+    return port->addressed_mode_r;
+}
+
+int ossl_quic_port_is_addressed_w(const QUIC_PORT *port)
+{
+    return port->addressed_mode_w;
+}
+
+int ossl_quic_port_is_addressed(const QUIC_PORT *port)
+{
+    return ossl_quic_port_is_addressed_r(port) && ossl_quic_port_is_addressed_w(port);
+}
+
 /*
  * QUIC_PORT does not ref any BIO it is provided with, nor is any ref
  * transferred to it. The caller (e.g., QUIC_CONNECTION) is responsible for
@@ -261,6 +326,7 @@ int ossl_quic_port_set_net_rbio(QUIC_PORT *port, BIO *net_rbio)
 
     ossl_quic_demux_set_bio(port->demux, net_rbio);
     port->net_rbio = net_rbio;
+    port_update_addressing_mode(port);
     return 1;
 }
 
@@ -278,9 +344,15 @@ int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio)
         ossl_qtx_set_bio(ch->qtx, net_wbio);
 
     port->net_wbio = net_wbio;
+    port_update_addressing_mode(port);
     return 1;
 }
 
+uint64_t ossl_quic_port_get_net_bio_epoch(const QUIC_PORT *port)
+{
+    return port->net_bio_epoch;
+}
+
 /*
  * QUIC Port: Channel Lifecycle
  * ============================
@@ -330,6 +402,7 @@ static QUIC_CHANNEL *port_make_channel(QUIC_PORT *port, SSL *tls, int is_server)
         return NULL;
     }
 
+    ossl_qtx_set_bio(ch->qtx, port->net_wbio);
     return ch;
 }
 
@@ -346,10 +419,27 @@ QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls)
 
     ch = port_make_channel(port, tls, /*is_server=*/1);
     port->tserver_ch = ch;
-    port->is_server  = 1;
+    port->allow_incoming = 1;
+    return ch;
+}
+
+QUIC_CHANNEL *ossl_quic_port_pop_incoming(QUIC_PORT *port)
+{
+    QUIC_CHANNEL *ch;
+
+    ch = ossl_list_incoming_ch_head(&port->incoming_channel_list);
+    if (ch == NULL)
+        return NULL;
+
+    ossl_list_incoming_ch_remove(&port->incoming_channel_list, ch);
     return ch;
 }
 
+void ossl_quic_port_set_allow_incoming(QUIC_PORT *port, int allow_incoming)
+{
+    port->allow_incoming = allow_incoming;
+}
+
 /*
  * QUIC Port: Ticker-Mutator
  * =========================
@@ -404,7 +494,7 @@ static void port_rx_pre(QUIC_PORT *port)
      * Therefore, this check is essential as we do not require our API users to
      * bind a socket first when using the API in client mode.
      */
-    if (!port->is_server && !port->have_sent_any_pkt)
+    if (!port->allow_incoming && !port->have_sent_any_pkt)
         return;
 
     /*
@@ -432,6 +522,8 @@ static void port_on_new_conn(QUIC_PORT *port, const BIO_ADDR *peer,
                              const QUIC_CONN_ID *dcid,
                              QUIC_CHANNEL **new_ch)
 {
+    QUIC_CHANNEL *ch;
+
     if (port->tserver_ch != NULL) {
         /* Specially assign to existing channel */
         if (!ossl_quic_channel_on_new_conn(port->tserver_ch, peer, scid, dcid))
@@ -441,6 +533,18 @@ static void port_on_new_conn(QUIC_PORT *port, const BIO_ADDR *peer,
         port->tserver_ch = NULL;
         return;
     }
+
+    ch = port_make_channel(port, NULL, /*is_server=*/1);
+    if (ch == NULL)
+        return;
+
+    if (!ossl_quic_channel_on_new_conn(ch, peer, scid, dcid)) {
+        ossl_quic_channel_free(ch);
+        return;
+    }
+
+    ossl_list_incoming_ch_insert_tail(&port->incoming_channel_list, ch);
+    *new_ch = ch;
 }
 
 static int port_try_handle_stateless_reset(QUIC_PORT *port, const QUIC_URXE *e)
@@ -518,14 +622,9 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
 
     /*
      * If we have an incoming packet which doesn't match any existing connection
-     * we assume this is an attempt to make a new connection. Currently we
-     * require our caller to have precreated a latent 'incoming' channel via
-     * TSERVER which then gets turned into the new connection.
-     *
-     * TODO(QUIC SERVER): In the future we will construct channels dynamically
-     * in this case.
+     * we assume this is an attempt to make a new connection.
      */
-    if (port->tserver_ch == NULL)
+    if (!port->allow_incoming)
         goto undesirable;
 
     /*
index b5e1206368c905de9a45a07126fced7519e8d2fe..79336ebef5fde94af7e0db30a23b5b2292ec33a5 100644 (file)
@@ -26,6 +26,7 @@
  * Other components should not include this header.
  */
 DECLARE_LIST_OF(ch, QUIC_CHANNEL);
+DECLARE_LIST_OF(incoming_ch, QUIC_CHANNEL);
 
 /* A port is always in one of the following states: */
 enum {
@@ -62,6 +63,12 @@ struct quic_port_st {
     /* List of all child channels. */
     OSSL_LIST(ch)                   channel_list;
 
+    /*
+     * Queue of unaccepted incoming channels. Each such channel is also on
+     * channel_list.
+     */
+    OSSL_LIST(incoming_ch)          incoming_channel_list;
+
     /* Special TSERVER channel. To be removed in the future. */
     QUIC_CHANNEL                    *tserver_ch;
 
@@ -74,6 +81,9 @@ struct quic_port_st {
     /* Port-level permanent errors (causing failure state) are stored here. */
     ERR_STATE                       *err_state;
 
+    /* Network BIO epoch. Increments whenever network BIO config changes. */
+    uint64_t                        net_bio_epoch;
+
     /* DCID length used for incoming short header packets. */
     unsigned char                   rx_short_dcid_len;
     /* For clients, CID length used for outgoing Initial packets. */
@@ -89,10 +99,14 @@ struct quic_port_st {
     unsigned int                    have_sent_any_pkt               : 1;
 
     /* Does this port allow incoming connections? */
-    unsigned int                    is_server                       : 1;
+    unsigned int                    allow_incoming                  : 1;
 
     /* Are we on the QUIC_ENGINE linked list of ports? */
     unsigned int                    on_engine_list                  : 1;
+
+    /* Are we using addressed mode (BIO_sendmmsg with non-NULL peer)? */
+    unsigned int                    addressed_mode_w                : 1;
+    unsigned int                    addressed_mode_r                : 1;
 };
 
 # endif