src/openvpn/event.c
6fbf66fa
 /*
  *  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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
caa54ac3
  *  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.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #include "buffer.h"
 #include "error.h"
 #include "integer.h"
 #include "event.h"
09ee4192
 #include "fdmisc.h"
6fbf66fa
 
 #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()
  */
445b192a
 #ifdef _WIN32
6fbf66fa
 #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
81d882d5
 tv_to_ms_timeout(const struct timeval *tv)
6fbf66fa
 {
81d882d5
     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);
     }
6fbf66fa
 }
 
445b192a
 #ifdef _WIN32
6fbf66fa
 
 struct we_set
 {
81d882d5
     struct event_set_functions func;
     bool fast;
     HANDLE *events;
     struct event_set_return *esr;
     int n_events;
     int capacity;
6fbf66fa
 };
 
 static inline void
81d882d5
 we_set_event(struct we_set *wes, int i, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     ASSERT(i >= 0 && i < wes->capacity);
6fbf66fa
 
81d882d5
     if (rwflags == EVENT_READ)
6fbf66fa
     {
81d882d5
         ASSERT(event->read != NULL);
         wes->events[i] = event->read;
6fbf66fa
     }
81d882d5
     else if (rwflags == EVENT_WRITE)
6fbf66fa
     {
81d882d5
         ASSERT(event->write != NULL);
         wes->events[i] = event->write;
6fbf66fa
     }
81d882d5
     else
     {
         msg(M_FATAL, "fatal error in we_set_events: rwflags=%d", rwflags);
     }
 
     wes->esr[i].rwflags = rwflags;
     wes->esr[i].arg = arg;
6fbf66fa
 }
 
 static inline bool
81d882d5
 we_append_event(struct we_set *wes, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     if (rwflags & EVENT_WRITE)
6fbf66fa
     {
81d882d5
         if (wes->n_events < wes->capacity)
         {
             we_set_event(wes, wes->n_events, event, EVENT_WRITE, arg);
             ++wes->n_events;
         }
         else
         {
             return false;
         }
6fbf66fa
     }
81d882d5
     if (rwflags & EVENT_READ)
6fbf66fa
     {
81d882d5
         if (wes->n_events < wes->capacity)
         {
             we_set_event(wes, wes->n_events, event, EVENT_READ, arg);
             ++wes->n_events;
         }
         else
         {
             return false;
         }
6fbf66fa
     }
81d882d5
     return true;
6fbf66fa
 }
 
 static void
81d882d5
 we_del_event(struct we_set *wes, event_t event)
6fbf66fa
 {
81d882d5
     int i, j = 0;
     const int len = wes->n_events;
6fbf66fa
 
81d882d5
     for (i = 0; i < len; ++i)
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
 }
 
 static void
81d882d5
 we_del_index(struct we_set *wes, int index)
6fbf66fa
 {
81d882d5
     int i;
     ASSERT(index >= 0 && index < wes->n_events);
     for (i = index; i < wes->n_events - 1; ++i)
6fbf66fa
     {
81d882d5
         wes->events[i] = wes->events[i+1];
         wes->esr[i] = wes->esr[i+1];
6fbf66fa
     }
81d882d5
     --wes->n_events;
6fbf66fa
 }
 
 static void
81d882d5
 we_get_rw_indices(struct we_set *wes, event_t event, int *ri, int *wi)
6fbf66fa
 {
81d882d5
     int i;
     *ri = *wi = -1;
     for (i = 0; i < wes->n_events; ++i)
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
 }
 
 static void
81d882d5
 we_free(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct we_set *wes = (struct we_set *) es;
     free(wes->events);
     free(wes->esr);
     free(wes);
6fbf66fa
 }
 
 static void
81d882d5
 we_reset(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct we_set *wes = (struct we_set *) es;
     ASSERT(wes->fast);
     wes->n_events = 0;
6fbf66fa
 }
 
 static void
81d882d5
 we_del(struct event_set *es, event_t event)
6fbf66fa
 {
81d882d5
     struct we_set *wes = (struct we_set *) es;
     ASSERT(!wes->fast);
     we_del_event(wes, event);
6fbf66fa
 }
 
 static void
