]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
RIO: Amend SSL_poll to support blocking on QUIC objects
authorHugo Landau <hlandau@openssl.org>
Mon, 13 May 2024 19:20:23 +0000 (20:20 +0100)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25416)

ssl/rio/poll_immediate.c

index eaa9f2b214eae6daa4cb0e4f0000def857e06d0f..9154acd064821af90d2ba9589bcd90b3cbebb8e9 100644 (file)
@@ -12,6 +12,7 @@
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include "../ssl_local.h"
+#include "poll_builder.h"
 
 #define ITEM_N(items, stride, n) \
     (*(SSL_POLL_ITEM *)((char *)(items) + (n)*(stride)))
         FAIL_FROM(i + 1);                                                   \
     } while (0)
 
-int SSL_poll(SSL_POLL_ITEM *items,
-             size_t num_items,
-             size_t stride,
-             const struct timeval *timeout,
-             uint64_t flags,
-             size_t *p_result_count)
+static int poll_translate_ssl_quic(SSL *ssl, RIO_POLL_BUILDER *rpb)
+{
+    BIO_POLL_DESCRIPTOR rd, wd;
+    int fd1 = -1, fd2 = -1;
+    int fd1_r = 0, fd1_w = 0, fd2_w = 0;
+
+    if (SSL_net_read_desired(ssl)) {
+        if (!SSL_get_rpoll_descriptor(ssl, &rd)) {
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll requires the network BIOs underlying "
+                           "a QUIC SSL object provide poll descriptors");
+            return 0;
+        }
+
+        if (rd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll requires the poll descriptors of the "
+                           "network BIOs underlying a QUIC SSL object be "
+                           "of socket type");
+            return 0;
+        }
+
+        fd1   = rd.value.fd;
+        fd1_r = 1;
+    }
+
+    if (SSL_net_write_desired(ssl)) {
+        if (!SSL_get_wpoll_descriptor(ssl, &wd)) {
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll requires the network BIOs underlying "
+                           "a QUIC SSL object provide poll descriptors");
+            return 0;
+        }
+
+        if (rd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll requires the poll descriptors of the "
+                           "network BIOs underlying a QUIC SSL object be "
+                           "of socket type");
+            return 0;
+        }
+
+        fd2   = wd.value.fd;
+        fd2_w = 1;
+    }
+
+    if (fd2 == fd1) {
+        fd2 = -1;
+        fd1_w = fd1_w || fd2_w;
+    }
+
+    if (fd1 != -1 && (fd1_r || fd1_w))
+        if (!ossl_rio_poll_builder_add_fd(rpb, fd1, fd1_r, fd1_w))
+            return 0;
+
+    if (fd2 != -1 && fd2_w)
+        if (!ossl_rio_poll_builder_add_fd(rpb, fd2, /*r=*/0, fd2_w))
+            return 0;
+
+    return 1;
+}
+
+static int poll_translate(SSL_POLL_ITEM *items,
+                          size_t num_items,
+                          size_t stride,
+                          RIO_POLL_BUILDER *rpb,
+                          OSSL_TIME *p_earliest_wakeup_deadline)
 {
     int ok = 1;
-    size_t i, result_count = 0;
     SSL_POLL_ITEM *item;
+    size_t result_count = 0;
     SSL *ssl;
-    uint64_t revents;
-    ossl_unused uint64_t events;
-    ossl_unused int do_tick = ((flags & SSL_POLL_FLAG_NO_HANDLE_EVENTS) == 0);
-    int is_immediate
-        = (timeout != NULL
-           && timeout->tv_sec == 0 && timeout->tv_usec == 0);
-
-    /*
-     * Prevent calls which use SSL_poll functionality which is not currently
-     * supported.
-     */
-    if (!is_immediate) {
-        ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
-                       "SSL_poll does not currently support blocking "
-                       "operation");
-        FAIL_FROM(0);
+    OSSL_TIME earliest_wakeup_deadline = ossl_time_infinite();
+    struct timeval timeout;
+    int is_infinite = 0;
+    size_t i;
+
+    for (i = 0; i < num_items; ++i) {
+        item = &ITEM_N(items, stride, i);
+
+        switch (item->desc.type) {
+        case BIO_POLL_DESCRIPTOR_TYPE_SSL:
+            ssl = item->desc.value.ssl;
+            if (ssl == NULL)
+                /* NULL items are no-ops and have revents reported as 0 */
+                break;
+
+            switch (ssl->type) {
+#ifndef OPENSSL_NO_QUIC
+            case SSL_TYPE_QUIC_CONNECTION:
+            case SSL_TYPE_QUIC_XSO:
+                if (!poll_translate_ssl_quic(ssl, rpb))
+                    FAIL_ITEM(i);
+
+                if (!SSL_get_event_timeout(ssl, &timeout, &is_infinite))
+                    FAIL_ITEM(i);
+
+                if (!is_infinite)
+                    earliest_wakeup_deadline
+                        = ossl_time_min(earliest_wakeup_deadline,
+                                        ossl_time_add(ossl_time_now(),
+                                                      ossl_time_from_timeval(timeout)));
+
+                break;
+#endif
+
+            default:
+                ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                               "SSL_poll currently only supports QUIC SSL "
+                               "objects");
+                FAIL_ITEM(i);
+            }
+            break;
+
+        case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll currently does not support polling "
+                           "sockets");
+            FAIL_ITEM(i);
+
+        default:
+            ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
+                           "SSL_poll does not support unknown poll descriptor "
+                           "type %d", item->desc.type);
+            FAIL_ITEM(i);
+        }
     }
 
