Reviewed-by: Neil Horman <nhorman@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Release: yes
175 lines
4.5 KiB
C
175 lines
4.5 KiB
C
/*
|
|
* 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_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;
|
|
}
|
|
|
|
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;
|
|
}
|