81d882d5
 we_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     struct we_set *wes = (struct we_set *) es;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "WE_CTL n=%d ev=%p rwflags=0x%04x arg=" ptr_format,
          wes->n_events,
          event,
          rwflags,
          (ptr_type)arg);
6fbf66fa
 
81d882d5
     if (wes->fast)
6fbf66fa
     {
81d882d5
         if (!we_append_event(wes, event, rwflags, arg))
         {
             goto err;
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         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);
         }
6fbf66fa
     }
81d882d5
     return;
6fbf66fa
 
81d882d5
 err:
     msg(D_EVENT_ERRORS, "Error: Windows resource limit WSA_MAXIMUM_WAIT_EVENTS (%d) has been exceeded", WSA_MAXIMUM_WAIT_EVENTS);
6fbf66fa
 }
 
 static int
81d882d5
 we_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
6fbf66fa
 {
81d882d5
     struct we_set *wes = (struct we_set *) es;
     const int timeout = tv_to_ms_timeout(tv);
     DWORD status;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "WE_WAIT enter n=%d to=%d", wes->n_events, timeout);
6fbf66fa
 
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_EVENT_WAIT))
     {
         int i;
         for (i = 0; i < wes->n_events; ++i)
4cd4899e
         {
81d882d5
             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);
4cd4899e
         }
81d882d5
     }
6fbf66fa
 #endif
 
81d882d5
     /*
      * 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)
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         /*
          * 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;
         }
6fbf66fa
     }
 }
 
 static struct event_set *
81d882d5
 we_init(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct we_set *wes;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(wes, struct we_set);
6fbf66fa
 
81d882d5
     /* 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;
6fbf66fa
 
81d882d5
     if (flags & EVENT_METHOD_FAST)
     {
         wes->fast = true;
     }
     wes->n_events = 0;
6fbf66fa
 
81d882d5
     /* 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);
6fbf66fa
 
81d882d5
     /* Allocate space for Win32 event handles */
     ALLOC_ARRAY_CLEAR(wes->events, HANDLE, wes->capacity);
6fbf66fa
 
81d882d5
     /* Allocate space for event_set_return objects */
     ALLOC_ARRAY_CLEAR(wes->esr, struct event_set_return, wes->capacity);
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d capacity=%d",
          *maxevents, wes->capacity);
6fbf66fa
 
81d882d5
     return (struct event_set *) wes;
6fbf66fa
 }
 
445b192a
 #endif /* _WIN32 */
6fbf66fa
 
 #if EPOLL
 
 struct ep_set
 {
81d882d5
     struct event_set_functions func;
     bool fast;
     int epfd;
     int maxevents;
     struct epoll_event *events;
6fbf66fa
 };
 
 static void
81d882d5
 ep_free(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct ep_set *eps = (struct ep_set *) es;
     close(eps->epfd);
     free(eps->events);
     free(eps);
6fbf66fa
 }
 
 static void
81d882d5
 ep_reset(struct event_set *es)
6fbf66fa
 {
81d882d5
     const struct ep_set *eps = (struct ep_set *) es;
     ASSERT(eps->fast);
6fbf66fa
 }
 
 static void
81d882d5
 ep_del(struct event_set *es, event_t event)
6fbf66fa
 {
81d882d5
     struct epoll_event ev;
     struct ep_set *eps = (struct ep_set *) es;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "EP_DEL ev=%d", (int)event);
6fbf66fa
 
81d882d5
     ASSERT(!eps->fast);
     CLEAR(ev);
     epoll_ctl(eps->epfd, EPOLL_CTL_DEL, event, &ev);
6fbf66fa
 }
 
 static void
81d882d5
 ep_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     struct ep_set *eps = (struct ep_set *) es;
     struct epoll_event ev;
6fbf66fa
 
81d882d5
     CLEAR(ev);
6fbf66fa
 
81d882d5
     ev.data.ptr = arg;
     if (rwflags & EVENT_READ)
     {
         ev.events |= EPOLLIN;
     }
     if (rwflags & EVENT_WRITE)
     {
         ev.events |= EPOLLOUT;
     }
6fbf66fa
 
81d882d5
     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);
6fbf66fa
 
81d882d5
     if (epoll_ctl(eps->epfd, EPOLL_CTL_MOD, event, &ev) < 0)
6fbf66fa
     {
81d882d5
         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);
         }
6fbf66fa
     }
 }
 
 static int
81d882d5
 ep_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
6fbf66fa
 {
81d882d5
     struct ep_set *eps = (struct ep_set *) es;
     int stat;
6fbf66fa
 
81d882d5
     if (outlen > eps->maxevents)
     {
         outlen = eps->maxevents;
     }
6fbf66fa
 
81d882d5
     stat = epoll_wait(eps->epfd, eps->events, outlen, tv_to_ms_timeout(tv));
     ASSERT(stat <= outlen);
6fbf66fa
 
81d882d5
     if (stat > 0)
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
81d882d5
     return stat;
6fbf66fa
 }
 
 static struct event_set *
81d882d5
 ep_init(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct ep_set *eps;
     int fd;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "EP_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
6fbf66fa
 
81d882d5
     /* open epoll file descriptor */
     fd = epoll_create(*maxevents);
     if (fd < 0)
     {
         return NULL;
     }
6fbf66fa
 
81d882d5
     set_cloexec(fd);
09ee4192
 
81d882d5
     ALLOC_OBJ_CLEAR(eps, struct ep_set);
6fbf66fa
 
81d882d5
     /* 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;
6fbf66fa
 
81d882d5
     /* fast method ("sort of") corresponds to epoll one-shot */
     if (flags & EVENT_METHOD_FAST)
     {
         eps->fast = true;
     }
6fbf66fa
 
81d882d5
     /* allocate space for epoll_wait return */
     ASSERT(*maxevents > 0);
     eps->maxevents = *maxevents;
     ALLOC_ARRAY_CLEAR(eps->events, struct epoll_event, eps->maxevents);
6fbf66fa
 
81d882d5
     /* set epoll control fd */
     eps->epfd = fd;
6fbf66fa
 
81d882d5
     return (struct event_set *) eps;
6fbf66fa
 }
 #endif /* EPOLL */
 
 #if POLL
 
 struct po_set
 {
81d882d5
     struct event_set_functions func;
     bool fast;
     struct pollfd *events;
     void **args;
     int n_events;
     int capacity;
6fbf66fa
 };
 
 static void
81d882d5
 po_free(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct po_set *pos = (struct po_set *) es;
     free(pos->events);
     free(pos->args);
     free(pos);
6fbf66fa
 }
 
 static void
81d882d5
 po_reset(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct po_set *pos = (struct po_set *) es;
     ASSERT(pos->fast);
     pos->n_events = 0;
6fbf66fa
 }
 
 static void
81d882d5
 po_del(struct event_set *es, event_t event)
6fbf66fa
 {
81d882d5
     struct po_set *pos = (struct po_set *) es;
     int i;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "PO_DEL ev=%d", (int)event);
6fbf66fa
 
81d882d5
     ASSERT(!pos->fast);
     for (i = 0; i < pos->n_events; ++i)
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
 }
 
 static inline void
81d882d5
 po_set_pollfd_events(struct pollfd *pfdp, unsigned int rwflags)
6fbf66fa
 {
81d882d5
     pfdp->events = 0;
     if (rwflags & EVENT_WRITE)
     {
         pfdp->events |= POLLOUT;
     }
     if (rwflags & EVENT_READ)
     {
         pfdp->events |= (POLLIN|POLLPRI);
     }
6fbf66fa
 }
 
 static inline bool
81d882d5
 po_append_event(struct po_set *pos, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     if (pos->n_events < pos->capacity)
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
 }
 
 static void
81d882d5
 po_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     struct po_set *pos = (struct po_set *) es;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "PO_CTL rwflags=0x%04x ev=%d arg=" ptr_format,
          rwflags, (int)event, (ptr_type)arg);
6fbf66fa
 
81d882d5
     if (pos->fast)
6fbf66fa
     {
81d882d5
         if (!po_append_event(pos, event, rwflags, arg))
         {
             goto err;
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
 
81d882d5
 done:
     return;
6fbf66fa
 
81d882d5
 err:
     msg(D_EVENT_ERRORS, "Error: poll: too many I/O wait events");
6fbf66fa
 }
 
 static int
81d882d5
 po_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
6fbf66fa
 {
81d882d5
     struct po_set *pos = (struct po_set *) es;
     int stat;
6fbf66fa
 
81d882d5
     stat = poll(pos->events, pos->n_events, tv_to_ms_timeout(tv));
6fbf66fa
 
81d882d5
     ASSERT(stat <= pos->n_events);
6fbf66fa
 
81d882d5
     if (stat > 0)
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
81d882d5
     return stat;
6fbf66fa
 }
 
 static struct event_set *
81d882d5
 po_init(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct po_set *pos;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "PO_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(pos, struct po_set);
6fbf66fa
 
81d882d5
     /* 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;
6fbf66fa
 
81d882d5
     if (flags & EVENT_METHOD_FAST)
     {
         pos->fast = true;
     }
6fbf66fa
 
81d882d5
     pos->n_events = 0;
6fbf66fa
 
81d882d5
     /* Figure our event capacity */
     ASSERT(*maxevents > 0);
     pos->capacity = *maxevents;
6fbf66fa
 
81d882d5
     /* Allocate space for pollfd structures to be passed to poll() */
     ALLOC_ARRAY_CLEAR(pos->events, struct pollfd, pos->capacity);
6fbf66fa
 
81d882d5
     /* Allocate space for event_set_return objects */
     ALLOC_ARRAY_CLEAR(pos->args, void *, pos->capacity);
6fbf66fa
 
81d882d5
     return (struct event_set *) pos;
6fbf66fa
 }
 #endif /* POLL */
 
 #if SELECT
 
 struct se_set
 {
81d882d5
     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 */
6fbf66fa
 };
 
 static void
81d882d5
 se_free(struct event_set *es)
6fbf66fa
 {
81d882d5
     struct se_set *ses = (struct se_set *) es;
     free(ses->args);
     free(ses);
6fbf66fa
 }
 
 static void
81d882d5
 se_reset(struct event_set *es)
6fbf66fa
 {
81d882d5
     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)
4cd4899e
     {
81d882d5
         ses->args[i] = NULL;
4cd4899e
     }
81d882d5
     ses->maxfd = -1;
6fbf66fa
 }
 
 static void
81d882d5
 se_del(struct event_set *es, event_t event)
6fbf66fa
 {
81d882d5
     struct se_set *ses = (struct se_set *) es;
     ASSERT(!ses->fast);
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "SE_DEL ev=%d", (int)event);
6fbf66fa
 
81d882d5
     if (event >= 0 && event < ses->capacity)
     {
         FD_CLR(event, &ses->readfds);
         FD_CLR(event, &ses->writefds);
         ses->args[event] = NULL;
     }
     else
6fbf66fa
     {
81d882d5
         msg(D_EVENT_ERRORS, "Error: select/se_del: too many I/O wait events");
6fbf66fa
     }
81d882d5
     return;
6fbf66fa
 }
 
 static void
81d882d5
 se_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg)
6fbf66fa
 {
81d882d5
     struct se_set *ses = (struct se_set *) es;
6fbf66fa
 
81d882d5
     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);
6fbf66fa
 
81d882d5
     if (event >= 0 && event < ses->capacity)
6fbf66fa
     {
81d882d5
         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);
             }
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         msg(D_EVENT_ERRORS, "Error: select: too many I/O wait events, fd=%d cap=%d",
             (int) event,
             ses->capacity);
6fbf66fa
     }
 }
 
 static int
81d882d5
 se_wait_return(struct se_set *ses,
                fd_set *read,
                fd_set *write,
                struct event_set_return *out,
                int outlen)
6fbf66fa
 {
81d882d5
     int i, j = 0;
     for (i = 0; i <= ses->maxfd && j < outlen; ++i)
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
81d882d5
     return j;
6fbf66fa
 }
 
 static int
