openssl/ssl/rio/poll_builder.c
openssl-machine 0c679f5566 Copyright year updates
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Release: yes
2025-03-12 13:35:59 +00:00

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;
}