]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
winsock_event handler, working resolution and validation on windows.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 17 Jun 2008 14:57:18 +0000 (14:57 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 17 Jun 2008 14:57:18 +0000 (14:57 +0000)
git-svn-id: file:///svn/unbound/trunk@1124 be551aaa-1e26-0410-a405-d3ace91eadb9

config.h.in
configure
configure.ac
daemon/unbound.c
doc/Changelog
util/config_file.c
util/mini_event.c
util/mini_event.h
util/netevent.c
util/winsock_event.c [new file with mode: 0644]
util/winsock_event.h [new file with mode: 0644]

index 788ab1d1da2fcc0e2c64ecf4765040cac0784124..efe31585e0e0d9d539161a92ee7efa2e875227f5 100644 (file)
@@ -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
 
index 37bae2769bc7cceb1799befc519bcc7ded339935..948ec1496e90949c51bdf99d6f4032f5a9ba7d72 100755 (executable)
--- 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
 
 
index 0202bc66e5c0429ded143fe59db2e99ffd5f130f..fcea3bddf1ea2444750eefe66b314cd50de55a53 100644 (file)
@@ -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])
                ])
index 4d0f318829424967d18e25ab8c6cc1432e9667cb..7e71741802a549c9e2a5dd967c208a535c6cbc53 100644 (file)
 #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 <event.h>
+#  include <event.h>
 #endif
 
 /** global debug value to keep track of heap memory allocation */
index aeed63a608657b52655130e649b2c3c67cb57f19..bbab8f2eb55209026b66d860edb5fe6b8c3dd209 100644 (file)
@@ -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.
index a333c96794a9a8f7db965ceaabaf8a3e584a1e9b..5f806c3c56bae30376baf92a7a8d865ba0e17cd0 100644 (file)
@@ -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 */
index 198a1b872399207af1a9f599ace37ad8336f8204..2f84dd4b3fe39ee6841a3b8e6b14068da610c473 100644 (file)
@@ -42,7 +42,7 @@
 
 #include "config.h"
 
-#ifdef USE_MINI_EVENT
+#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
 #include <signal.h>
 #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 */
index 1abb347b641241fa58d35cddf66327609a0cec3a..61cbcfbdb7222b18392c165b9dbc68c95237abef 100644 (file)
@@ -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);
index 9197e4726808ce4de2dc4870ed82a7c313654a7a..13aedee700c333e336bdb39583bc372bc040340c 100644 (file)
 /* We define libevent structures here to hide the libevent stuff. */
 
 #ifdef USE_MINI_EVENT
-#include "util/mini_event.h"
-#else
-/* we use libevent */
-#include <event.h>
-#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 <event.h>
+#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 (file)
index 0000000..07ac224
--- /dev/null
@@ -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 <signal.h>
+#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; i<base->max; 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; i<base->max; 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; i<numwait; i++) {
+               short bits = 0;
+               /* eventlist[i] fired */
+               if(WSAEnumNetworkEvents(eventlist[i]->ev_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 (file)
index 0000000..37ce3f9
--- /dev/null
@@ -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 */