From: Wouter Wijngaards Date: Tue, 17 Jun 2008 14:57:18 +0000 (+0000) Subject: winsock_event handler, working resolution and validation on windows. X-Git-Tag: release-1.0.1~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7059107b3f229e015a7c58849a52a438581525d;p=thirdparty%2Funbound.git winsock_event handler, working resolution and validation on windows. git-svn-id: file:///svn/unbound/trunk@1124 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/config.h.in b/config.h.in index 788ab1d1d..efe31585e 100644 --- a/config.h.in +++ b/config.h.in @@ -63,6 +63,9 @@ /* Define to 1 if you have the `gmtime_r' function. */ #undef HAVE_GMTIME_R +/* If you have HMAC_CTX_init */ +#undef HAVE_HMAC_CTX_INIT + /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON @@ -81,9 +84,6 @@ /* Define to 1 if you have the `kill' function. */ #undef HAVE_KILL -/* Define to 1 if you have the `crypto' library (-lcrypto). */ -#undef HAVE_LIBCRYPTO - /* Define to 1 if you have the `ldns' library (-lldns). */ #undef HAVE_LIBLDNS diff --git a/configure b/configure index 37bae2769..948ec1496 100755 --- a/configure +++ b/configure @@ -19675,14 +19675,11 @@ echo "${ECHO_T}found in $ssldir" >&6 RUNTIME_PATH="$RUNTIME_PATH -R$ssldir/lib" fi -echo "$as_me:$LINENO: checking for HMAC_CTX_init in -lcrypto" >&5 + echo "$as_me:$LINENO: checking for HMAC_CTX_init in -lcrypto" >&5 echo $ECHO_N "checking for HMAC_CTX_init in -lcrypto... $ECHO_C" >&6 -if test "${ac_cv_lib_crypto_HMAC_CTX_init+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcrypto $LIBS" -cat >conftest.$ac_ext <<_ACEOF + ORIGLIBS="$LIBS" + LIBS="$LIBS -lcrypto" + cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF @@ -19690,17 +19687,13 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char HMAC_CTX_init (); int main () { -HMAC_CTX_init (); + + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ; return 0; } @@ -19717,29 +19710,24 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_lib_crypto_HMAC_CTX_init=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 -ac_cv_lib_crypto_HMAC_CTX_init=no -fi -rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_HMAC_CTX_init" >&5 -echo "${ECHO_T}$ac_cv_lib_crypto_HMAC_CTX_init" >&6 -if test $ac_cv_lib_crypto_HMAC_CTX_init = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBCRYPTO 1 + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_HMAC_CTX_INIT 1 _ACEOF - LIBS="-lcrypto $LIBS" else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 # check if -lwsock32 or -lgdi32 are needed. - LIBS="$LIBS -lcrypto -lgdi32" + LIBS="$LIBS -lgdi32" echo "$as_me:$LINENO: checking if -lcrypto needs -lgdi32" >&5 echo $ECHO_N "checking if -lcrypto needs -lgdi32... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF @@ -19754,28 +19742,34 @@ int main () { - void HMAC_CTX_init(void); - (void)HMAC_CTX_init(); + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' + { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_HMAC_CTX_INIT 1 +_ACEOF + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 + else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 @@ -19788,10 +19782,10 @@ echo "$as_me: error: OpenSSL found in $ssldir, but version 0.9.7 or higher is re { (exit 1); exit 1; }; } fi -rm -f conftest.$ac_objext conftest.$ac_ext +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi - +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi diff --git a/configure.ac b/configure.ac index 0202bc66e..fcea3bddf 100644 --- a/configure.ac +++ b/configure.ac @@ -510,15 +510,30 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname], if test "x$enable_rpath" = xyes; then RUNTIME_PATH="$RUNTIME_PATH -R$ssldir/lib" fi - AC_CHECK_LIB(crypto, HMAC_CTX_init,, [ + + AC_MSG_CHECKING([for HMAC_CTX_init in -lcrypto]) + ORIGLIBS="$LIBS" + LIBS="$LIBS -lcrypto" + AC_TRY_LINK(, [ + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_HMAC_CTX_INIT], 1, + [If you have HMAC_CTX_init]) + ], [ + AC_MSG_RESULT(no) # check if -lwsock32 or -lgdi32 are needed. - LIBS="$LIBS -lcrypto -lgdi32" + LIBS="$LIBS -lgdi32" AC_MSG_CHECKING([if -lcrypto needs -lgdi32]) - AC_TRY_COMPILE([], [ - void HMAC_CTX_init(void); - (void)HMAC_CTX_init(); - ], [ - AC_MSG_RESULT(yes) ],[ + AC_TRY_LINK([], [ + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ],[ + AC_DEFINE([HAVE_HMAC_CTX_INIT], 1, + [If you have HMAC_CTX_init]) + AC_MSG_RESULT(yes) + ],[ AC_MSG_RESULT(no) AC_MSG_ERROR([OpenSSL found in $ssldir, but version 0.9.7 or higher is required]) ]) diff --git a/daemon/unbound.c b/daemon/unbound.c index 4d0f31882..7e7174180 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -61,9 +61,13 @@ #endif #ifdef USE_MINI_EVENT -#include "util/mini_event.h" +# ifdef USE_WINSOCK +# include "util/winsock_event.h" +# else +# include "util/mini_event.h" +# endif #else -#include +# include #endif /** global debug value to keep track of heap memory allocation */ diff --git a/doc/Changelog b/doc/Changelog index aeed63a60..bbab8f2eb 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,11 @@ +17 June 2008: Wouter + - outgoing num fds 32 by default on windows ; it supports less + fds for waiting on than unixes. + - winsock_event minievent handler for windows. (you could also + attempt to link with libevent/libev ports for windows). + - neater crypto check and gdi32 detection. + - unbound.exe works to resolve and validate www.nlnetlabs.nl on vista. + 16 June 2008: Wouter - on windows, use windows threads, mutex and thread-local-storage(Tls). - detect if openssl needs gdi32. diff --git a/util/config_file.c b/util/config_file.c index a333c9679..5f806c3c5 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -82,7 +82,11 @@ config_create() cfg->do_udp = 1; cfg->do_tcp = 1; cfg->use_syslog = 1; +#ifndef USE_WINSOCK cfg->outgoing_num_ports = 256; +#else + cfg->outgoing_num_ports = 32; /* windows is limited in num fds */ +#endif cfg->outgoing_num_tcp = 10; cfg->incoming_num_tcp = 10; cfg->msg_buffer_size = 65552; /* 64 k + a small margin */ diff --git a/util/mini_event.c b/util/mini_event.c index 198a1b872..2f84dd4b3 100644 --- a/util/mini_event.c +++ b/util/mini_event.c @@ -42,7 +42,7 @@ #include "config.h" -#ifdef USE_MINI_EVENT +#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK) #include #include "util/mini_event.h" #include "util/fptr_wlist.h" @@ -376,8 +376,10 @@ int signal_del(struct event* ev) } #else /* USE_MINI_EVENT */ +#ifndef USE_WINSOCK int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) { return 0; } +#endif /* not USE_WINSOCK */ #endif /* USE_MINI_EVENT */ diff --git a/util/mini_event.h b/util/mini_event.h index 1abb347b6..61cbcfbdb 100644 --- a/util/mini_event.h +++ b/util/mini_event.h @@ -52,7 +52,7 @@ #ifndef MINI_EVENT_H #define MINI_EVENT_H -#ifdef USE_MINI_EVENT +#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK) #ifndef HAVE_EVENT_BASE_FREE #define HAVE_EVENT_BASE_FREE @@ -124,7 +124,7 @@ struct event { void *ev_arg; }; -/* function prototypes as they appear in event.h */ +/* function prototypes (some are as they appear in event.h) */ /** create event base */ void *event_init(uint32_t* time_secs, struct timeval* time_tv); /** get version */ @@ -159,7 +159,7 @@ int signal_add(struct event *, struct timeval *); /** remove signal handler */ int signal_del(struct event *); -#endif /* USE_MINI_EVENT */ +#endif /* USE_MINI_EVENT and not USE_WINSOCK */ /** compare events in tree, based on timevalue, ptr for uniqueness */ int mini_ev_cmp(const void* a, const void* b); diff --git a/util/netevent.c b/util/netevent.c index 9197e4726..13aedee70 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -54,11 +54,15 @@ /* We define libevent structures here to hide the libevent stuff. */ #ifdef USE_MINI_EVENT -#include "util/mini_event.h" -#else -/* we use libevent */ -#include -#endif +# ifdef USE_WINSOCK +# include "util/winsock_event.h" +# else +# include "util/mini_event.h" +# endif /* USE_WINSOCK */ +#else /* USE_MINI_EVENT */ + /* we use libevent */ +# include +#endif /* USE_MINI_EVENT */ /** * The internal event structure for keeping libevent info for the event. diff --git a/util/winsock_event.c b/util/winsock_event.c new file mode 100644 index 000000000..07ac22430 --- /dev/null +++ b/util/winsock_event.c @@ -0,0 +1,478 @@ +/* + * util/winsock_event.c - implementation of the unbound winsock event handler. + * + * Copyright (c) 2008, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file + * Implementation of the unbound WinSock2 API event notification handler + * for the Windows port. + */ + +#include "config.h" +#ifdef USE_WINSOCK +#include +#include "util/winsock_event.h" +#include "util/fptr_wlist.h" + +int mini_ev_cmp(const void* a, const void* b) +{ + const struct event *e = (const struct event*)a; + const struct event *f = (const struct event*)b; + if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec) + return -1; + if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec) + return 1; + if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec) + return -1; + if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec) + return 1; + if(e < f) + return -1; + if(e > f) + return 1; + return 0; +} + +/** set time */ +static int +settime(struct event_base* base) +{ + if(gettimeofday(base->time_tv, NULL) < 0) { + return -1; + } +#ifndef S_SPLINT_S + *base->time_secs = (uint32_t)base->time_tv->tv_sec; +#endif + return 0; +} + +/** + * Find a fd in the list of items. + * Note that not all items have a fd associated (those are -1). + * Signals are stored separately, and not searched. + * @param base: event base to look in. + * @param fd: what socket to look for. + * @return the index in the array, or -1 on failure. + */ +static int +find_fd(struct event_base* base, int fd) +{ + int i; + for(i=0; imax; i++) { + if(base->items[i]->ev_fd == fd) + return i; + } + return -1; +} + +void *event_init(uint32_t* time_secs, struct timeval* time_tv) +{ + struct event_base* base = (struct event_base*)malloc( + sizeof(struct event_base)); + if(!base) + return NULL; + memset(base, 0, sizeof(*base)); + base->time_secs = time_secs; + base->time_tv = time_tv; + if(settime(base) < 0) { + event_base_free(base); + return NULL; + } + base->items = (struct event**)calloc(WSK_MAX_ITEMS, + sizeof(struct event*)); + if(!base->items) { + event_base_free(base); + return NULL; + } + base->cap = WSK_MAX_ITEMS; + base->max = 0; + base->times = rbtree_create(mini_ev_cmp); + if(!base->times) { + event_base_free(base); + return NULL; + } + base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*)); + if(!base->signals) { + event_base_free(base); + return NULL; + } + return base; +} + +const char *event_get_version(void) +{ + return "winsock-event-"PACKAGE_VERSION; +} + +const char *event_get_method(void) +{ + return "WSAWaitForMultipleEvents"; +} + +/** call timeouts handlers, and return how long to wait for next one or -1 */ +static void handle_timeouts(struct event_base* base, struct timeval* now, + struct timeval* wait) +{ + struct event* p; +#ifndef S_SPLINT_S + wait->tv_sec = (time_t)-1; +#endif + + while((rbnode_t*)(p = (struct event*)rbtree_first(base->times)) + !=RBTREE_NULL) { +#ifndef S_SPLINT_S + if(p->ev_timeout.tv_sec > now->tv_sec || + (p->ev_timeout.tv_sec==now->tv_sec && + p->ev_timeout.tv_usec > now->tv_usec)) { + /* there is a next larger timeout. wait for it */ + wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec; + if(now->tv_usec > p->ev_timeout.tv_usec) { + wait->tv_sec--; + wait->tv_usec = 1000000 - (now->tv_usec - + p->ev_timeout.tv_usec); + } else { + wait->tv_usec = p->ev_timeout.tv_usec + - now->tv_usec; + } + return; + } +#endif + /* event times out, remove it */ + (void)rbtree_delete(base->times, p); + p->ev_events &= ~EV_TIMEOUT; + fptr_ok(fptr_whitelist_event(p->ev_callback)); + (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); + } +} + +/** call select and callbacks for that */ +static int handle_select(struct event_base* base, struct timeval* wait) +{ + DWORD timeout = 0; /* in milliseconds */ + DWORD ret; + WSAEVENT waitfor[WSK_MAX_ITEMS]; + struct event* eventlist[WSK_MAX_ITEMS]; + WSANETWORKEVENTS netev; + int i, numwait = 0, startidx = 0, was_timeout = 0; + +#ifndef S_SPLINT_S + if(wait->tv_sec==(time_t)-1) + wait = NULL; + if(wait) + timeout = wait->tv_sec*1000 + wait->tv_usec/1000; +#endif + + /* prepare event array */ + for(i=0; imax; i++) { + if(base->items[i]->ev_fd == -1) + continue; /* skip timer only events */ + eventlist[numwait] = base->items[i]; + waitfor[numwait++] = base->items[i]->hEvent; + } + log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS); + + + /* do the wait */ + if(numwait == 0) { + /* WSAWaitFor.. doesn't like 0 event objects */ + if(wait) { + Sleep(timeout); + } + was_timeout = 1; + } else { + ret = WSAWaitForMultipleEvents(numwait, waitfor, + 0 /* do not wait for all, just one will do */, + wait?timeout:WSA_INFINITE, + 0); /* we are not alertable (IO completion events) */ + if(ret == WSA_WAIT_IO_COMPLETION) { + log_err("WSAWaitForMultipleEvents failed: WSA_WAIT_IO_COMPLETION"); + return -1; + } else if(ret == WSA_WAIT_FAILED) { + log_err("WSAWaitForMultipleEvents failed: %s", + wsa_strerror(WSAGetLastError())); + return -1; + } else if(ret == WSA_WAIT_TIMEOUT) { + was_timeout = 1; + } else + startidx = ret - WSA_WAIT_EVENT_0; + } + + /* get new time after wait */ + if(settime(base) < 0) + return -1; + + /* callbacks */ + if(was_timeout) + return 0; + + for(i=startidx; iev_fd, + /*waitfor[i],*/ /* reset the event handle */ + NULL, /* do not reset the event handle */ + &netev) != 0) { + log_err("WSAEnumNetworkEvents failed: %s", + wsa_strerror(WSAGetLastError())); + return -1; + } + if((netev.lNetworkEvents & FD_READ)) { + if(netev.iErrorCode[FD_READ_BIT] != 0) + log_err("FD_READ_BIT error: %s", + wsa_strerror(netev.iErrorCode[FD_READ_BIT])); + bits |= EV_READ; + } + if((netev.lNetworkEvents & FD_WRITE)) { + if(netev.iErrorCode[FD_WRITE_BIT] != 0) + log_err("FD_WRITE_BIT error: %s", + wsa_strerror(netev.iErrorCode[FD_WRITE_BIT])); + bits |= EV_WRITE; + } + if((netev.lNetworkEvents & FD_CONNECT)) { + if(netev.iErrorCode[FD_CONNECT_BIT] != 0) + log_err("FD_CONNECT_BIT error: %s", + wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT])); + bits |= EV_READ; + bits |= EV_WRITE; + } + if((netev.lNetworkEvents & FD_ACCEPT)) { + if(netev.iErrorCode[FD_ACCEPT_BIT] != 0) + log_err("FD_ACCEPT_BIT error: %s", + wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT])); + bits |= EV_READ; + } + if((netev.lNetworkEvents & FD_CLOSE)) { + if(netev.iErrorCode[FD_CLOSE_BIT] != 0) + log_err("FD_CLOSE_BIT error: %s", + wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT])); + bits |= EV_READ; + bits |= EV_WRITE; + } + if(bits) { + fptr_ok(fptr_whitelist_event( + eventlist[i]->ev_callback)); + (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd, + bits, eventlist[i]->ev_arg); + } + } + return 0; +} + +int event_base_dispatch(struct event_base *base) +{ + struct timeval wait; + if(settime(base) < 0) + return -1; + while(!base->need_to_exit) + { + /* see if timeouts need handling */ + handle_timeouts(base, base->time_tv, &wait); + if(base->need_to_exit) + return 0; + /* do select */ + if(handle_select(base, &wait) < 0) { + if(base->need_to_exit) + return 0; + return -1; + } + } + return 0; +} + +int event_base_loopexit(struct event_base *base, + struct timeval * ATTR_UNUSED(tv)) +{ + base->need_to_exit = 1; + return 0; +} + +void event_base_free(struct event_base *base) +{ + if(!base) + return; + if(base->items) + free(base->items); + if(base->times) + free(base->times); + if(base->signals) + free(base->signals); + free(base); +} + +void event_set(struct event *ev, int fd, short bits, + void (*cb)(int, short, void *), void *arg) +{ + ev->node.key = ev; + ev->ev_fd = fd; + ev->ev_events = bits; + ev->ev_callback = cb; + fptr_ok(fptr_whitelist_event(ev->ev_callback)); + ev->ev_arg = arg; + ev->added = 0; +} + +int event_base_set(struct event_base *base, struct event *ev) +{ + ev->ev_base = base; + ev->added = 0; + return 0; +} + +int event_add(struct event *ev, struct timeval *tv) +{ + if(ev->added) + event_del(ev); + log_assert(ev->ev_fd==-1 || find_fd(ev->ev_base, ev->ev_fd) == -1); + if(ev->ev_base->max == ev->ev_base->cap) + return -1; + ev->idx = ev->ev_base->max++; + ev->ev_base->items[ev->idx] = ev; + + if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { + BOOL b=0; + int t, l; + long events = 0; + if( (ev->ev_events&EV_READ) ) + events |= FD_READ; + if( (ev->ev_events&EV_WRITE) ) + events |= FD_WRITE; + l = sizeof(t); + if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_TYPE, + (void*)&t, &l) != 0) + log_err("getsockopt(SO_TYPE) failed: %s", + wsa_strerror(WSAGetLastError())); + if(t == SOCK_STREAM) { + /* TCP socket */ + events |= FD_CLOSE; + if( (ev->ev_events&EV_WRITE) ) + events |= FD_CONNECT; + l = sizeof(b); + if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_ACCEPTCONN, + (void*)&b, &l) != 0) + log_err("getsockopt(SO_ACCEPTCONN) failed: %s", + wsa_strerror(WSAGetLastError())); + if(b) /* TCP accept socket */ + events |= FD_ACCEPT; + } + ev->hEvent = WSACreateEvent(); + if(ev->hEvent == WSA_INVALID_EVENT) + log_err("WSACreateEvent failed: %s", + wsa_strerror(WSAGetLastError())); + /* automatically sets fd to nonblocking mode. + * nonblocking cannot be disabled, until wsaES(fd, NULL, 0) */ + if(WSAEventSelect(ev->ev_fd, ev->hEvent, events) != 0) { + log_err("WSAEventSelect failed: %s", + wsa_strerror(WSAGetLastError())); + } + } + + if(tv && (ev->ev_events&EV_TIMEOUT)) { +#ifndef S_SPLINT_S + struct timeval *now = ev->ev_base->time_tv; + ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; + ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; + while(ev->ev_timeout.tv_usec > 1000000) { + ev->ev_timeout.tv_usec -= 1000000; + ev->ev_timeout.tv_sec++; + } +#endif + (void)rbtree_insert(ev->ev_base->times, &ev->node); + } + ev->added = 1; + return 0; +} + +int event_del(struct event *ev) +{ + if(!ev->added) + return 0; + log_assert(ev->added && ev->ev_base->max > 0) + /* remove item and compact the list */ + ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1]; + ev->ev_base->items[ev->ev_base->max-1] = NULL; + ev->ev_base->max--; + if(ev->idx < ev->ev_base->max) + ev->ev_base->items[ev->idx]->idx = ev->idx; + + if((ev->ev_events&EV_TIMEOUT)) + (void)rbtree_delete(ev->ev_base->times, &ev->node); + if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { + if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0) + log_err("WSAEventSelect(disable) failed: %s", + wsa_strerror(WSAGetLastError())); + if(!WSACloseEvent(ev->hEvent)) + log_err("WSACloseEvent failed: %s", + wsa_strerror(WSAGetLastError())); + } + ev->added = 0; + return 0; +} + +/** which base gets to handle signals */ +static struct event_base* signal_base = NULL; +/** signal handler */ +static RETSIGTYPE sigh(int sig) +{ + struct event* ev; + if(!signal_base || sig < 0 || sig >= MAX_SIG) + return; + ev = signal_base->signals[sig]; + if(!ev) + return; + fptr_ok(fptr_whitelist_event(ev->ev_callback)); + (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg); +} + +int signal_add(struct event *ev, struct timeval * ATTR_UNUSED(tv)) +{ + if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) + return -1; + signal_base = ev->ev_base; + ev->ev_base->signals[ev->ev_fd] = ev; + ev->added = 1; + if(signal(ev->ev_fd, sigh) == SIG_ERR) { + return -1; + } + return 0; +} + +int signal_del(struct event *ev) +{ + if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) + return -1; + ev->ev_base->signals[ev->ev_fd] = NULL; + ev->added = 0; + return 0; +} + +#endif /* USE_WINSOCK */ diff --git a/util/winsock_event.h b/util/winsock_event.h new file mode 100644 index 000000000..37ce3f915 --- /dev/null +++ b/util/winsock_event.h @@ -0,0 +1,193 @@ +/* + * util/winsock_event.h - unbound event handling for winsock on windows + * + * Copyright (c) 2008, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains interface functions with the WinSock2 API on Windows. + * It uses the winsock WSAWaitForMultipleEvents interface on a number of + * sockets. + * + * Note that windows can only wait for max 64 events at one time. + * + * Also, file descriptors cannot be waited for. + * + * Named pipes are not easily available (and are not usable in select() ). + * For interprocess communication, it is possible to wait for a hEvent to + * be signaled by another thread. + * + * When a socket becomes readable, then it will not be flagged as + * readable again until you have gotten WOULDBLOCK from a recv routine. + * That means the event handler must store the readability (edge notify) + * and process the incoming data until it blocks. + * The function performing recv then has to inform the event handler that + * the socket has blocked, and the event handler can mark it as such. + * Thus, this file transforms the edge notify from windows to a level notify + * that is compatible with UNIX. + * However, the WSAEventSelect page says that it does do level notify, as long + * as you call a recv/write/accept at least once when it is signalled. + * + * To stay 'fair', instead of emptying a socket completely, the event handler + * can test the other (marked as blocking) sockets for new events. + * + * Additionally, TCP accept sockets get special event support. + * + * Socket numbers are not starting small, they can be any number (say 33060). + * Therefore, bitmaps are not used, but arrays. + */ + +#ifndef UTIL_WINSOCK_EVENT_H +#define UTIL_WINSOCK_EVENT_H + +#ifdef USE_WINSOCK + +#ifndef HAVE_EVENT_BASE_FREE +#define HAVE_EVENT_BASE_FREE +#endif + +/** event timeout */ +#define EV_TIMEOUT 0x01 +/** event fd readable */ +#define EV_READ 0x02 +/** event fd writable */ +#define EV_WRITE 0x04 +/** event signal */ +#define EV_SIGNAL 0x08 +/** event must persist */ +#define EV_PERSIST 0x10 + +/* needs our redblack tree */ +#include "rbtree.h" + +/** max number of signals to support */ +#define MAX_SIG 32 + +/** The number of items that the winsock event handler can service. + * Windows cannot handle more anyway */ +#define WSK_MAX_ITEMS 64 + +/** + * event base for winsock event handler + */ +struct event_base +{ + /** sorted by timeout (absolute), ptr */ + rbtree_t* times; + /** array (first part in use) of handles to work on */ + struct event** items; + /** number of items in use in array */ + int max; + /** capacity of array, size of array in items */ + int cap; + /** array of 0 - maxsig of ptr to event for it */ + struct event** signals; + /** if we need to exit */ + int need_to_exit; + /** where to store time in seconds */ + uint32_t* time_secs; + /** where to store time in microseconds */ + struct timeval* time_tv; +}; + +/** + * Event structure. Has some of the event elements. + */ +struct event { + /** node in timeout rbtree */ + rbnode_t node; + /** is event already added */ + int added; + + /** event base it belongs to */ + struct event_base *ev_base; + /** fd to poll or -1 for timeouts. signal number for sigs. */ + int ev_fd; + /** what events this event is interested in, see EV_.. above. */ + short ev_events; + /** timeout value */ + struct timeval ev_timeout; + + /** callback to call: fd, eventbits, userarg */ + void (*ev_callback)(int, short, void *arg); + /** callback user arg */ + void *ev_arg; + + /* ----- nonpublic part, for winsock_event only ----- */ + /** index of this event in the items array (if added) */ + int idx; + /** the event handle to wait for new events to become ready */ + WSAEVENT hEvent; +}; + +/** create event base */ +void *event_init(uint32_t* time_secs, struct timeval* time_tv); +/** get version */ +const char *event_get_version(void); +/** get polling method (select,epoll) */ +const char *event_get_method(void); +/** run select in a loop */ +int event_base_dispatch(struct event_base *); +/** exit that loop */ +int event_base_loopexit(struct event_base *, struct timeval *); +/** free event base. Free events yourself */ +void event_base_free(struct event_base *); +/** set content of event */ +void event_set(struct event *, int, short, void (*)(int, short, void *), void *); + +/** add event to a base. You *must* call this for every event. */ +int event_base_set(struct event_base *, struct event *); +/** add event to make it active. You may not change it with event_set anymore */ +int event_add(struct event *, struct timeval *); +/** remove event. You may change it again */ +int event_del(struct event *); + +#define evtimer_add(ev, tv) event_add(ev, tv) +#define evtimer_del(ev) event_del(ev) + +/* uses different implementation. Cannot mix fd/timeouts and signals inside + * the same struct event. create several event structs for that. */ +/** install signal handler */ +int signal_add(struct event *, struct timeval *); +/** set signal event contents */ +#define signal_set(ev, x, cb, arg) \ + event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) +/** remove signal handler */ +int signal_del(struct event *); + +/** compare events in tree, based on timevalue, ptr for uniqueness */ +int mini_ev_cmp(const void* a, const void* b); + +#endif /* USE_WINSOCK */ +#endif /* UTIL_WINSOCK_EVENT_H */