-    /* Trivial case. */
-    if (num_items == 0)
+out:
+    *p_earliest_wakeup_deadline = earliest_wakeup_deadline;
+    return ok;
+}
+
+static int poll_block(SSL_POLL_ITEM *items,
+                      size_t num_items,
+                      size_t stride,
+                      OSSL_TIME user_deadline)
+{
+    int ok = 0;
+    RIO_POLL_BUILDER rpb;
+    OSSL_TIME earliest_wakeup_deadline;
+
+    ossl_rio_poll_builder_init(&rpb);
+
+    if (!poll_translate(items, num_items, stride, &rpb,
+                        &earliest_wakeup_deadline))
+        goto out;
+
+    earliest_wakeup_deadline = ossl_time_min(earliest_wakeup_deadline,
+                                             user_deadline);
+
+    if (!ossl_rio_poll_builder_poll(&rpb, earliest_wakeup_deadline))
         goto out;
 
-    /* Poll current state of each item. */
+    ok = 1;
+out:
+    ossl_rio_poll_builder_cleanup(&rpb);
+    return ok;
+}
+
+static int poll_readout(SSL_POLL_ITEM *items,
+                        size_t num_items,
+                        size_t stride,
+                        int do_tick,
+                        size_t *p_result_count)
+{
+    int ok = 1;
+    size_t i, result_count = 0;
+    SSL_POLL_ITEM *item;
+    SSL *ssl;
+    uint64_t events, revents;
+
     for (i = 0; i < num_items; ++i) {
         item    = &ITEM_N(items, stride, i);
         events  = item->events;
@@ -116,7 +255,68 @@ int SSL_poll(SSL_POLL_ITEM *items,
         item->revents = revents;
     }
 
-    /* TODO(QUIC POLLING): Blocking mode */
+out:
+    if (p_result_count != NULL)
+        *p_result_count = result_count;
+
+    return ok;
+}
+
+int SSL_poll(SSL_POLL_ITEM *items,
+             size_t num_items,
+             size_t stride,
+             const struct timeval *timeout,
+             uint64_t flags,
+             size_t *p_result_count)
+{
+    int ok = 1;
+    size_t result_count = 0;
+    ossl_unused int do_tick = ((flags & SSL_POLL_FLAG_NO_HANDLE_EVENTS) == 0);
+    OSSL_TIME deadline;
+
+    /* Trivial case. */
+    if (num_items == 0)
+        goto out;
+
+    /* Convert timeout to deadline. */
+    if (timeout == NULL)
+        deadline = ossl_time_infinite();
+    else if (timeout->tv_sec == 0 && timeout->tv_usec == 0)
+        deadline = ossl_time_zero();
+    else
+        deadline = ossl_time_add(ossl_time_now(),
+                                 ossl_time_from_timeval(*timeout));
+
+    /* Loop until we have something to report. */
+    for (;;) {
+        /* Readout phase - poll current state of each item. */
+        if (!poll_readout(items, num_items, stride, do_tick, &result_count)) {
+            result_count = 0;
+            ok = 0;
+            goto out;
+        }
+
+        /*
+         * If we got anything, or we are in immediate mode (zero timeout), or
+         * the deadline has expired, we're done.
+         */
+        if (result_count > 0
+            || ossl_time_is_zero(deadline) /* (avoids now call) */
+            || ossl_time_compare(ossl_time_now(), deadline) >= 0)
+            goto out;
+
+        /*
+         * Block until something is ready. Ignore NO_HANDLE_EVENTS from this
+         * point onwards.
+         */
+        do_tick = 1;
+        if (!poll_block(items, num_items, stride, deadline)) {
+            ok = 0;
+            goto out;
+        }
+    }
+
+    /* TODO(QUIC POLLING): Support for polling listeners */
     /* TODO(QUIC POLLING): Support for polling FDs */
 
 out: