fragattacks/src/utils/eloop_win.c
Johannes Berg 594516b4c2 Use monotonic clock for relative time for eloop if available
Relative time shouldn't be calculated based on gettimeofday
because that clock can jump (e.g., when the time is adjusted
by the system administrator.)

On systems where that is available, use CLOCK_BOOTTIME (on
fairly recent Linux systems, this clock takes into account
the time spend suspended) or CLOCK_MONOTONIC (on Linux and
some POSIX systems, this clock is just freely running with
no adjustments.)

Reported-by: Holger Schurig <holgerschurig@gmail.com>
Signed-hostap: Johannes Berg <johannes.berg@intel.com>
2013-11-20 23:52:56 +02:00

693 lines
15 KiB
C

/*
* Event loop based on Windows events and WaitForMultipleObjects
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <winsock2.h>
#include "common.h"
#include "list.h"
#include "eloop.h"
struct eloop_sock {
int sock;
void *eloop_data;
void *user_data;
eloop_sock_handler handler;
WSAEVENT event;
};
struct eloop_event {
void *eloop_data;
void *user_data;
eloop_event_handler handler;
HANDLE event;
};
struct eloop_timeout {
struct dl_list list;
struct os_reltime time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
};
struct eloop_signal {
int sig;
void *user_data;
eloop_signal_handler handler;
int signaled;
};
struct eloop_data {
int max_sock;
size_t reader_count;
struct eloop_sock *readers;
size_t event_count;
struct eloop_event *events;
struct dl_list timeout;
int signal_count;
struct eloop_signal *signals;
int signaled;
int pending_terminate;
int terminate;
int reader_table_changed;
struct eloop_signal term_signal;
HANDLE term_event;
HANDLE *handles;
size_t num_handles;
};
static struct eloop_data eloop;
int eloop_init(void)
{
os_memset(&eloop, 0, sizeof(eloop));
dl_list_init(&eloop.timeout);
eloop.num_handles = 1;
eloop.handles = os_malloc(eloop.num_handles *
sizeof(eloop.handles[0]));
if (eloop.handles == NULL)
return -1;
eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (eloop.term_event == NULL) {
printf("CreateEvent() failed: %d\n",
(int) GetLastError());
os_free(eloop.handles);
return -1;
}
return 0;
}
static int eloop_prepare_handles(void)
{
HANDLE *n;
if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
return 0;
n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
sizeof(eloop.handles[0]));
if (n == NULL)
return -1;
eloop.handles = n;
eloop.num_handles *= 2;
return 0;
}
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
WSAEVENT event;
struct eloop_sock *tmp;
if (eloop_prepare_handles())
return -1;
event = WSACreateEvent();
if (event == WSA_INVALID_EVENT) {
printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
return -1;
}
if (WSAEventSelect(sock, event, FD_READ)) {
printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
WSACloseEvent(event);
return -1;
}
tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
sizeof(struct eloop_sock));
if (tmp == NULL) {
WSAEventSelect(sock, event, 0);
WSACloseEvent(event);
return -1;
}
tmp[eloop.reader_count].sock = sock;
tmp[eloop.reader_count].eloop_data = eloop_data;
tmp[eloop.reader_count].user_data = user_data;
tmp[eloop.reader_count].handler = handler;
tmp[eloop.reader_count].event = event;
eloop.reader_count++;
eloop.readers = tmp;
if (sock > eloop.max_sock)
eloop.max_sock = sock;
eloop.reader_table_changed = 1;
return 0;
}
void eloop_unregister_read_sock(int sock)
{
size_t i;
if (eloop.readers == NULL || eloop.reader_count == 0)
return;
for (i = 0; i < eloop.reader_count; i++) {
if (eloop.readers[i].sock == sock)
break;
}
if (i == eloop.reader_count)
return;
WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
WSACloseEvent(eloop.readers[i].event);
if (i != eloop.reader_count - 1) {
os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
(eloop.reader_count - i - 1) *
sizeof(struct eloop_sock));
}
eloop.reader_count--;
eloop.reader_table_changed = 1;
}
int eloop_register_event(void *event, size_t event_size,
eloop_event_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_event *tmp;
HANDLE h = event;
if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
return -1;
if (eloop_prepare_handles())
return -1;
tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
sizeof(struct eloop_event));
if (tmp == NULL)
return -1;
tmp[eloop.event_count].eloop_data = eloop_data;
tmp[eloop.event_count].user_data = user_data;
tmp[eloop.event_count].handler = handler;
tmp[eloop.event_count].event = h;
eloop.event_count++;
eloop.events = tmp;
return 0;
}
void eloop_unregister_event(void *event, size_t event_size)
{
size_t i;
HANDLE h = event;
if (eloop.events == NULL || eloop.event_count == 0 ||
event_size != sizeof(HANDLE))
return;
for (i = 0; i < eloop.event_count; i++) {
if (eloop.events[i].event == h)
break;
}
if (i == eloop.event_count)
return;
if (i != eloop.event_count - 1) {
os_memmove(&eloop.events[i], &eloop.events[i + 1],
(eloop.event_count - i - 1) *
sizeof(struct eloop_event));
}
eloop.event_count--;
}
int eloop_register_timeout(unsigned int secs, unsigned int usecs,
eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp;
os_time_t now_sec;
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
if (os_get_reltime(&timeout->time) < 0) {
os_free(timeout);
return -1;
}
now_sec = timeout->time.sec;
timeout->time.sec += secs;
if (timeout->time.sec < now_sec) {
/*
* Integer overflow - assume long enough timeout to be assumed
* to be infinite, i.e., the timeout would never happen.
*/
wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
"ever happen - ignore it", secs);
os_free(timeout);
return 0;
}
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
timeout->time.usec -= 1000000;
}
timeout->eloop_data = eloop_data;
timeout->user_data = user_data;
timeout->handler = handler;
/* Maintain timeouts in order of increasing time */
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (os_reltime_before(&timeout->time, &tmp->time)) {
dl_list_add(tmp->list.prev, &timeout->list);
return 0;
}
}
dl_list_add_tail(&eloop.timeout, &timeout->list);
return 0;
}
static void eloop_remove_timeout(struct eloop_timeout *timeout)
{
dl_list_del(&timeout->list);
os_free(timeout);
}
int eloop_cancel_timeout(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
if (timeout->handler == handler &&
(timeout->eloop_data == eloop_data ||
eloop_data == ELOOP_ALL_CTX) &&
(timeout->user_data == user_data ||
user_data == ELOOP_ALL_CTX)) {
eloop_remove_timeout(timeout);
removed++;
}
}
return removed;
}
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
void *eloop_data, void *user_data,
struct os_reltime *remaining)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
struct os_reltime now;
os_get_reltime(&now);
remaining->sec = remaining->usec = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
if (timeout->handler == handler &&
(timeout->eloop_data == eloop_data) &&
(timeout->user_data == user_data)) {
removed = 1;
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, remaining);
eloop_remove_timeout(timeout);
break;
}
}
return removed;
}
int eloop_is_timeout_registered(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data)
return 1;
}
return 0;
}
int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
eloop_timeout_handler handler, void *eloop_data,
void *user_data)
{
struct os_reltime now, requested, remaining;
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data) {
requested.sec = req_secs;
requested.usec = req_usecs;
os_get_reltime(&now);
os_reltime_sub(&tmp->time, &now, &remaining);
if (os_reltime_before(&requested, &remaining)) {
eloop_cancel_timeout(handler, eloop_data,
user_data);
eloop_register_timeout(requested.sec,
requested.usec,
handler, eloop_data,
user_data);
return 1;
}
}
}
return 0;
}
int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
eloop_timeout_handler handler, void *eloop_data,
void *user_data)
{
struct os_reltime now, requested, remaining;
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data) {
requested.sec = req_secs;
requested.usec = req_usecs;
os_get_reltime(&now);
os_reltime_sub(&tmp->time, &now, &remaining);
if (os_reltime_before(&remaining, &requested)) {
eloop_cancel_timeout(handler, eloop_data,
user_data);
eloop_register_timeout(requested.sec,
requested.usec,
handler, eloop_data,
user_data);
return 1;
}
}
}
return 0;
}
/* TODO: replace with suitable signal handler */
#if 0
static void eloop_handle_signal(int sig)
{
int i;
eloop.signaled++;
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].sig == sig) {
eloop.signals[i].signaled++;
break;
}
}
}
#endif
static void eloop_process_pending_signals(void)
{
int i;
if (eloop.signaled == 0)
return;
eloop.signaled = 0;
if (eloop.pending_terminate) {
eloop.pending_terminate = 0;
}
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].signaled) {
eloop.signals[i].signaled = 0;
eloop.signals[i].handler(eloop.signals[i].sig,
eloop.signals[i].user_data);
}
}
if (eloop.term_signal.signaled) {
eloop.term_signal.signaled = 0;
eloop.term_signal.handler(eloop.term_signal.sig,
eloop.term_signal.user_data);
}
}
int eloop_register_signal(int sig, eloop_signal_handler handler,
void *user_data)
{
struct eloop_signal *tmp;
tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
sizeof(struct eloop_signal));
if (tmp == NULL)
return -1;
tmp[eloop.signal_count].sig = sig;
tmp[eloop.signal_count].user_data = user_data;
tmp[eloop.signal_count].handler = handler;
tmp[eloop.signal_count].signaled = 0;
eloop.signal_count++;
eloop.signals = tmp;
/* TODO: register signal handler */
return 0;
}
#ifndef _WIN32_WCE
static BOOL eloop_handle_console_ctrl(DWORD type)
{
switch (type) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
eloop.signaled++;
eloop.term_signal.signaled++;
SetEvent(eloop.term_event);
return TRUE;
default:
return FALSE;
}
}
#endif /* _WIN32_WCE */
int eloop_register_signal_terminate(eloop_signal_handler handler,
void *user_data)
{
#ifndef _WIN32_WCE
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
TRUE) == 0) {
printf("SetConsoleCtrlHandler() failed: %d\n",
(int) GetLastError());
return -1;
}
#endif /* _WIN32_WCE */
eloop.term_signal.handler = handler;
eloop.term_signal.user_data = user_data;
return 0;
}
int eloop_register_signal_reconfig(eloop_signal_handler handler,
void *user_data)
{
/* TODO */
return 0;
}
void eloop_run(void)
{
struct os_reltime tv, now;
DWORD count, ret, timeout_val, err;
size_t i;
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 ||
eloop.event_count > 0)) {
struct eloop_timeout *timeout;
tv.sec = tv.usec = 0;
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, &tv);
}
count = 0;
for (i = 0; i < eloop.event_count; i++)
eloop.handles[count++] = eloop.events[i].event;
for (i = 0; i < eloop.reader_count; i++)
eloop.handles[count++] = eloop.readers[i].event;
if (eloop.term_event)
eloop.handles[count++] = eloop.term_event;
if (timeout)
timeout_val = tv.sec * 1000 + tv.usec / 1000;
else
timeout_val = INFINITE;
if (count > MAXIMUM_WAIT_OBJECTS) {
printf("WaitForMultipleObjects: Too many events: "
"%d > %d (ignoring extra events)\n",
(int) count, MAXIMUM_WAIT_OBJECTS);
count = MAXIMUM_WAIT_OBJECTS;
}
#ifdef _WIN32_WCE
ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
timeout_val);
#else /* _WIN32_WCE */
ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
timeout_val, TRUE);
#endif /* _WIN32_WCE */
err = GetLastError();
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (!os_reltime_before(&now, &timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler =
timeout->handler;
eloop_remove_timeout(timeout);
handler(eloop_data, user_data);
}
}
if (ret == WAIT_FAILED) {
printf("WaitForMultipleObjects(count=%d) failed: %d\n",
(int) count, (int) err);
os_sleep(1, 0);
continue;
}
#ifndef _WIN32_WCE
if (ret == WAIT_IO_COMPLETION)
continue;
#endif /* _WIN32_WCE */
if (ret == WAIT_TIMEOUT)
continue;
while (ret >= WAIT_OBJECT_0 &&
ret < WAIT_OBJECT_0 + eloop.event_count) {
eloop.events[ret].handler(
eloop.events[ret].eloop_data,
eloop.events[ret].user_data);
ret = WaitForMultipleObjects(eloop.event_count,
eloop.handles, FALSE, 0);
}
eloop.reader_table_changed = 0;
for (i = 0; i < eloop.reader_count; i++) {
WSANETWORKEVENTS events;
if (WSAEnumNetworkEvents(eloop.readers[i].sock,
eloop.readers[i].event,
&events) == 0 &&
(events.lNetworkEvents & FD_READ)) {
eloop.readers[i].handler(
eloop.readers[i].sock,
eloop.readers[i].eloop_data,
eloop.readers[i].user_data);
if (eloop.reader_table_changed)
break;
}
}
}
}
void eloop_terminate(void)
{
eloop.terminate = 1;
SetEvent(eloop.term_event);
}
void eloop_destroy(void)
{
struct eloop_timeout *timeout, *prev;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
eloop_remove_timeout(timeout);
}
os_free(eloop.readers);
os_free(eloop.signals);
if (eloop.term_event)
CloseHandle(eloop.term_event);
os_free(eloop.handles);
eloop.handles = NULL;
os_free(eloop.events);
eloop.events = NULL;
}
int eloop_terminated(void)
{
return eloop.terminate;
}
void eloop_wait_for_read_sock(int sock)
{
WSAEVENT event;
event = WSACreateEvent();
if (event == WSA_INVALID_EVENT) {
printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
return;
}
if (WSAEventSelect(sock, event, FD_READ)) {
printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
WSACloseEvent(event);
return ;
}
WaitForSingleObject(event, INFINITE);
WSAEventSelect(sock, event, 0);
WSACloseEvent(event);
}