aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssl/ssl/rio/poll_builder.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssl/ssl/rio/poll_builder.c')
-rw-r--r--crypto/openssl/ssl/rio/poll_builder.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/crypto/openssl/ssl/rio/poll_builder.c b/crypto/openssl/ssl/rio/poll_builder.c
new file mode 100644
index 000000000000..bd9317a8b8bb
--- /dev/null
+++ b/crypto/openssl/ssl/rio/poll_builder.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include "internal/safe_math.h"
+#include "poll_builder.h"
+
+OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
+
+int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb)
+{
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_NONE
+ return 0;
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+ FD_ZERO(&rpb->rfd);
+ FD_ZERO(&rpb->wfd);
+ FD_ZERO(&rpb->efd);
+ rpb->hwm_fd = -1;
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ rpb->pfd_heap = NULL;
+ rpb->pfd_num = 0;
+ rpb->pfd_alloc = OSSL_NELEM(rpb->pfds);
+#endif
+ return 1;
+}
+
+void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb)
+{
+ if (rpb == NULL)
+ return;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ OPENSSL_free(rpb->pfd_heap);
+#endif
+}
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc)
+{
+ struct pollfd *pfd_heap_new;
+ size_t total_size;
+ int error = 0;
+
+ if (alloc <= rpb->pfd_alloc)
+ return 1;
+
+ total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error);
+ if (error)
+ return 0;
+
+ pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size);
+ if (pfd_heap_new == NULL)
+ return 0;
+
+ if (rpb->pfd_heap == NULL) {
+ /* Copy the contents of the stacked array. */
+ memcpy(pfd_heap_new, rpb->pfds, sizeof(rpb->pfds));
+ }
+ rpb->pfd_heap = pfd_heap_new;
+ rpb->pfd_alloc = alloc;
+ return 1;
+}
+#endif
+
+int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
+ int want_read, int want_write)
+{
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ size_t i;
+ struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds);
+ struct pollfd *pfd;
+#endif
+
+ if (fd < 0)
+ return 0;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+
+# ifndef OPENSSL_SYS_WINDOWS
+ /*
+ * On Windows there is no relevant limit to the magnitude of a fd value (see
+ * above). On *NIX the fd_set uses a bitmap and we must check the limit.
+ */
+ if (fd >= FD_SETSIZE)
+ return 0;
+# endif
+
+ if (want_read)
+ openssl_fdset(fd, &rpb->rfd);
+
+ if (want_write)
+ openssl_fdset(fd, &rpb->wfd);
+
+ openssl_fdset(fd, &rpb->efd);
+ if (fd > rpb->hwm_fd)
+ rpb->hwm_fd = fd;
+
+ return 1;
+
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ for (i = 0; i < rpb->pfd_num; ++i) {
+ pfd = &pfds[i];
+
+ if (pfd->fd == -1 || pfd->fd == fd)
+ break;
+ }
+
+ if (i >= rpb->pfd_alloc) {
+ if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2))
+ return 0;
+ pfds = rpb->pfd_heap;
+ }
+
+ assert((rpb->pfd_heap != NULL && rpb->pfd_heap == pfds) ||
+ (rpb->pfd_heap == NULL && rpb->pfds == pfds));
+ assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc);
+ pfds[i].fd = fd;
+ pfds[i].events = 0;
+
+ if (want_read)
+ pfds[i].events |= POLLIN;
+ if (want_write)
+ pfds[i].events |= POLLOUT;
+
+ if (i == rpb->pfd_num)
+ ++rpb->pfd_num;
+
+ return 1;
+#endif
+}
+
+int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
+{
+ int rc;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+ do {
+ struct timeval timeout, *p_timeout = &timeout;
+
+ /*
+ * select expects a timeout, not a deadline, so do the conversion.
+ * Update for each call to ensure the correct value is used if we repeat
+ * due to EINTR.
+ */
+ if (ossl_time_is_infinite(deadline))
+ p_timeout = NULL;
+ else
+ /*
+ * ossl_time_subtract saturates to zero so we don't need to check if
+ * now > deadline.
+ */
+ timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
+ ossl_time_now()));
+
+ rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout);
+ } while (rc == -1 && get_last_socket_error_is_eintr());
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ do {
+ int timeout_ms;
+
+ if (ossl_time_is_infinite(deadline))
+ timeout_ms = -1;
+ else
+ timeout_ms = ossl_time2ms(ossl_time_subtract(deadline,
+ ossl_time_now()));
+
+ rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds,
+ rpb->pfd_num, timeout_ms);
+ } while (rc == -1 && get_last_socket_error_is_eintr());
+#endif
+
+ return rc < 0 ? 0 : 1;
+}