81d882d5
 se_wait_fast(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
6fbf66fa
 {
81d882d5
     struct se_set *ses = (struct se_set *) es;
     struct timeval tv_tmp = *tv;
     int stat;
6fbf66fa
 
06ad53e0
     dmsg(D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%"PRIi64"/%ld",
81d882d5
          ses->maxfd,
06ad53e0
          (int64_t)tv_tmp.tv_sec,
31b5c0e9
          (long)tv_tmp.tv_usec);
6fbf66fa
 
81d882d5
     stat = select(ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp);
6fbf66fa
 
81d882d5
     if (stat > 0)
     {
         stat = se_wait_return(ses, &ses->readfds, &ses->writefds, out, outlen);
     }
6fbf66fa
 
81d882d5
     return stat;
6fbf66fa
 }
 
 static int
81d882d5
 se_wait_scalable(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
6fbf66fa
 {
81d882d5
     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;
6fbf66fa
 
06ad53e0
     dmsg(D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%"PRIi64"/%ld",
          ses->maxfd, (int64_t)tv_tmp.tv_sec, (long)tv_tmp.tv_usec);
6fbf66fa
 
81d882d5
     stat = select(ses->maxfd + 1, &read, &write, NULL, &tv_tmp);
6fbf66fa
 
81d882d5
     if (stat > 0)
     {
         stat = se_wait_return(ses, &read, &write, out, outlen);
     }
6fbf66fa
 
81d882d5
     return stat;
6fbf66fa
 }
 
 static struct event_set *
81d882d5
 se_init(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct se_set *ses;
6fbf66fa
 
81d882d5
     dmsg(D_EVENT_WAIT, "SE_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(ses, struct se_set);
6fbf66fa
 
81d882d5
     /* 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;
6fbf66fa
 
81d882d5
     if (flags & EVENT_METHOD_FAST)
6fbf66fa
     {
81d882d5
         ses->fast = true;
         ses->func.wait = se_wait_fast;
6fbf66fa
     }
 
81d882d5
     /* Select needs to be passed this value + 1 */
     ses->maxfd = -1;
6fbf66fa
 
81d882d5
     /* Set our event capacity */
     ASSERT(*maxevents > 0);
     *maxevents = min_int(*maxevents, SELECT_MAX_FDS);
     ses->capacity = SELECT_MAX_FDS;
6fbf66fa
 
81d882d5
     /* Allocate space for event_set_return void * args */
     ALLOC_ARRAY_CLEAR(ses->args, void *, ses->capacity);
6fbf66fa
 
81d882d5
     return (struct event_set *) ses;
6fbf66fa
 }
 #endif /* SELECT */
 
 static struct event_set *
81d882d5
 event_set_init_simple(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct event_set *ret = NULL;
445b192a
 #ifdef _WIN32
81d882d5
     ret = we_init(maxevents, flags);
6fbf66fa
 #elif POLL && SELECT
 #if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */
81d882d5
     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);
     }
6fbf66fa
 #endif
 #elif POLL
81d882d5
     ret = po_init(maxevents, flags);
6fbf66fa
 #elif SELECT
81d882d5
     ret = se_init(maxevents, flags);
 #else  /* ifdef _WIN32 */
6fbf66fa
 #error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel
81d882d5
 #endif /* ifdef _WIN32 */
     ASSERT(ret);
     return ret;
6fbf66fa
 }
 
 static struct event_set *
81d882d5
 event_set_init_scalable(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     struct event_set *ret = NULL;
6fbf66fa
 #if EPOLL
81d882d5
     ret = ep_init(maxevents, flags);
     if (!ret)
6fbf66fa
     {
81d882d5
         msg(M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API");
         ret = event_set_init_simple(maxevents, flags);
6fbf66fa
     }
81d882d5
 #else  /* if EPOLL */
     ret = event_set_init_simple(maxevents, flags);
6fbf66fa
 #endif
81d882d5
     ASSERT(ret);
     return ret;
6fbf66fa
 }
 
 struct event_set *
81d882d5
 event_set_init(int *maxevents, unsigned int flags)
6fbf66fa
 {
81d882d5
     if (flags & EVENT_METHOD_FAST)
     {
         return event_set_init_simple(maxevents, flags);
     }
     else
     {
         return event_set_init_scalable(maxevents, flags);
     }
6fbf66fa
 }