From: Hugo Landau Date: Mon, 13 May 2024 19:20:23 +0000 (+0100) Subject: RIO: Amend SSL_poll to support blocking on QUIC objects X-Git-Tag: openssl-3.5.0-alpha1~347 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=67df6bd9362b96072b853ec8e62103028ecbb55f;p=thirdparty%2Fopenssl.git RIO: Amend SSL_poll to support blocking on QUIC objects Reviewed-by: Matt Caswell Reviewed-by: Saša Nedvědický Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/25416) --- diff --git a/ssl/rio/poll_immediate.c b/ssl/rio/poll_immediate.c index eaa9f2b214e..9154acd0648 100644 --- a/ssl/rio/poll_immediate.c +++ b/ssl/rio/poll_immediate.c @@ -12,6 +12,7 @@ #include #include #include "../ssl_local.h" +#include "poll_builder.h" #define ITEM_N(items, stride, n) \ (*(SSL_POLL_ITEM *)((char *)(items) + (n)*(stride))) @@ -34,40 +35,178 @@ 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: