| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single TCP/UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| |
| #include "buffer.h" |
| #include "error.h" |
| #include "integer.h" |
| #include "event.h" |
| #include "fdmisc.h" |
| |
| #include "memdbg.h" |
| |
| /* |
| * Some OSes will prefer select() over poll() |
| * when both are available. |
| */ |
| #if defined(TARGET_DARWIN) |
| #define SELECT_PREFERRED_OVER_POLL |
| #endif |
| |
| /* |
| * All non-windows OSes are assumed to have select() |
| */ |
| #ifdef _WIN32 |
| #define SELECT 0 |
| #else |
| #define SELECT 1 |
| #endif |
| |
| /* |
| * This should be set to the highest file descriptor |
| * which can be used in one of the FD_ macros. |
| */ |
| #ifdef FD_SETSIZE |
| #define SELECT_MAX_FDS FD_SETSIZE |
| #else |
| #define SELECT_MAX_FDS 256 |
| #endif |
| |
| static inline int |
| tv_to_ms_timeout(const struct timeval *tv) |
| { |
| if (tv->tv_sec == 0 && tv->tv_usec == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| return max_int(tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000, 1); |
| } |
| } |
| |
| #ifdef _WIN32 |
| |
| struct we_set |
| { |
| struct event_set_functions func; |
| bool fast; |
| HANDLE *events; |
| struct event_set_return *esr; |
| int n_events; |
| int capacity; |
| }; |
| |
| static inline void |
| we_set_event(struct we_set *wes, int i, event_t event, unsigned int rwflags, void *arg) |
| { |
| ASSERT(i >= 0 && i < wes->capacity); |
| |
| if (rwflags == EVENT_READ) |
| { |
| ASSERT(event->read != NULL); |
| wes->events[i] = event->read; |
| } |
| else if (rwflags == EVENT_WRITE) |
| { |
| ASSERT(event->write != NULL); |
| wes->events[i] = event->write; |
| } |
| else |
| { |
| msg(M_FATAL, "fatal error in we_set_events: rwflags=%d", rwflags); |
| } |
| |
| wes->esr[i].rwflags = rwflags; |
| wes->esr[i].arg = arg; |
| } |
| |
| static inline bool |
| we_append_event(struct we_set *wes, event_t event, unsigned int rwflags, void *arg) |
| { |
| if (rwflags & EVENT_WRITE) |
| { |
| if (wes->n_events < wes->capacity) |
| { |
| we_set_event(wes, wes->n_events, event, EVENT_WRITE, arg); |
| ++wes->n_events; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| if (rwflags & EVENT_READ) |
| { |
| if (wes->n_events < wes->capacity) |
| { |
| we_set_event(wes, wes->n_events, event, EVENT_READ, arg); |
| ++wes->n_events; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static void |
| we_del_event(struct we_set *wes, event_t event) |
| { |
| int i, j = 0; |
| const int len = wes->n_events; |
| |
| for (i = 0; i < len; ++i) |
| { |
| const HANDLE h = wes->events[i]; |
| if (h == event->read || h == event->write) |
| { |
| --wes->n_events; |
| } |
| else |
| { |
| if (i != j) |
| { |
| wes->events[j] = wes->events[i]; |
| wes->esr[j] = wes->esr[i]; |
| } |
| ++j; |
| } |
| } |
| } |
| |
| static void |
| we_del_index(struct we_set *wes, int index) |
| { |
| int i; |
| ASSERT(index >= 0 && index < wes->n_events); |
| for (i = index; i < wes->n_events - 1; ++i) |
| { |
| wes->events[i] = wes->events[i+1]; |
| wes->esr[i] = wes->esr[i+1]; |
| } |
| --wes->n_events; |
| } |
| |
| static void |
| we_get_rw_indices(struct we_set *wes, event_t event, int *ri, int *wi) |
| { |
| int i; |
| *ri = *wi = -1; |
| for (i = 0; i < wes->n_events; ++i) |
| { |
| const HANDLE h = wes->events[i]; |
| if (h == event->read) |
| { |
| ASSERT(*ri == -1); |
| *ri = i; |
| } |
| else if (h == event->write) |
| { |
| ASSERT(*wi == -1); |
| *wi = i; |
| } |
| } |
| } |
| |
| static void |
| we_free(struct event_set *es) |
| { |
| struct we_set *wes = (struct we_set *) es; |
| free(wes->events); |
| free(wes->esr); |
| free(wes); |
| } |
| |
| static void |
| we_reset(struct event_set *es) |
| { |
| struct we_set *wes = (struct we_set *) es; |
| ASSERT(wes->fast); |
| wes->n_events = 0; |
| } |
| |
| static void |
| we_del(struct event_set *es, event_t event) |
| { |
| struct we_set *wes = (struct we_set *) es; |
| ASSERT(!wes->fast); |
| we_del_event(wes, event); |
| } |
| |
| static void |
| we_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) |
| { |
| struct we_set *wes = (struct we_set *) es; |
| |
| dmsg(D_EVENT_WAIT, "WE_CTL n=%d ev=%p rwflags=0x%04x arg=" ptr_format, |
| wes->n_events, |
| event, |
| rwflags, |
| (ptr_type)arg); |
| |
| if (wes->fast) |
| { |
| if (!we_append_event(wes, event, rwflags, arg)) |
| { |
| goto err; |
| } |
| } |
| else |
| { |
| int ri, wi; |
| int one = -1; |
| int n = 0; |
| |
| we_get_rw_indices(wes, event, &ri, &wi); |
| if (wi >= 0) |
| { |
| one = wi; |
| ++n; |
| } |
| if (ri >= 0) |
| { |
| one = ri; |
| ++n; |
| } |
| switch (rwflags) |
| { |
| case 0: |
| switch (n) |
| { |
| case 0: |
| break; |
| |
| case 1: |
| we_del_index(wes, one); |
| break; |
| |
| case 2: |
| we_del_event(wes, event); |
| break; |
| |
| default: |
| ASSERT(0); |
| } |
| break; |
| |
| case EVENT_READ: |
| switch (n) |
| { |
| case 0: |
| if (!we_append_event(wes, event, EVENT_READ, arg)) |
| { |
| goto err; |
| } |
| break; |
| |
| case 1: |
| we_set_event(wes, one, event, EVENT_READ, arg); |
| break; |
| |
| case 2: |
| we_del_index(wes, wi); |
| break; |
| |
| default: |
| ASSERT(0); |
| } |
| break; |
| |
| case EVENT_WRITE: |
| switch (n) |
| { |
| case 0: |
| if (!we_append_event(wes, event, EVENT_WRITE, arg)) |
| { |
| goto err; |
| } |
| break; |
| |
| case 1: |
| we_set_event(wes, one, event, EVENT_WRITE, arg); |
| break; |
| |
| case 2: |
| we_del_index(wes, ri); |
| break; |
| |
| default: |
| ASSERT(0); |
| } |
| break; |
| |
| case EVENT_READ|EVENT_WRITE: |
| switch (n) |
| { |
| case 0: |
| if (!we_append_event(wes, event, EVENT_READ|EVENT_WRITE, arg)) |
| { |
| goto err; |
| } |
| break; |
| |
| case 1: |
| if (ri == -1) |
| { |
| ASSERT(wi != -1); |
| if (!we_append_event(wes, event, EVENT_READ, arg)) |
| { |
| goto err; |
| } |
| } |
| else if (wi == -1) |
| { |
| if (!we_append_event(wes, event, EVENT_WRITE, arg)) |
| { |
| goto err; |
| } |
| } |
| else |
| { |
| ASSERT(0); |
| } |
| break; |
| |
| case 2: |
| break; |
| |
| default: |
| ASSERT(0); |
| } |
| break; |
| |
| default: |
| msg(M_FATAL, "fatal error in we_ctl: rwflags=%d", rwflags); |
| } |
| } |
| return; |
| |
| err: |
| msg(D_EVENT_ERRORS, "Error: Windows resource limit WSA_MAXIMUM_WAIT_EVENTS (%d) has been exceeded", WSA_MAXIMUM_WAIT_EVENTS); |
| } |
| |
| static int |
| we_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) |
| { |
| struct we_set *wes = (struct we_set *) es; |
| const int timeout = tv_to_ms_timeout(tv); |
| DWORD status; |
| |
| dmsg(D_EVENT_WAIT, "WE_WAIT enter n=%d to=%d", wes->n_events, timeout); |
| |
| #ifdef ENABLE_DEBUG |
| if (check_debug_level(D_EVENT_WAIT)) |
| { |
| int i; |
| for (i = 0; i < wes->n_events; ++i) |
| { |
| dmsg(D_EVENT_WAIT, "[%d] ev=%p rwflags=0x%04x arg=" ptr_format, |
| i, |
| wes->events[i], |
| wes->esr[i].rwflags, |
| (ptr_type)wes->esr[i].arg); |
| } |
| } |
| #endif |
| |
| /* |
| * First poll our event list with 0 timeout |
| */ |
| status = WSAWaitForMultipleEvents( |
| (DWORD) wes->n_events, |
| wes->events, |
| FALSE, |
| (DWORD) 0, |
| FALSE); |
| |
| /* |
| * If at least one event is already set, we must |
| * individually poll the whole list. |
| */ |
| if (status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) |
| { |
| int i; |
| int j = 0; |
| for (i = 0; i < wes->n_events; ++i) |
| { |
| if (j >= outlen) |
| { |
| break; |
| } |
| if (WaitForSingleObject(wes->events[i], 0) == WAIT_OBJECT_0) |
| { |
| *out = wes->esr[i]; |
| dmsg(D_EVENT_WAIT, "WE_WAIT leave [%d,%d] rwflags=0x%04x arg=" ptr_format, |
| i, j, out->rwflags, (ptr_type)out->arg); |
| ++j; |
| ++out; |
| } |
| } |
| return j; |
| } |
| else |
| { |
| /* |
| * If caller specified timeout > 0, we know at this point |
| * that no events are set, so wait only for the first event |
| * (or timeout) and return at most one event_set_return object. |
| * |
| * If caller specified timeout == 0, the second call to |
| * WSAWaitForMultipleEvents would be redundant -- just |
| * return 0 indicating timeout. |
| */ |
| if (timeout > 0) |
| { |
| status = WSAWaitForMultipleEvents( |
| (DWORD) wes->n_events, |
| wes->events, |
| FALSE, |
| (DWORD) timeout, |
| FALSE); |
| } |
| |
| if (outlen >= 1 && status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) |
| { |
| *out = wes->esr[status - WSA_WAIT_EVENT_0]; |
| dmsg(D_EVENT_WAIT, "WE_WAIT leave rwflags=0x%04x arg=" ptr_format, |
| out->rwflags, (ptr_type)out->arg); |
| return 1; |
| } |
| else if (status == WSA_WAIT_TIMEOUT) |
| { |
| return 0; |
| } |
| else |
| { |
| return -1; |
| } |
| } |
| } |
| |
| static struct event_set * |
| we_init(int *maxevents, unsigned int flags) |
| { |
| struct we_set *wes; |
| |
| dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); |
| |
| ALLOC_OBJ_CLEAR(wes, struct we_set); |
| |
| /* set dispatch functions */ |
| wes->func.free = we_free; |
| wes->func.reset = we_reset; |
| wes->func.del = we_del; |
| wes->func.ctl = we_ctl; |
| wes->func.wait = we_wait; |
| |
| if (flags & EVENT_METHOD_FAST) |
| { |
| wes->fast = true; |
| } |
| wes->n_events = 0; |
| |
| /* Figure our event capacity */ |
| ASSERT(*maxevents > 0); |
| wes->capacity = min_int(*maxevents * 2, WSA_MAXIMUM_WAIT_EVENTS); |
| *maxevents = min_int(*maxevents, WSA_MAXIMUM_WAIT_EVENTS); |
| |
| /* Allocate space for Win32 event handles */ |
| ALLOC_ARRAY_CLEAR(wes->events, HANDLE, wes->capacity); |
| |
| /* Allocate space for event_set_return objects */ |
| ALLOC_ARRAY_CLEAR(wes->esr, struct event_set_return, wes->capacity); |
| |
| dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d capacity=%d", |
| *maxevents, wes->capacity); |
| |
| return (struct event_set *) wes; |
| } |
| |
| #endif /* _WIN32 */ |
| |
| #if EPOLL |
| |
| struct ep_set |
| { |
| struct event_set_functions func; |
| bool fast; |
| int epfd; |
| int maxevents; |
| struct epoll_event *events; |
| }; |
| |
| static void |
| ep_free(struct event_set *es) |
| { |
| struct ep_set *eps = (struct ep_set *) es; |
| close(eps->epfd); |
| free(eps->events); |
| free(eps); |
| } |
| |
| static void |
| ep_reset(struct event_set *es) |
| { |
| const struct ep_set *eps = (struct ep_set *) es; |
| ASSERT(eps->fast); |
| } |
| |
| static void |
| ep_del(struct event_set *es, event_t event) |
| { |
| struct epoll_event ev; |
| struct ep_set *eps = (struct ep_set *) es; |
| |
| dmsg(D_EVENT_WAIT, "EP_DEL ev=%d", (int)event); |
| |
| ASSERT(!eps->fast); |
| CLEAR(ev); |
| epoll_ctl(eps->epfd, EPOLL_CTL_DEL, event, &ev); |
| } |
| |
| static void |
| ep_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) |
| { |
| struct ep_set *eps = (struct ep_set *) es; |
| struct epoll_event ev; |
| |
| CLEAR(ev); |
| |
| ev.data.ptr = arg; |
| if (rwflags & EVENT_READ) |
| { |
| ev.events |= EPOLLIN; |
| } |
| if (rwflags & EVENT_WRITE) |
| { |
| ev.events |= EPOLLOUT; |
| } |
| |
| dmsg(D_EVENT_WAIT, "EP_CTL fd=%d rwflags=0x%04x ev=0x%08x arg=" ptr_format, |
| (int)event, |
| rwflags, |
| (unsigned int)ev.events, |
| (ptr_type)ev.data.ptr); |
| |
| if (epoll_ctl(eps->epfd, EPOLL_CTL_MOD, event, &ev) < 0) |
| { |
| if (errno == ENOENT) |
| { |
| if (epoll_ctl(eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0) |
| { |
| msg(M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed, sd=%d", (int)event); |
| } |
| } |
| else |
| { |
| msg(M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed, sd=%d", (int)event); |
| } |
| } |
| } |
| |
| static int |
| ep_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) |
| { |
| struct ep_set *eps = (struct ep_set *) es; |
| int stat; |
| |
| if (outlen > eps->maxevents) |
| { |
| outlen = eps->maxevents; |
| } |
| |
| stat = epoll_wait(eps->epfd, eps->events, outlen, tv_to_ms_timeout(tv)); |
| ASSERT(stat <= outlen); |
| |
| if (stat > 0) |
| { |
| int i; |
| const struct epoll_event *ev = eps->events; |
| struct event_set_return *esr = out; |
| for (i = 0; i < stat; ++i) |
| { |
| esr->rwflags = 0; |
| if (ev->events & (EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP)) |
| { |
| esr->rwflags |= EVENT_READ; |
| } |
| if (ev->events & EPOLLOUT) |
| { |
| esr->rwflags |= EVENT_WRITE; |
| } |
| esr->arg = ev->data.ptr; |
| dmsg(D_EVENT_WAIT, "EP_WAIT[%d] rwflags=0x%04x ev=0x%08x arg=" ptr_format, |
| i, esr->rwflags, ev->events, (ptr_type)ev->data.ptr); |
| ++ev; |
| ++esr; |
| } |
| } |
| return stat; |
| } |
| |
| static struct event_set * |
| ep_init(int *maxevents, unsigned int flags) |
| { |
| struct ep_set *eps; |
| int fd; |
| |
| dmsg(D_EVENT_WAIT, "EP_INIT maxevents=%d flags=0x%08x", *maxevents, flags); |
| |
| /* open epoll file descriptor */ |
| fd = epoll_create(*maxevents); |
| if (fd < 0) |
| { |
| return NULL; |
| } |
| |
| set_cloexec(fd); |
| |
| ALLOC_OBJ_CLEAR(eps, struct ep_set); |
| |
| /* set dispatch functions */ |
| eps->func.free = ep_free; |
| eps->func.reset = ep_reset; |
| eps->func.del = ep_del; |
| eps->func.ctl = ep_ctl; |
| eps->func.wait = ep_wait; |
| |
| /* fast method ("sort of") corresponds to epoll one-shot */ |
| if (flags & EVENT_METHOD_FAST) |
| { |
| eps->fast = true; |
| } |
| |
| /* allocate space for epoll_wait return */ |
| ASSERT(*maxevents > 0); |
| eps->maxevents = *maxevents; |
| ALLOC_ARRAY_CLEAR(eps->events, struct epoll_event, eps->maxevents); |
| |
| /* set epoll control fd */ |
| eps->epfd = fd; |
| |
| return (struct event_set *) eps; |
| } |
| #endif /* EPOLL */ |
| |
| #if POLL |
| |
| struct po_set |
| { |
| struct event_set_functions func; |
| bool fast; |
| struct pollfd *events; |
| void **args; |
| int n_events; |
| int capacity; |
| }; |
| |
| static void |
| po_free(struct event_set *es) |
| { |
| struct po_set *pos = (struct po_set *) es; |
| free(pos->events); |
| free(pos->args); |
| free(pos); |
| } |
| |
| static void |
| po_reset(struct event_set *es) |
| { |
| struct po_set *pos = (struct po_set *) es; |
| ASSERT(pos->fast); |
| pos->n_events = 0; |
| } |
| |
| static void |
| po_del(struct event_set *es, event_t event) |
| { |
| struct po_set *pos = (struct po_set *) es; |
| int i; |
| |
| dmsg(D_EVENT_WAIT, "PO_DEL ev=%d", (int)event); |
| |
| ASSERT(!pos->fast); |
| for (i = 0; i < pos->n_events; ++i) |
| { |
| if (pos->events[i].fd == event) |
| { |
| int j; |
| for (j = i; j < pos->n_events - 1; ++j) |
| { |
| pos->events[j] = pos->events[j+1]; |
| pos->args[j] = pos->args[j+1]; |
| } |
| --pos->n_events; |
| break; |
| } |
| } |
| } |
| |
| static inline void |
| po_set_pollfd_events(struct pollfd *pfdp, unsigned int rwflags) |
| { |
| pfdp->events = 0; |
| if (rwflags & EVENT_WRITE) |
| { |
| pfdp->events |= POLLOUT; |
| } |
| if (rwflags & EVENT_READ) |
| { |
| pfdp->events |= (POLLIN|POLLPRI); |
| } |
| } |
| |
| static inline bool |
| po_append_event(struct po_set *pos, event_t event, unsigned int rwflags, void *arg) |
| { |
| if (pos->n_events < pos->capacity) |
| { |
| struct pollfd *pfdp = &pos->events[pos->n_events]; |
| pfdp->fd = event; |
| pos->args[pos->n_events] = arg; |
| po_set_pollfd_events(pfdp, rwflags); |
| ++pos->n_events; |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| static void |
| po_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) |
| { |
| struct po_set *pos = (struct po_set *) es; |
| |
| dmsg(D_EVENT_WAIT, "PO_CTL rwflags=0x%04x ev=%d arg=" ptr_format, |
| rwflags, (int)event, (ptr_type)arg); |
| |
| if (pos->fast) |
| { |
| if (!po_append_event(pos, event, rwflags, arg)) |
| { |
| goto err; |
| } |
| } |
| else |
| { |
| int i; |
| for (i = 0; i < pos->n_events; ++i) |
| { |
| struct pollfd *pfdp = &pos->events[i]; |
| if (pfdp->fd == event) |
| { |
| pos->args[i] = arg; |
| po_set_pollfd_events(pfdp, rwflags); |
| goto done; |
| } |
| } |
| if (!po_append_event(pos, event, rwflags, arg)) |
| { |
| goto err; |
| } |
| } |
| |
| done: |
| return; |
| |
| err: |
| msg(D_EVENT_ERRORS, "Error: poll: too many I/O wait events"); |
| } |
| |
| static int |
| po_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) |
| { |
| struct po_set *pos = (struct po_set *) es; |
| int stat; |
| |
| stat = poll(pos->events, pos->n_events, tv_to_ms_timeout(tv)); |
| |
| ASSERT(stat <= pos->n_events); |
| |
| if (stat > 0) |
| { |
| int i, j = 0; |
| const struct pollfd *pfdp = pos->events; |
| for (i = 0; i < pos->n_events && j < outlen; ++i) |
| { |
| if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP|POLLOUT)) |
| { |
| out->rwflags = 0; |
| if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP)) |
| { |
| out->rwflags |= EVENT_READ; |
| } |
| if (pfdp->revents & POLLOUT) |
| { |
| out->rwflags |= EVENT_WRITE; |
| } |
| out->arg = pos->args[i]; |
| dmsg(D_EVENT_WAIT, "PO_WAIT[%d,%d] fd=%d rev=0x%08x rwflags=0x%04x arg=" ptr_format " %s", |
| i, j, pfdp->fd, pfdp->revents, out->rwflags, (ptr_type)out->arg, pos->fast ? "" : "[scalable]"); |
| ++out; |
| ++j; |
| } |
| else if (pfdp->revents) |
| { |
| msg(D_EVENT_ERRORS, "Error: poll: unknown revents=0x%04x", (unsigned int)pfdp->revents); |
| } |
| ++pfdp; |
| } |
| return j; |
| } |
| return stat; |
| } |
| |
| static struct event_set * |
| po_init(int *maxevents, unsigned int flags) |
| { |
| struct po_set *pos; |
| |
| dmsg(D_EVENT_WAIT, "PO_INIT maxevents=%d flags=0x%08x", *maxevents, flags); |
| |
| ALLOC_OBJ_CLEAR(pos, struct po_set); |
| |
| /* set dispatch functions */ |
| pos->func.free = po_free; |
| pos->func.reset = po_reset; |
| pos->func.del = po_del; |
| pos->func.ctl = po_ctl; |
| pos->func.wait = po_wait; |
| |
| if (flags & EVENT_METHOD_FAST) |
| { |
| pos->fast = true; |
| } |
| |
| pos->n_events = 0; |
| |
| /* Figure our event capacity */ |
| ASSERT(*maxevents > 0); |
| pos->capacity = *maxevents; |
| |
| /* Allocate space for pollfd structures to be passed to poll() */ |
| ALLOC_ARRAY_CLEAR(pos->events, struct pollfd, pos->capacity); |
| |
| /* Allocate space for event_set_return objects */ |
| ALLOC_ARRAY_CLEAR(pos->args, void *, pos->capacity); |
| |
| return (struct event_set *) pos; |
| } |
| #endif /* POLL */ |
| |
| #if SELECT |
| |
| struct se_set |
| { |
| struct event_set_functions func; |
| bool fast; |
| fd_set readfds; |
| fd_set writefds; |
| void **args; /* allocated to capacity size */ |
| int maxfd; /* largest fd seen so far, always < capacity */ |
| int capacity; /* fixed largest fd + 1 */ |
| }; |
| |
| static void |
| se_free(struct event_set *es) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| free(ses->args); |
| free(ses); |
| } |
| |
| static void |
| se_reset(struct event_set *es) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| int i; |
| ASSERT(ses->fast); |
| |
| dmsg(D_EVENT_WAIT, "SE_RESET"); |
| |
| FD_ZERO(&ses->readfds); |
| FD_ZERO(&ses->writefds); |
| for (i = 0; i <= ses->maxfd; ++i) |
| { |
| ses->args[i] = NULL; |
| } |
| ses->maxfd = -1; |
| } |
| |
| static void |
| se_del(struct event_set *es, event_t event) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| ASSERT(!ses->fast); |
| |
| dmsg(D_EVENT_WAIT, "SE_DEL ev=%d", (int)event); |
| |
| if (event >= 0 && event < ses->capacity) |
| { |
| FD_CLR(event, &ses->readfds); |
| FD_CLR(event, &ses->writefds); |
| ses->args[event] = NULL; |
| } |
| else |
| { |
| msg(D_EVENT_ERRORS, "Error: select/se_del: too many I/O wait events"); |
| } |
| return; |
| } |
| |
| static void |
| se_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| |
| dmsg(D_EVENT_WAIT, "SE_CTL rwflags=0x%04x ev=%d fast=%d cap=%d maxfd=%d arg=" ptr_format, |
| rwflags, (int)event, (int)ses->fast, ses->capacity, ses->maxfd, (ptr_type)arg); |
| |
| if (event >= 0 && event < ses->capacity) |
| { |
| ses->maxfd = max_int(event, ses->maxfd); |
| ses->args[event] = arg; |
| if (ses->fast) |
| { |
| if (rwflags & EVENT_READ) |
| { |
| openvpn_fd_set(event, &ses->readfds); |
| } |
| if (rwflags & EVENT_WRITE) |
| { |
| openvpn_fd_set(event, &ses->writefds); |
| } |
| } |
| else |
| { |
| if (rwflags & EVENT_READ) |
| { |
| openvpn_fd_set(event, &ses->readfds); |
| } |
| else |
| { |
| FD_CLR(event, &ses->readfds); |
| } |
| if (rwflags & EVENT_WRITE) |
| { |
| openvpn_fd_set(event, &ses->writefds); |
| } |
| else |
| { |
| FD_CLR(event, &ses->writefds); |
| } |
| } |
| } |
| else |
| { |
| msg(D_EVENT_ERRORS, "Error: select: too many I/O wait events, fd=%d cap=%d", |
| (int) event, |
| ses->capacity); |
| } |
| } |
| |
| static int |
| se_wait_return(struct se_set *ses, |
| fd_set *read, |
| fd_set *write, |
| struct event_set_return *out, |
| int outlen) |
| { |
| int i, j = 0; |
| for (i = 0; i <= ses->maxfd && j < outlen; ++i) |
| { |
| const bool r = FD_ISSET(i, read); |
| const bool w = FD_ISSET(i, write); |
| if (r || w) |
| { |
| out->rwflags = 0; |
| if (r) |
| { |
| out->rwflags |= EVENT_READ; |
| } |
| if (w) |
| { |
| out->rwflags |= EVENT_WRITE; |
| } |
| out->arg = ses->args[i]; |
| dmsg(D_EVENT_WAIT, "SE_WAIT[%d,%d] rwflags=0x%04x arg=" ptr_format, |
| i, j, out->rwflags, (ptr_type)out->arg); |
| ++out; |
| ++j; |
| } |
| } |
| return j; |
| } |
| |
| static int |
| se_wait_fast(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| struct timeval tv_tmp = *tv; |
| int stat; |
| |
| dmsg(D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%d/%d", |
| ses->maxfd, |
| (int)tv_tmp.tv_sec, |
| (int)tv_tmp.tv_usec); |
| |
| stat = select(ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp); |
| |
| if (stat > 0) |
| { |
| stat = se_wait_return(ses, &ses->readfds, &ses->writefds, out, outlen); |
| } |
| |
| return stat; |
| } |
| |
| static int |
| se_wait_scalable(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) |
| { |
| struct se_set *ses = (struct se_set *) es; |
| struct timeval tv_tmp = *tv; |
| fd_set read = ses->readfds; |
| fd_set write = ses->writefds; |
| int stat; |
| |
| dmsg(D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%d/%d", |
| ses->maxfd, (int)tv_tmp.tv_sec, (int)tv_tmp.tv_usec); |
| |
| stat = select(ses->maxfd + 1, &read, &write, NULL, &tv_tmp); |
| |
| if (stat > 0) |
| { |
| stat = se_wait_return(ses, &read, &write, out, outlen); |
| } |
| |
| return stat; |
| } |
| |
| static struct event_set * |
| se_init(int *maxevents, unsigned int flags) |
| { |
| struct se_set *ses; |
| |
| dmsg(D_EVENT_WAIT, "SE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); |
| |
| ALLOC_OBJ_CLEAR(ses, struct se_set); |
| |
| /* set dispatch functions */ |
| ses->func.free = se_free; |
| ses->func.reset = se_reset; |
| ses->func.del = se_del; |
| ses->func.ctl = se_ctl; |
| ses->func.wait = se_wait_scalable; |
| |
| if (flags & EVENT_METHOD_FAST) |
| { |
| ses->fast = true; |
| ses->func.wait = se_wait_fast; |
| } |
| |
| /* Select needs to be passed this value + 1 */ |
| ses->maxfd = -1; |
| |
| /* Set our event capacity */ |
| ASSERT(*maxevents > 0); |
| *maxevents = min_int(*maxevents, SELECT_MAX_FDS); |
| ses->capacity = SELECT_MAX_FDS; |
| |
| /* Allocate space for event_set_return void * args */ |
| ALLOC_ARRAY_CLEAR(ses->args, void *, ses->capacity); |
| |
| return (struct event_set *) ses; |
| } |
| #endif /* SELECT */ |
| |
| static struct event_set * |
| event_set_init_simple(int *maxevents, unsigned int flags) |
| { |
| struct event_set *ret = NULL; |
| #ifdef _WIN32 |
| ret = we_init(maxevents, flags); |
| #elif POLL && SELECT |
| #if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */ |
| if (flags & EVENT_METHOD_US_TIMEOUT) |
| { |
| ret = se_init(maxevents, flags); |
| } |
| #endif |
| #ifdef SELECT_PREFERRED_OVER_POLL |
| if (!ret) |
| { |
| ret = se_init(maxevents, flags); |
| } |
| if (!ret) |
| { |
| ret = po_init(maxevents, flags); |
| } |
| #else /* ifdef SELECT_PREFERRED_OVER_POLL */ |
| if (!ret) |
| { |
| ret = po_init(maxevents, flags); |
| } |
| if (!ret) |
| { |
| ret = se_init(maxevents, flags); |
| } |
| #endif |
| #elif POLL |
| ret = po_init(maxevents, flags); |
| #elif SELECT |
| ret = se_init(maxevents, flags); |
| #else /* ifdef _WIN32 */ |
| #error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel |
| #endif /* ifdef _WIN32 */ |
| ASSERT(ret); |
| return ret; |
| } |
| |
| static struct event_set * |
| event_set_init_scalable(int *maxevents, unsigned int flags) |
| { |
| struct event_set *ret = NULL; |
| #if EPOLL |
| ret = ep_init(maxevents, flags); |
| if (!ret) |
| { |
| msg(M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API"); |
| ret = event_set_init_simple(maxevents, flags); |
| } |
| #else /* if EPOLL */ |
| ret = event_set_init_simple(maxevents, flags); |
| #endif |
| ASSERT(ret); |
| return ret; |
| } |
| |
| struct event_set * |
| event_set_init(int *maxevents, unsigned int flags) |
| { |
| if (flags & EVENT_METHOD_FAST) |
| { |
| return event_set_init_simple(maxevents, flags); |
| } |
| else |
| { |
| return event_set_init_scalable(maxevents, flags); |
| } |
| } |