List:Commits« Previous MessageNext Message »
From:damien Date:October 31 2007 7:37pm
Subject:bk commit into 6.0 tree (dkatz:1.2647)
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of dkatz. When dkatz does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-10-31 15:36:58-04:00, dkatz@stripped +42 -0
  WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  config/ac-macros/libevent.m4@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +304 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  config/ac-macros/libevent.m4@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  configure.in@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +2 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/Makefile.am@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +2 -2
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/Makefile.am@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +20 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/Makefile.am@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/README@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +53 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/README@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/buffer.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +460 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/buffer.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/compat/sys/_time.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +163 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/compat/sys/_time.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  extra/libevent/compat/sys/queue.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +488 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/compat/sys/queue.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  extra/libevent/compat/sys/tree.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +677 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/compat/sys/tree.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  extra/libevent/devpoll.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +419 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/devpoll.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/epoll.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +366 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/epoll.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/epoll_sub.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +52 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/epoll_sub.c@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +0 -0

  extra/libevent/evbuffer.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +413 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evbuffer.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/evdns.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +3116 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evdns.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/evdns.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +367 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evdns.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/event-internal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +61 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/event-internal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/event.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +928 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/event.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/event.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +340 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/event.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/event_tagging.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +372 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/event_tagging.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/evhttp.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +221 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evhttp.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/evport.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +531 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evport.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/evsignal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +44 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/evsignal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/http-internal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +122 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/http-internal.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/http.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +2368 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/http.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/kqueue.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +419 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/kqueue.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/log.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +219 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/log.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/log.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +43 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/log.h@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/poll.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +391 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/poll.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/select.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +373 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/select.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/signal.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +195 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/signal.c@stripped, 2007-10-31 15:36:51-04:00, dkatz@stripped +0 -0

  extra/libevent/strlcpy-internal.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +23 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/strlcpy-internal.h@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  extra/libevent/strlcpy.c@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +76 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  extra/libevent/strlcpy.c@stripped, 2007-10-31 15:36:52-04:00, dkatz@stripped +0 -0

  libmysqld/Makefile.am@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +5 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  mysql-test/r/crash_commit_before.result@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +1 -1
    Fix a test case that fails by setting the global debug variable instead of the session debug variable.
    
    "SET SESSION debug" sets a value that is actually stored in a OS thread variable, instead of the session (THD) object. The libevent work breaks this because each request is likely handled by a different OS thread.

  mysql-test/r/sp-threads.result@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +2 -2
    Changed code that populates the process table to uniformally output blank status as NULL, instead of sometimes NULL/sometimes emptry string.

  mysql-test/t/crash_commit_before.test@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +1 -1
    Fix a test case that fails by setting the global debug variable instead of the session debug variable.
    
    "SET SESSION debug" sets a value that is actually stored in a OS thread variable, instead of the session (THD) object. The libevent work breaks this because each request is likely handled by a different OS thread.

  sql/Makefile.am@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +3 -2
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  sql/mysql_priv.h@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +1 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  sql/scheduler.cc@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +514 -3
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  sql/scheduler.h@stripped, 2007-10-31 15:36:49-04:00, dkatz@stripped +26 -0
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  sql/sql_connect.cc@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +1 -1
    WL441: Work to integrate libevent and support the --thread_handling=pool-of-threads runtime option.

  sql/sql_show.cc@stripped, 2007-10-31 15:36:50-04:00, dkatz@stripped +2 -2
    Changed code that populates the process table to uniformally output blank status as NULL, instead of sometimes NULL/sometimes emptry string.

diff -Nrup a/config/ac-macros/libevent.m4 b/config/ac-macros/libevent.m4
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/config/ac-macros/libevent.m4	2007-10-31 15:36:52 -04:00
@@ -0,0 +1,304 @@
+dnl ---------------------------------------------------------------------------
+dnl Macro: MYSQL_USE_BUNDLED_LIBEVENT
+dnl
+dnl SYNOPSIS
+dnl   MYSQL_USE_BUNDLED_LIBEVENT()
+dnl
+dnl DESCRIPTION
+dnl  Add defines so libevent is built and linked with
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([MYSQL_USE_BUNDLED_LIBEVENT], [
+
+  libevent_dir="libevent"
+  AC_SUBST([libevent_dir])
+
+  libevent_libs="\$(top_builddir)/extra/libevent/libevent.la"
+  libevent_includes="-I\$(top_builddir)/extra/libevent"
+  AC_SUBST(libevent_libs)
+  AC_SUBST(libevent_includes)
+
+  AC_DEFINE([HAVE_LIBEVENT], [1], [If we want to use libevent and have connection pooling])
+  AC_MSG_RESULT([using bundled libevent])
+
+  dnl Things that libevent needs
+  AC_CHECK_HEADERS(inttypes.h stdint.h poll.h signal.h sys/epoll.h sys/time.h \
+                   sys/queue.h sys/event.h sys/devpoll.h netinet/in6.h)
+
+if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
+	AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/queue.h>
+#ifdef TAILQ_FOREACH
+ yes
+#endif
+],	[AC_MSG_RESULT(yes)
+	 AC_DEFINE(HAVE_TAILQFOREACH, 1,
+		[Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
+	AC_MSG_RESULT(no)
+	)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timeradd in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timeradd
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERADD, 1,
+		[Define if timeradd is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timercmp in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timercmp
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERCMP, 1,
+		[Define if timercmp is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timerclear in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerclear
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERCLEAR, 1,
+		[Define if timerclear is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timerisset in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerisset
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERISSET, 1,
+		[Define if timerisset is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(vasprintf strsep getaddrinfo getnameinfo inet_ntop)
+
+if test "x$ac_cv_func_clock_gettime" = "xyes"; then
+   AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if clock_gettime is available in libc])
+else
+   AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define is no secure id variant is available])
+fi
+
+AC_MSG_CHECKING(for F_SETFD in fcntl.h)
+AC_EGREP_CPP(yes,
+[
+#define _GNU_SOURCE
+#include <fcntl.h>
+#ifdef F_SETFD
+yes
+#endif
+],	[ AC_DEFINE(HAVE_SETFD, 1,
+	      [Define if F_SETFD is defined in <fcntl.h>])
+	  AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no))
+
+needsignal=no
+if test "x$ac_cv_func_select" = "xyes" ; then
+	 AC_LIBOBJ(select)
+	 needsignal=yes
+fi
+
+if test "x$ac_cv_func_poll" = "xyes" ; then
+	 AC_LIBOBJ(poll)
+	 needsignal=yes
+fi
+
+haveepoll=no
+AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
+if test "x$haveepoll" = "xyes" ; then
+	AC_DEFINE(HAVE_EPOLL, 1,
+		[Define if your system supports the epoll system calls])
+	AC_LIBOBJ(epoll)
+	needsignal=yes
+fi
+
+havedevpoll=no
+if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then
+	AC_DEFINE(HAVE_DEVPOLL, 1,
+		    [Define if /dev/poll is available])
+        AC_LIBOBJ(devpoll)
+fi
+
+havekqueue=no
+if test "x$ac_cv_header_sys_event_h" = "xyes"; then
+	AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
+	if test "x$havekqueue" = "xyes" ; then
+		AC_MSG_CHECKING(for working kqueue)
+		AC_TRY_RUN(
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main(int argc, char **argv)
+{
+	int kq;
+	int n;
+	int fd[[2]];
+	struct kevent ev;
+	struct timespec ts;
+	char buf[[8000]];
+
+	if (pipe(fd) == -1)
+		exit(1);
+	if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
+		exit(1);
+
+	while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
+		;
+
+        if ((kq = kqueue()) == -1)
+		exit(1);
+
+	ev.ident = fd[[1]];
+	ev.filter = EVFILT_WRITE;
+	ev.flags = EV_ADD | EV_ENABLE;
+	n = kevent(kq, &ev, 1, NULL, 0, NULL);
+	if (n == -1)
+		exit(1);
+	
+	read(fd[[0]], buf, sizeof(buf));
+
+	ts.tv_sec = 0;
+	ts.tv_nsec = 0;
+	n = kevent(kq, NULL, 0, &ev, 1, &ts);
+	if (n == -1 || n == 0)
+		exit(1);
+
+	exit(0);
+}, [AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_WORKING_KQUEUE, 1,
+		[Define if kqueue works correctly with pipes])
+    AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+	fi
+fi
+
+haveepollsyscall=no
+if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
+	if test "x$haveepoll" = "xno" ; then
+		AC_MSG_CHECKING(for epoll system call)
+		AC_TRY_RUN(
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+int
+epoll_create(int size)
+{
+	return (syscall(__NR_epoll_create, size));
+}
+
+int
+main(int argc, char **argv)
+{
+	int epfd;
+
+	epfd = epoll_create(256);
+	exit (epfd == -1 ? 1 : 0);
+}, [AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_EPOLL, 1,
+	[Define if your system supports the epoll system calls])
+    needsignal=yes
+    AC_LIBOBJ(epoll_sub)
+    AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+	fi
+fi
+
+haveeventports=no
+AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
+if test "x$haveeventports" = "xyes" ; then
+	AC_DEFINE(HAVE_EVENT_PORTS, 1,
+		[Define if your system supports event ports])
+	AC_LIBOBJ(evport)
+	needsignal=yes
+fi
+if test "x$needsignal" = "xyes" ; then
+	AC_LIBOBJ(signal)
+fi
+
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_CHECK_TYPE(uint64_t, unsigned long long)
+AC_CHECK_TYPE(uint32_t, unsigned int)
+AC_CHECK_TYPE(uint16_t, unsigned short)
+AC_CHECK_TYPE(uint8_t, unsigned char)
+AC_CHECK_TYPES([struct in6_addr], , ,
+[#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif])
+
+AC_MSG_CHECKING([for socklen_t])
+AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>],
+  [socklen_t x;],
+  AC_MSG_RESULT([yes]),
+  [AC_MSG_RESULT([no])
+  AC_DEFINE(socklen_t, unsigned int,
+	[Define to unsigned int if you dont have it])]
+)
+
+])
+
+
+dnl ------------------------------------------------------------------------
+dnl Macro: MYSQL_CHECK_LIBEVENT
+dnl
+dnl SYNOPSIS
+dnl   MYSQL_CHECK_LIBEVENT
+dnl
+dnl ------------------------------------------------------------------------
+AC_DEFUN([MYSQL_CHECK_LIBEVENT], [
+
+  AC_CONFIG_FILES(extra/libevent/Makefile)
+
+  AC_MSG_CHECKING(for libevent)
+  AC_ARG_WITH([libevent],
+      [  --with-libevent         use libevent and have connection pooling],
+      [with_libevent=$withval],
+      [with_libevent=no]
+  )
+
+  if test "$with_libevent" != "no"
+  then
+    MYSQL_USE_BUNDLED_LIBEVENT
+  fi
+  AM_CONDITIONAL([HAVE_LIBEVENT], [ test "$with_libevent" != "no" ])
+])
diff -Nrup a/configure.in b/configure.in
--- a/configure.in	2007-10-19 16:19:57 -04:00
+++ b/configure.in	2007-10-31 15:36:49 -04:00
@@ -49,6 +49,7 @@ sinclude(config/ac-macros/large_file.m4)
 sinclude(config/ac-macros/misc.m4)
 sinclude(config/ac-macros/readline.m4)
 sinclude(config/ac-macros/ssl.m4)
+sinclude(config/ac-macros/libevent.m4)
 sinclude(config/ac-macros/zlib.m4)
 
 # Remember to add a directory sql/share/LANGUAGE
@@ -2275,6 +2276,7 @@ MYSQL_CHECK_BIG_TABLES
 MYSQL_CHECK_MAX_INDEXES
 MYSQL_CHECK_VIO
 MYSQL_CHECK_SSL
+MYSQL_CHECK_LIBEVENT
 
 #--------------------------------------------------------------------
 # Declare our plugin modules
diff -Nrup a/extra/Makefile.am b/extra/Makefile.am
--- a/extra/Makefile.am	2007-08-30 19:23:48 -04:00
+++ b/extra/Makefile.am	2007-10-31 15:36:49 -04:00
@@ -23,8 +23,8 @@ BUILT_SOURCES=		$(top_builddir)/include/
                         $(top_builddir)/include/mysqld_ername.h
 pkginclude_HEADERS=	$(BUILT_SOURCES)
 DISTCLEANFILES =	$(BUILT_SOURCES)
-SUBDIRS =		@yassl_dir@
-DIST_SUBDIRS =		yassl
+SUBDIRS =		@yassl_dir@ @libevent_dir@
+DIST_SUBDIRS =		yassl libevent
 
 # This will build mysqld_error.h, mysqld_ername.h and sql_state.h
 # NOTE Built files should depend on their sources to avoid
diff -Nrup a/extra/libevent/Makefile.am b/extra/libevent/Makefile.am
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/Makefile.am	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,20 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+EXTRA_DIST = select.c poll.c epoll.c epoll_sub.c devpoll.c kqueue.c \
+	evport.c evsignal.h signal.c event-internal.h README compat/sys
+
+lib_LTLIBRARIES = libevent.la
+
+SYS_LIBS =
+SYS_SRC =
+SYS_INCLUDES =
+
+libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
+	http.c evhttp.h http-internal.h evdns.c evdns.h strlcpy.c \
+	strlcpy-internal.h strlcpy-internal.h
+libevent_la_LIBADD = @LTLIBOBJS@
+libevent_la_LDFLAGS = -release 1.3e -version-info 1:3:0
+
+include_HEADERS = event.h evhttp.h evdns.h log.h
+
+INCLUDES = -Icompat -I$(top_srcdir)/include
diff -Nrup a/extra/libevent/README b/extra/libevent/README
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/README	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,53 @@
+To build libevent, type
+
+$ ./configure && make
+
+Install as root via
+
+# make install
+
+You can run the regression tests by
+
+$ make verify
+
+Before, reporting any problems, please run the regression tests.
+
+To enable the low-level tracing build the library as:
+
+CFLAGS=-DUSE_DEBUG ./configure [...]
+ 
+Acknowledgements:
+-----------------
+
+The following people have helped with suggestions, ideas, code or
+fixing bugs:
+
+  Alejo
+  Weston Andros Adamson
+  William Ahern
+  Stas Bekman
+  Andrew Danforth
+  Mike Davis
+  Shie Erlich
+  Alexander von Gernler
+  Artur Grabowski
+  Aaron Hopkins
+  Claudio Jeker
+  Scott Lamb
+  Adam Langley
+  Philip Lewis
+  David Libenzi
+  Nick Mathewson
+  Andrey Matveev
+  Richard Nyberg
+  Jon Oberheide
+  Phil Oleson
+  Dave Pacheco
+  Tassilo von Parseval
+  Pierre Phaneuf
+  Jon Poland
+  Bert JW Regeer
+  Dug Song
+  Taral
+
+If I have forgotten your name, please contact me.
diff -Nrup a/extra/libevent/buffer.c b/extra/libevent/buffer.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/buffer.c	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2002, 2003 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_VASPRINTF
+/* If we have vasprintf, we need to define this before we include stdio.h. */
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "event.h"
+
+struct evbuffer *
+evbuffer_new(void)
+{
+	struct evbuffer *buffer;
+	
+	buffer = calloc(1, sizeof(struct evbuffer));
+
+	return (buffer);
+}
+
+void
+evbuffer_free(struct evbuffer *buffer)
+{
+	if (buffer->orig_buffer != NULL)
+		free(buffer->orig_buffer);
+	free(buffer);
+}
+
+/* 
+ * This is a destructive add.  The data from one buffer moves into
+ * the other buffer.
+ */
+
+#define SWAP(x,y) do { \
+	(x)->buffer = (y)->buffer; \
+	(x)->orig_buffer = (y)->orig_buffer; \
+	(x)->misalign = (y)->misalign; \
+	(x)->totallen = (y)->totallen; \
+	(x)->off = (y)->off; \
+} while (0)
+
+int
+evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
+{
+	int res;
+
+	/* Short cut for better performance */
+	if (outbuf->off == 0) {
+		struct evbuffer tmp;
+		size_t oldoff = inbuf->off;
+
+		/* Swap them directly */
+		SWAP(&tmp, outbuf);
+		SWAP(outbuf, inbuf);
+		SWAP(inbuf, &tmp);
+
+		/* 
+		 * Optimization comes with a price; we need to notify the
+		 * buffer if necessary of the changes. oldoff is the amount
+		 * of data that we transfered from inbuf to outbuf
+		 */
+		if (inbuf->off != oldoff && inbuf->cb != NULL)
+			(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
+		if (oldoff && outbuf->cb != NULL)
+			(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
+		
+		return (0);
+	}
+
+	res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
+	if (res == 0) {
+		/* We drain the input buffer on success */
+		evbuffer_drain(inbuf, inbuf->off);
+	}
+
+	return (res);
+}
+
+int
+evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
+{
+	char *buffer;
+	size_t space;
+	size_t oldoff = buf->off;
+	int sz;
+	va_list aq;
+
+	/* make sure that at least some space is available */
+	evbuffer_expand(buf, 64);
+	for (;;) {
+		size_t used = buf->misalign + buf->off;
+		buffer = (char *)buf->buffer + buf->off;
+		assert(buf->totallen >= used);
+		space = buf->totallen - used;
+
+#ifndef va_copy
+#define	va_copy(dst, src)	memcpy(&(dst), &(src), sizeof(va_list))
+#endif
+		va_copy(aq, ap);
+
+#ifdef WIN32
+		sz = vsnprintf(buffer, space - 1, fmt, aq);
+		buffer[space - 1] = '\0';
+#else
+		sz = vsnprintf(buffer, space, fmt, aq);
+#endif
+
+		va_end(aq);
+
+		if (sz < 0)
+			return (-1);
+		if (sz < space) {
+			buf->off += sz;
+			if (buf->cb != NULL)
+				(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+			return (sz);
+		}
+		if (evbuffer_expand(buf, sz + 1) == -1)
+			return (-1);
+
+	}
+	/* NOTREACHED */
+}
+
+int
+evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
+{
+	int res = -1;
+	va_list ap;
+
+	va_start(ap, fmt);
+	res = evbuffer_add_vprintf(buf, fmt, ap);
+	va_end(ap);
+
+	return (res);
+}
+
+/* Reads data from an event buffer and drains the bytes read */
+
+int
+evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
+{
+	size_t nread = datlen;
+	if (nread >= buf->off)
+		nread = buf->off;
+
+	memcpy(data, buf->buffer, nread);
+	evbuffer_drain(buf, nread);
+	
+	return (nread);
+}
+
+/*
+ * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
+ * The returned buffer needs to be freed by the called.
+ */
+
+char *
+evbuffer_readline(struct evbuffer *buffer)
+{
+	u_char *data = EVBUFFER_DATA(buffer);
+	size_t len = EVBUFFER_LENGTH(buffer);
+	char *line;
+	unsigned int i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] == '\r' || data[i] == '\n')
+			break;
+	}
+
+	if (i == len)
+		return (NULL);
+
+	if ((line = malloc(i + 1)) == NULL) {
+		fprintf(stderr, "%s: out of memory\n", __func__);
+		evbuffer_drain(buffer, i);
+		return (NULL);
+	}
+
+	memcpy(line, data, i);
+	line[i] = '\0';
+
+	/*
+	 * Some protocols terminate a line with '\r\n', so check for
+	 * that, too.
+	 */
+	if ( i < len - 1 ) {
+		char fch = data[i], sch = data[i+1];
+
+		/* Drain one more character if needed */
+		if ( (sch == '\r' || sch == '\n') && sch != fch )
+			i += 1;
+	}
+
+	evbuffer_drain(buffer, i + 1);
+
+	return (line);
+}
+
+/* Adds data to an event buffer */
+
+static void
+evbuffer_align(struct evbuffer *buf)
+{
+	memmove(buf->orig_buffer, buf->buffer, buf->off);
+	buf->buffer = buf->orig_buffer;
+	buf->misalign = 0;
+}
+
+/* Expands the available space in the event buffer to at least datlen */
+
+int
+evbuffer_expand(struct evbuffer *buf, size_t datlen)
+{
+	size_t need = buf->misalign + buf->off + datlen;
+
+	/* If we can fit all the data, then we don't have to do anything */
+	if (buf->totallen >= need)
+		return (0);
+
+	/*
+	 * If the misalignment fulfills our data needs, we just force an
+	 * alignment to happen.  Afterwards, we have enough space.
+	 */
+	if (buf->misalign >= datlen) {
+		evbuffer_align(buf);
+	} else {
+		void *newbuf;
+		size_t length = buf->totallen;
+
+		if (length < 256)
+			length = 256;
+		while (length < need)
+			length <<= 1;
+
+		if (buf->orig_buffer != buf->buffer)
+			evbuffer_align(buf);
+		if ((newbuf = realloc(buf->buffer, length)) == NULL)
+			return (-1);
+
+		buf->orig_buffer = buf->buffer = newbuf;
+		buf->totallen = length;
+	}
+
+	return (0);
+}
+
+int
+evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
+{
+	size_t need = buf->misalign + buf->off + datlen;
+	size_t oldoff = buf->off;
+
+	if (buf->totallen < need) {
+		if (evbuffer_expand(buf, datlen) == -1)
+			return (-1);
+	}
+
+	memcpy(buf->buffer + buf->off, data, datlen);
+	buf->off += datlen;
+
+	if (datlen && buf->cb != NULL)
+		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+	return (0);
+}
+
+void
+evbuffer_drain(struct evbuffer *buf, size_t len)
+{
+	size_t oldoff = buf->off;
+
+	if (len >= buf->off) {
+		buf->off = 0;
+		buf->buffer = buf->orig_buffer;
+		buf->misalign = 0;
+		goto done;
+	}
+
+	buf->buffer += len;
+	buf->misalign += len;
+
+	buf->off -= len;
+
+ done:
+	/* Tell someone about changes in this buffer */
+	if (buf->off != oldoff && buf->cb != NULL)
+		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+}
+
+/*
+ * Reads data from a file descriptor into a buffer.
+ */
+
+#define EVBUFFER_MAX_READ	4096
+
+int
+evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
+{
+	u_char *p;
+	size_t oldoff = buf->off;
+	int n = EVBUFFER_MAX_READ;
+#ifdef WIN32
+	DWORD dwBytesRead;
+#endif
+
+#ifdef FIONREAD
+	if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
+		n = EVBUFFER_MAX_READ;
+	} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
+		/*
+		 * It's possible that a lot of data is available for
+		 * reading.  We do not want to exhaust resources
+		 * before the reader has a chance to do something
+		 * about it.  If the reader does not tell us how much
+		 * data we should read, we artifically limit it.
+		 */
+		if (n > buf->totallen << 2)
+			n = buf->totallen << 2;
+		if (n < EVBUFFER_MAX_READ)
+			n = EVBUFFER_MAX_READ;
+	}
+#endif	
+	if (howmuch < 0 || howmuch > n)
+		howmuch = n;
+
+	/* If we don't have FIONREAD, we might waste some space here */
+	if (evbuffer_expand(buf, howmuch) == -1)
+		return (-1);
+
+	/* We can append new data at this point */
+	p = buf->buffer + buf->off;
+
+#ifndef WIN32
+	n = read(fd, p, howmuch);
+	if (n == -1)
+		return (-1);
+	if (n == 0)
+		return (0);
+#else
+	n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL);
+	if (n == 0)
+		return (-1);
+	if (dwBytesRead == 0)
+		return (0);
+	n = dwBytesRead;
+#endif
+
+	buf->off += n;
+
+	/* Tell someone about changes in this buffer */
+	if (buf->off != oldoff && buf->cb != NULL)
+		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+
+	return (n);
+}
+
+int
+evbuffer_write(struct evbuffer *buffer, int fd)
+{
+	int n;
+#ifdef WIN32
+	DWORD dwBytesWritten;
+#endif
+
+#ifndef WIN32
+	n = write(fd, buffer->buffer, buffer->off);
+	if (n == -1)
+		return (-1);
+	if (n == 0)
+		return (0);
+#else
+	n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL);
+	if (n == 0)
+		return (-1);
+	if (dwBytesWritten == 0)
+		return (0);
+	n = dwBytesWritten;
+#endif
+	evbuffer_drain(buffer, n);
+
+	return (n);
+}
+
+u_char *
+evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
+{
+	u_char *search = buffer->buffer, *end = search + buffer->off;
+	u_char *p;
+
+	while (search < end &&
+	    (p = memchr(search, *what, end - search)) != NULL) {
+		if (p + len > end)
+			break;
+		if (memcmp(p, what, len) == 0)
+			return (p);
+		search = p + 1;
+	}
+
+	return (NULL);
+}
+
+void evbuffer_setcb(struct evbuffer *buffer,
+    void (*cb)(struct evbuffer *, size_t, size_t, void *),
+    void *cbarg)
+{
+	buffer->cb = cb;
+	buffer->cbarg = cbarg;
+}
diff -Nrup a/extra/libevent/compat/sys/_time.h b/extra/libevent/compat/sys/_time.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/compat/sys/_time.h	2007-10-31 15:36:52 -04:00
@@ -0,0 +1,163 @@
+/*	$OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $	*/
+/*	$NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University 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 REGENTS 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.
+ *
+ *	@(#)time.h	8.2 (Berkeley) 7/10/94
+ */
+
+#ifndef _SYS_TIME_H_
+#define _SYS_TIME_H_
+
+#include <sys/types.h>
+
+/*
+ * Structure returned by gettimeofday(2) system call,
+ * and used in other calls.
+ */
+struct timeval {
+	long	tv_sec;		/* seconds */
+	long	tv_usec;	/* and microseconds */
+};
+
+/*
+ * Structure defined by POSIX.1b to be like a timeval.
+ */
+struct timespec {
+	time_t	tv_sec;		/* seconds */
+	long	tv_nsec;	/* and nanoseconds */
+};
+
+#define	TIMEVAL_TO_TIMESPEC(tv, ts) {					\
+	(ts)->tv_sec = (tv)->tv_sec;					\
+	(ts)->tv_nsec = (tv)->tv_usec * 1000;				\
+}
+#define	TIMESPEC_TO_TIMEVAL(tv, ts) {					\
+	(tv)->tv_sec = (ts)->tv_sec;					\
+	(tv)->tv_usec = (ts)->tv_nsec / 1000;				\
+}
+
+struct timezone {
+	int	tz_minuteswest;	/* minutes west of Greenwich */
+	int	tz_dsttime;	/* type of dst correction */
+};
+#define	DST_NONE	0	/* not on dst */
+#define	DST_USA		1	/* USA style dst */
+#define	DST_AUST	2	/* Australian style dst */
+#define	DST_WET		3	/* Western European dst */
+#define	DST_MET		4	/* Middle European dst */
+#define	DST_EET		5	/* Eastern European dst */
+#define	DST_CAN		6	/* Canada */
+
+/* Operations on timevals. */
+#define	timerclear(tvp)		(tvp)->tv_sec = (tvp)->tv_usec = 0
+#define	timerisset(tvp)		((tvp)->tv_sec || (tvp)->tv_usec)
+#define	timercmp(tvp, uvp, cmp)						\
+	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
+	    ((tvp)->tv_usec cmp (uvp)->tv_usec) :			\
+	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#define	timeradd(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec >= 1000000) {			\
+			(vvp)->tv_sec++;				\
+			(vvp)->tv_usec -= 1000000;			\
+		}							\
+	} while (0)
+#define	timersub(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec < 0) {				\
+			(vvp)->tv_sec--;				\
+			(vvp)->tv_usec += 1000000;			\
+		}							\
+	} while (0)
+
+/* Operations on timespecs. */
+#define	timespecclear(tsp)		(tsp)->tv_sec = (tsp)->tv_nsec = 0
+#define	timespecisset(tsp)		((tsp)->tv_sec || (tsp)->tv_nsec)
+#define	timespeccmp(tsp, usp, cmp)					\
+	(((tsp)->tv_sec == (usp)->tv_sec) ?				\
+	    ((tsp)->tv_nsec cmp (usp)->tv_nsec) :			\
+	    ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define	timespecadd(tsp, usp, vsp)					\
+	do {								\
+		(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;		\
+		(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;	\
+		if ((vsp)->tv_nsec >= 1000000000L) {			\
+			(vsp)->tv_sec++;				\
+			(vsp)->tv_nsec -= 1000000000L;			\
+		}							\
+	} while (0)
+#define	timespecsub(tsp, usp, vsp)					\
+	do {								\
+		(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;		\
+		(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;	\
+		if ((vsp)->tv_nsec < 0) {				\
+			(vsp)->tv_sec--;				\
+			(vsp)->tv_nsec += 1000000000L;			\
+		}							\
+	} while (0)
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define	ITIMER_REAL	0
+#define	ITIMER_VIRTUAL	1
+#define	ITIMER_PROF	2
+
+struct	itimerval {
+	struct	timeval it_interval;	/* timer interval */
+	struct	timeval it_value;	/* current value */
+};
+
+/*
+ * Getkerninfo clock information structure
+ */
+struct clockinfo {
+	int	hz;		/* clock frequency */
+	int	tick;		/* micro-seconds per hz tick */
+	int	tickadj;	/* clock skew rate for adjtime() */
+	int	stathz;		/* statistics clock frequency */
+	int	profhz;		/* profiling clock frequency */
+};
+
+#define CLOCK_REALTIME	0
+#define CLOCK_VIRTUAL	1
+#define CLOCK_PROF	2
+
+#define TIMER_RELTIME	0x0	/* relative timer */
+#define TIMER_ABSTIME	0x1	/* absolute timer */
+
+/* --- stuff got cut here - niels --- */
+
+#endif /* !_SYS_TIME_H_ */
diff -Nrup a/extra/libevent/compat/sys/queue.h b/extra/libevent/compat/sys/queue.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/compat/sys/queue.h	2007-10-31 15:36:52 -04:00
@@ -0,0 +1,488 @@
+/*	$OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University 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 REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists, 
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+ 
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#ifndef WIN32
+#define SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+#endif
+
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_END(head)		NULL
+#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = SLIST_FIRST(head);					\
+	    (var) != SLIST_END(head);					\
+	    (var) = SLIST_NEXT(var, field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) {						\
+	SLIST_FIRST(head) = SLIST_END(head);				\
+}
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List access methods
+ */
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_END(head)			NULL
+#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head))
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)					\
+	for((var) = LIST_FIRST(head);					\
+	    (var)!= LIST_END(head);					\
+	    (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST(head) = LIST_END(head);				\
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev =			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {				\
+	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\
+		(elm2)->field.le_next->field.le_prev =			\
+		    &(elm2)->field.le_next;				\
+	(elm2)->field.le_prev = (elm)->field.le_prev;			\
+	*(elm2)->field.le_prev = (elm2);				\
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
+#define	SIMPLEQ_END(head)	    NULL
+#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)				\
+	for((var) = SIMPLEQ_FIRST(head);				\
+	    (var) != SIMPLEQ_END(head);					\
+	    (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do {			\
+	if (((head)->sqh_first = (elm)->field.sqe_next) == NULL)	\
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/* 
+ * tail queue access methods 
+ */
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define	TAILQ_EMPTY(head)						\
+	(TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname)		\
+	for((var) = TAILQ_LAST(head, headname);				\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue access methods 
+ */
+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define	CIRCLEQ_END(head)		((void *)(head))
+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+#define	CIRCLEQ_EMPTY(head)						\
+	(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field)				\
+	for((var) = CIRCLEQ_FIRST(head);				\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for((var) = CIRCLEQ_LAST(head);					\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = CIRCLEQ_END(head);				\
+	(head)->cqh_last = CIRCLEQ_END(head);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == CIRCLEQ_END(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = CIRCLEQ_END(head);			\
+	if ((head)->cqh_last == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = CIRCLEQ_END(head);			\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_last = (elm2);				\
+	else								\
+		(elm2)->field.cqe_next->field.cqe_prev = (elm2);	\
+	if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_first = (elm2);				\
+	else								\
+		(elm2)->field.cqe_prev->field.cqe_next = (elm2);	\
+} while (0)
+
+#endif	/* !_SYS_QUEUE_H_ */
diff -Nrup a/extra/libevent/compat/sys/tree.h b/extra/libevent/compat/sys/tree.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/compat/sys/tree.h	2007-10-31 15:36:52 -04:00
@@ -0,0 +1,677 @@
+/*	$OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $	*/
+/*
+ * Copyright 2002 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef	_SYS_TREE_H_
+#define	_SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *	- every search path from the root to a leaf consists of the
+ *	  same number of black nodes,
+ *	- each red node (except for the root) has a black parent,
+ *	- each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define SPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define SPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (0)
+
+#define SPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define SPLAY_ROOT(head)		(head)->sph_root
+#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+	
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {				\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
+	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_SPLAY(struct name *, struct type *);			\
+void name##_SPLAY_MINMAX(struct name *, int);				\
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static __inline struct type *						\
+name##_SPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (SPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_SPLAY(head, elm);					\
+	if (SPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = SPLAY_RIGHT(elm, field);				\
+		while (SPLAY_LEFT(elm, field) != NULL) {		\
+			elm = SPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_SPLAY_MINMAX(head, val);					\
+        return (SPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (SPLAY_EMPTY(head)) {						\
+	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_SPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (SPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+			name##_SPLAY(head, elm);			\
+			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_SPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}									\
+									\
+/* Splay with either the minimum or the maximum element			\
+ * Used to find minimum or maximum element in tree.			\
+ */									\
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define SPLAY_NEGINF	-1
+#define SPLAY_INF	1
+
+#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)					\
+	for ((x) = SPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-back tree */
+#define RB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define RB_INITIALIZER(root)						\
+	{ NULL }
+
+#define RB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (0)
+
+#define RB_BLACK	0
+#define RB_RED		1
+#define RB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+
+#define RB_LEFT(elm, field)		(elm)->field.rbe_left
+#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define RB_COLOR(elm, field)		(elm)->field.rbe_color
+#define RB_ROOT(head)			(head)->rbh_root
+#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {					\
+	RB_PARENT(elm, field) = parent;					\
+	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
+	RB_COLOR(elm, field) = RB_RED;					\
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do {				\
+	RB_COLOR(black, field) = RB_BLACK;				\
+	RB_COLOR(red, field) = RB_RED;					\
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = RB_RIGHT(elm, field);					\
+	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
+		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_LEFT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = RB_LEFT(elm, field);					\
+	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
+		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_RIGHT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp)				\
+void name##_RB_INSERT_COLOR(struct name *, struct type *);	\
+void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+struct type *name##_RB_REMOVE(struct name *, struct type *);		\
+struct type *name##_RB_INSERT(struct name *, struct type *);		\
+struct type *name##_RB_FIND(struct name *, struct type *);		\
+struct type *name##_RB_NEXT(struct type *);				\
+struct type *name##_RB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp)				\
+void									\
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = RB_PARENT(elm, field)) &&			\
+	    RB_COLOR(parent, field) == RB_RED) {			\
+		gparent = RB_PARENT(parent, field);			\
+		if (parent == RB_LEFT(gparent, field)) {		\
+			tmp = RB_RIGHT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_RIGHT(parent, field) == elm) {		\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = RB_LEFT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_LEFT(parent, field) == elm) {		\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
+}									\
+									\
+void									\
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
+	    elm != RB_ROOT(head)) {					\
+		if (RB_LEFT(parent, field) == elm) {			\
+			tmp = RB_RIGHT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = RB_RIGHT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_RIGHT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = RB_LEFT(tmp, field)))\
+						RB_COLOR(oleft, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = RB_RIGHT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_RIGHT(tmp, field))		\
+					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = RB_LEFT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = RB_LEFT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_LEFT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = RB_RIGHT(tmp, field)))\
+						RB_COLOR(oright, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = RB_LEFT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_LEFT(tmp, field))		\
+					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		RB_COLOR(elm, field) = RB_BLACK;			\
+}									\
+									\
+struct type *								\
+name##_RB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (RB_LEFT(elm, field) == NULL)				\
+		child = RB_RIGHT(elm, field);				\
+	else if (RB_RIGHT(elm, field) == NULL)				\
+		child = RB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = RB_RIGHT(elm, field);				\
+		while ((left = RB_LEFT(elm, field)))			\
+			elm = left;					\
+		child = RB_RIGHT(elm, field);				\
+		parent = RB_PARENT(elm, field);				\
+		color = RB_COLOR(elm, field);				\
+		if (child)						\
+			RB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (RB_LEFT(parent, field) == elm)		\
+				RB_LEFT(parent, field) = child;		\
+			else						\
+				RB_RIGHT(parent, field) = child;	\
+			RB_AUGMENT(parent);				\
+		} else							\
+			RB_ROOT(head) = child;				\
+		if (RB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (RB_PARENT(old, field)) {				\
+			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+				RB_LEFT(RB_PARENT(old, field), field) = elm;\
+			else						\
+				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+			RB_AUGMENT(RB_PARENT(old, field));		\
+		} else							\
+			RB_ROOT(head) = elm;				\
+		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
+		if (RB_RIGHT(old, field))				\
+			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				RB_AUGMENT(left);			\
+			} while ((left = RB_PARENT(left, field)));	\
+		}							\
+		goto color;						\
+	}								\
+	parent = RB_PARENT(elm, field);					\
+	color = RB_COLOR(elm, field);					\
+	if (child)							\
+		RB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (RB_LEFT(parent, field) == elm)			\
+			RB_LEFT(parent, field) = child;			\
+		else							\
+			RB_RIGHT(parent, field) = child;		\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = child;					\
+color:									\
+	if (color == RB_BLACK)						\
+		name##_RB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+struct type *								\
+name##_RB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = RB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	RB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			RB_LEFT(parent, field) = elm;			\
+		else							\
+			RB_RIGHT(parent, field) = elm;			\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = elm;					\
+	name##_RB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+struct type *								\
+name##_RB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_RB_NEXT(struct type *elm)					\
+{									\
+	if (RB_RIGHT(elm, field)) {					\
+		elm = RB_RIGHT(elm, field);				\
+		while (RB_LEFT(elm, field))				\
+			elm = RB_LEFT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+struct type *								\
+name##_RB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else							\
+			tmp = RB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define RB_NEGINF	-1
+#define RB_INF	1
+
+#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
+#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
+#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)					\
+	for ((x) = RB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_NEXT(x))
+
+#endif	/* _SYS_TREE_H_ */
diff -Nrup a/extra/libevent/devpoll.c b/extra/libevent/devpoll.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/devpoll.c	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/devpoll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+/* due to limitations in the devpoll interface, we need to keep track of
+ * all file descriptors outself.
+ */
+struct evdevpoll {
+	struct event *evread;
+	struct event *evwrite;
+};
+
+struct devpollop {
+	struct evdevpoll *fds;
+	int nfds;
+	struct pollfd *events;
+	int nevents;
+	int dpfd;
+	struct pollfd *changes;
+	int nchanges;
+};
+
+void *devpoll_init	(struct event_base *);
+int devpoll_add	(void *, struct event *);
+int devpoll_del	(void *, struct event *);
+int devpoll_recalc	(struct event_base *, void *, int);
+int devpoll_dispatch	(struct event_base *, void *, struct timeval *);
+void devpoll_dealloc	(struct event_base *, void *);
+
+struct eventop devpollops = {
+	"devpoll",
+	devpoll_init,
+	devpoll_add,
+	devpoll_del,
+	devpoll_recalc,
+	devpoll_dispatch,
+	devpoll_dealloc
+};
+
+#define NEVENT	32000
+
+static int
+devpoll_commit(struct devpollop *devpollop)
+{
+	/*
+	 * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
+	 * Write is limited to 2GB of data, until it will fail.
+	 */
+	if (pwrite(devpollop->dpfd, devpollop->changes,
+		sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
+		return(-1);
+
+	devpollop->nchanges = 0;
+	return(0);
+}
+
+static int
+devpoll_queue(struct devpollop *devpollop, int fd, int events) {
+	struct pollfd *pfd;
+
+	if (devpollop->nchanges >= devpollop->nevents) {
+		/*
+		 * Change buffer is full, must commit it to /dev/poll before 
+		 * adding more 
+		 */
+		if (devpoll_commit(devpollop) != 0)
+			return(-1);
+	}
+
+	pfd = &devpollop->changes[devpollop->nchanges++];
+	pfd->fd = fd;
+	pfd->events = events;
+	pfd->revents = 0;
+
+	return(0);
+}
+
+void *
+devpoll_init(struct event_base *base)
+{
+	int dpfd, nfiles = NEVENT;
+	struct rlimit rl;
+	struct devpollop *devpollop;
+
+	/* Disable devpoll when this environment variable is set */
+	if (getenv("EVENT_NODEVPOLL"))
+		return (NULL);
+
+	if (!(devpollop = calloc(1, sizeof(struct devpollop))))
+		return (NULL);
+
+	if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
+	    rl.rlim_cur != RLIM_INFINITY)
+		nfiles = rl.rlim_cur - 1;
+
+	/* Initialize the kernel queue */
+	if ((dpfd = open("/dev/poll", O_RDWR)) == -1) {
+                event_warn("open: /dev/poll");
+		free(devpollop);
+		return (NULL);
+	}
+
+	devpollop->dpfd = dpfd;
+
+	/* Initialize fields */
+	devpollop->events = calloc(nfiles, sizeof(struct pollfd));
+	if (devpollop->events == NULL) {
+		free(devpollop);
+		close(dpfd);
+		return (NULL);
+	}
+	devpollop->nevents = nfiles;
+
+	devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll));
+	if (devpollop->fds == NULL) {
+		free(devpollop->events);
+		free(devpollop);
+		close(dpfd);
+		return (NULL);
+	}
+	devpollop->nfds = nfiles;
+
+	devpollop->changes = calloc(nfiles, sizeof(struct pollfd));
+	if (devpollop->changes == NULL) {
+		free(devpollop->fds);
+		free(devpollop->events);
+		free(devpollop);
+		close(dpfd);
+		return (NULL);
+	}
+
+	evsignal_init(base);
+
+	return (devpollop);
+}
+
+int
+devpoll_recalc(struct event_base *base, void *arg, int max)
+{
+	struct devpollop *devpollop = arg;
+
+	if (max > devpollop->nfds) {
+		struct evdevpoll *fds;
+		int nfds;
+
+		nfds = devpollop->nfds;
+		while (nfds < max)
+			nfds <<= 1;
+
+		fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll));
+		if (fds == NULL) {
+			event_warn("realloc");
+			return (-1);
+		}
+		devpollop->fds = fds;
+		memset(fds + devpollop->nfds, 0,
+		    (nfds - devpollop->nfds) * sizeof(struct evdevpoll));
+		devpollop->nfds = nfds;
+	}
+
+	return (0);
+}
+
+int
+devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+	struct devpollop *devpollop = arg;
+	struct pollfd *events = devpollop->events;
+	struct dvpoll dvp;
+	struct evdevpoll *evdp;
+	int i, res, timeout = -1;
+
+	if (devpollop->nchanges)
+		devpoll_commit(devpollop);
+
+	if (tv != NULL)
+		timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+
+	dvp.dp_fds = devpollop->events;
+	dvp.dp_nfds = devpollop->nevents;
+	dvp.dp_timeout = timeout;
+
+	res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
+
+	if (res == -1) {
+		if (errno != EINTR) {
+			event_warn("ioctl: DP_POLL");
+			return (-1);
+		}
+
+		evsignal_process(base);
+		return (0);
+	} else if (base->sig.evsignal_caught) {
+		evsignal_process(base);
+	}
+
+	event_debug(("%s: devpoll_wait reports %d", __func__, res));
+
+	for (i = 0; i < res; i++) {
+		int which = 0;
+		int what = events[i].revents;
+		struct event *evread = NULL, *evwrite = NULL;
+
+		assert(events[i].fd < devpollop->nfds);
+		evdp = &devpollop->fds[events[i].fd];
+   
+                if (what & POLLHUP)
+                        what |= POLLIN | POLLOUT;
+                else if (what & POLLERR)
+                        what |= POLLIN | POLLOUT;
+
+		if (what & POLLIN) {
+			evread = evdp->evread;
+			which |= EV_READ;
+		}
+
+		if (what & POLLOUT) {
+			evwrite = evdp->evwrite;
+			which |= EV_WRITE;
+		}
+
+		if (!which)
+			continue;
+
+		if (evread != NULL && !(evread->ev_events & EV_PERSIST))
+			event_del(evread);
+		if (evwrite != NULL && evwrite != evread &&
+		    !(evwrite->ev_events & EV_PERSIST))
+			event_del(evwrite);
+
+		if (evread != NULL)
+			event_active(evread, EV_READ, 1);
+		if (evwrite != NULL)
+			event_active(evwrite, EV_WRITE, 1);
+	}
+
+	return (0);
+}
+
+
+int
+devpoll_add(void *arg, struct event *ev)
+{
+	struct devpollop *devpollop = arg;
+	struct evdevpoll *evdp;
+	int fd, events;
+
+	if (ev->ev_events & EV_SIGNAL)
+		return (evsignal_add(ev));
+
+	fd = ev->ev_fd;
+	if (fd >= devpollop->nfds) {
+		/* Extend the file descriptor array as necessary */
+		if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1)
+			return (-1);
+	}
+	evdp = &devpollop->fds[fd];
+
+	/* 
+	 * It's not necessary to OR the existing read/write events that we
+	 * are currently interested in with the new event we are adding.
+	 * The /dev/poll driver ORs any new events with the existing events
+	 * that it has cached for the fd.
+	 */
+
+	events = 0;
+	if (ev->ev_events & EV_READ) {
+		if (evdp->evread && evdp->evread != ev) {
+		   /* There is already a different read event registered */
+		   return(-1);
+		}
+		events |= POLLIN;
+	}
+
+	if (ev->ev_events & EV_WRITE) {
+		if (evdp->evwrite && evdp->evwrite != ev) {
+		   /* There is already a different write event registered */
+		   return(-1);
+		}
+		events |= POLLOUT;
+	}
+
+	if (devpoll_queue(devpollop, fd, events) != 0)
+		return(-1);
+
+	/* Update events responsible */
+	if (ev->ev_events & EV_READ)
+		evdp->evread = ev;
+	if (ev->ev_events & EV_WRITE)
+		evdp->evwrite = ev;
+
+	return (0);
+}
+
+int
+devpoll_del(void *arg, struct event *ev)
+{
+	struct devpollop *devpollop = arg;
+	struct evdevpoll *evdp;
+	int fd, events;
+	int needwritedelete = 1, needreaddelete = 1;
+
+	if (ev->ev_events & EV_SIGNAL)
+		return (evsignal_del(ev));
+
+	fd = ev->ev_fd;
+	if (fd >= devpollop->nfds)
+		return (0);
+	evdp = &devpollop->fds[fd];
+
+	events = 0;
+	if (ev->ev_events & EV_READ)
+		events |= POLLIN;
+	if (ev->ev_events & EV_WRITE)
+		events |= POLLOUT;
+
+	/*
+	 * The only way to remove an fd from the /dev/poll monitored set is
+	 * to use POLLREMOVE by itself.  This removes ALL events for the fd 
+	 * provided so if we care about two events and are only removing one 
+	 * we must re-add the other event after POLLREMOVE.
+	 */
+
+	if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
+		return(-1);
+
+	if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
+		/*
+		 * We're not deleting all events, so we must resubmit the
+		 * event that we are still interested in if one exists.
+		 */
+
+		if ((events & POLLIN) && evdp->evwrite != NULL) {
+			/* Deleting read, still care about write */
+			devpoll_queue(devpollop, fd, POLLOUT);
+			needwritedelete = 0;
+		} else if ((events & POLLOUT) && evdp->evread != NULL) {
+			/* Deleting write, still care about read */
+			devpoll_queue(devpollop, fd, POLLIN);
+			needreaddelete = 0;
+		}
+	}
+
+	if (needreaddelete)
+		evdp->evread = NULL;
+	if (needwritedelete)
+		evdp->evwrite = NULL;
+
+	return (0);
+}
+
+void
+devpoll_dealloc(struct event_base *base, void *arg)
+{
+	struct devpollop *devpollop = arg;
+
+	evsignal_dealloc(base);
+	if (devpollop->fds)
+		free(devpollop->fds);
+	if (devpollop->events)
+		free(devpollop->events);
+	if (devpollop->changes)
+		free(devpollop->changes);
+	if (devpollop->dpfd >= 0)
+		close(devpollop->dpfd);
+
+	memset(devpollop, 0, sizeof(struct devpollop));
+	free(devpollop);
+}
diff -Nrup a/extra/libevent/epoll.c b/extra/libevent/epoll.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/epoll.c	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2000-2003 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/tree.h>
+#include <sys/resource.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/epoll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+/* due to limitations in the epoll interface, we need to keep track of
+ * all file descriptors outself.
+ */
+struct evepoll {
+	struct event *evread;
+	struct event *evwrite;
+};
+
+struct epollop {
+	struct evepoll *fds;
+	int nfds;
+	struct epoll_event *events;
+	int nevents;
+	int epfd;
+};
+
+void *epoll_init	(struct event_base *);
+int epoll_add	(void *, struct event *);
+int epoll_del	(void *, struct event *);
+int epoll_recalc	(struct event_base *, void *, int);
+int epoll_dispatch	(struct event_base *, void *, struct timeval *);
+void epoll_dealloc	(struct event_base *, void *);
+
+struct eventop epollops = {
+	"epoll",
+	epoll_init,
+	epoll_add,
+	epoll_del,
+	epoll_recalc,
+	epoll_dispatch,
+	epoll_dealloc
+};
+
+#ifdef HAVE_SETFD
+#define FD_CLOSEONEXEC(x) do { \
+        if (fcntl(x, F_SETFD, 1) == -1) \
+                event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)
+#else
+#define FD_CLOSEONEXEC(x)
+#endif
+
+#define NEVENT	32000
+
+void *
+epoll_init(struct event_base *base)
+{
+	int epfd, nfiles = NEVENT;
+	struct rlimit rl;
+	struct epollop *epollop;
+
+	/* Disable epollueue when this environment variable is set */
+	if (getenv("EVENT_NOEPOLL"))
+		return (NULL);
+
+	if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
+	    rl.rlim_cur != RLIM_INFINITY) {
+		/*
+		 * Solaris is somewhat retarded - it's important to drop
+		 * backwards compatibility when making changes.  So, don't
+		 * dare to put rl.rlim_cur here.
+		 */
+		nfiles = rl.rlim_cur - 1;
+	}
+
+	/* Initalize the kernel queue */
+
+	if ((epfd = epoll_create(nfiles)) == -1) {
+                event_warn("epoll_create");
+		return (NULL);
+	}
+
+	FD_CLOSEONEXEC(epfd);
+
+	if (!(epollop = calloc(1, sizeof(struct epollop))))
+		return (NULL);
+
+	epollop->epfd = epfd;
+
+	/* Initalize fields */
+	epollop->events = malloc(nfiles * sizeof(struct epoll_event));
+	if (epollop->events == NULL) {
+		free(epollop);
+		return (NULL);
+	}
+	epollop->nevents = nfiles;
+
+	epollop->fds = calloc(nfiles, sizeof(struct evepoll));
+	if (epollop->fds == NULL) {
+		free(epollop->events);
+		free(epollop);
+		return (NULL);
+	}
+	epollop->nfds = nfiles;
+
+	evsignal_init(base);
+
+	return (epollop);
+}
+
+int
+epoll_recalc(struct event_base *base, void *arg, int max)
+{
+	struct epollop *epollop = arg;
+
+	if (max > epollop->nfds) {
+		struct evepoll *fds;
+		int nfds;
+
+		nfds = epollop->nfds;
+		while (nfds < max)
+			nfds <<= 1;
+
+		fds = realloc(epollop->fds, nfds * sizeof(struct evepoll));
+		if (fds == NULL) {
+			event_warn("realloc");
+			return (-1);
+		}
+		epollop->fds = fds;
+		memset(fds + epollop->nfds, 0,
+		    (nfds - epollop->nfds) * sizeof(struct evepoll));
+		epollop->nfds = nfds;
+	}
+
+	return (0);
+}
+
+int
+epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+	struct epollop *epollop = arg;
+	struct epoll_event *events = epollop->events;
+	struct evepoll *evep;
+	int i, res, timeout = -1;
+
+	if (tv != NULL)
+		timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+
+	res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
+
+	if (res == -1) {
+		if (errno != EINTR) {
+			event_warn("epoll_wait");
+			return (-1);
+		}
+
+		evsignal_process(base);
+		return (0);
+	} else if (base->sig.evsignal_caught) {
+		evsignal_process(base);
+	}
+
+	event_debug(("%s: epoll_wait reports %d", __func__, res));
+
+	for (i = 0; i < res; i++) {
+		int which = 0;
+		int what = events[i].events;
+		struct event *evread = NULL, *evwrite = NULL;
+
+		evep = (struct evepoll *)events[i].data.ptr;
+   
+                if (what & EPOLLHUP)
+                        what |= EPOLLIN | EPOLLOUT;
+                else if (what & EPOLLERR)
+                        what |= EPOLLIN | EPOLLOUT;
+
+		if (what & EPOLLIN) {
+			evread = evep->evread;
+			which |= EV_READ;
+		}
+
+		if (what & EPOLLOUT) {
+			evwrite = evep->evwrite;
+			which |= EV_WRITE;
+		}
+
+		if (!which)
+			continue;
+
+		if (evread != NULL && !(evread->ev_events & EV_PERSIST))
+			event_del(evread);
+		if (evwrite != NULL && evwrite != evread &&
+		    !(evwrite->ev_events & EV_PERSIST))
+			event_del(evwrite);
+
+		if (evread != NULL)
+			event_active(evread, EV_READ, 1);
+		if (evwrite != NULL)
+			event_active(evwrite, EV_WRITE, 1);
+	}
+
+	return (0);
+}
+
+
+int
+epoll_add(void *arg, struct event *ev)
+{
+	struct epollop *epollop = arg;
+	struct epoll_event epev = {0, {0}};
+	struct evepoll *evep;
+	int fd, op, events;
+
+	if (ev->ev_events & EV_SIGNAL)
+		return (evsignal_add(ev));
+
+	fd = ev->ev_fd;
+	if (fd >= epollop->nfds) {
+		/* Extent the file descriptor array as necessary */
+		if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
+			return (-1);
+	}
+	evep = &epollop->fds[fd];
+	op = EPOLL_CTL_ADD;
+	events = 0;
+	if (evep->evread != NULL) {
+		events |= EPOLLIN;
+		op = EPOLL_CTL_MOD;
+	}
+	if (evep->evwrite != NULL) {
+		events |= EPOLLOUT;
+		op = EPOLL_CTL_MOD;
+	}
+
+	if (ev->ev_events & EV_READ)
+		events |= EPOLLIN;
+	if (ev->ev_events & EV_WRITE)
+		events |= EPOLLOUT;
+
+	epev.data.ptr = evep;
+	epev.events = events;
+	if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
+			return (-1);
+
+	/* Update events responsible */
+	if (ev->ev_events & EV_READ)
+		evep->evread = ev;
+	if (ev->ev_events & EV_WRITE)
+		evep->evwrite = ev;
+
+	return (0);
+}
+
+int
+epoll_del(void *arg, struct event *ev)
+{
+	struct epollop *epollop = arg;
+	struct epoll_event epev = {0, {0}};
+	struct evepoll *evep;
+	int fd, events, op;
+	int needwritedelete = 1, needreaddelete = 1;
+
+	if (ev->ev_events & EV_SIGNAL)
+		return (evsignal_del(ev));
+
+	fd = ev->ev_fd;
+	if (fd >= epollop->nfds)
+		return (0);
+	evep = &epollop->fds[fd];
+
+	op = EPOLL_CTL_DEL;
+	events = 0;
+
+	if (ev->ev_events & EV_READ)
+		events |= EPOLLIN;
+	if (ev->ev_events & EV_WRITE)
+		events |= EPOLLOUT;
+
+	if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) {
+		if ((events & EPOLLIN) && evep->evwrite != NULL) {
+			needwritedelete = 0;
+			events = EPOLLOUT;
+			op = EPOLL_CTL_MOD;
+		} else if ((events & EPOLLOUT) && evep->evread != NULL) {
+			needreaddelete = 0;
+			events = EPOLLIN;
+			op = EPOLL_CTL_MOD;
+		}
+	}
+
+	epev.events = events;
+	epev.data.ptr = evep;
+
+	if (needreaddelete)
+		evep->evread = NULL;
+	if (needwritedelete)
+		evep->evwrite = NULL;
+
+	if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1)
+		return (-1);
+
+	return (0);
+}
+
+void
+epoll_dealloc(struct event_base *base, void *arg)
+{
+	struct epollop *epollop = arg;
+
+	evsignal_dealloc(base);
+	if (epollop->fds)
+		free(epollop->fds);
+	if (epollop->events)
+		free(epollop->events);
+	if (epollop->epfd >= 0)
+		close(epollop->epfd);
+
+	memset(epollop, 0, sizeof(struct epollop));
+	free(epollop);
+}
diff -Nrup a/extra/libevent/epoll_sub.c b/extra/libevent/epoll_sub.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/epoll_sub.c	2007-10-31 15:36:50 -04:00
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#include <stdint.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+int
+epoll_create(int size)
+{
+	return (syscall(__NR_epoll_create, size));
+}
+
+int
+epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+
+	return (syscall(__NR_epoll_ctl, epfd, op, fd, event));
+}
+
+int
+epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+	return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout));
+}
diff -Nrup a/extra/libevent/evbuffer.c b/extra/libevent/evbuffer.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/evbuffer.c	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2002-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include "event.h"
+
+/* prototypes */
+
+void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
+void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
+
+static int
+bufferevent_add(struct event *ev, int timeout)
+{
+	struct timeval tv, *ptv = NULL;
+
+	if (timeout) {
+		timerclear(&tv);
+		tv.tv_sec = timeout;
+		ptv = &tv;
+	}
+
+	return (event_add(ev, ptv));
+}
+
+/* 
+ * This callback is executed when the size of the input buffer changes.
+ * We use it to apply back pressure on the reading side.
+ */
+
+void
+bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
+    void *arg) {
+	struct bufferevent *bufev = arg;
+	/* 
+	 * If we are below the watermark then reschedule reading if it's
+	 * still enabled.
+	 */
+	if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
+		evbuffer_setcb(buf, NULL, NULL);
+
+		if (bufev->enabled & EV_READ)
+			bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+	}
+}
+
+static void
+bufferevent_readcb(int fd, short event, void *arg)
+{
+	struct bufferevent *bufev = arg;
+	int res = 0;
+	short what = EVBUFFER_READ;
+	size_t len;
+	int howmuch = -1;
+
+	if (event == EV_TIMEOUT) {
+		what |= EVBUFFER_TIMEOUT;
+		goto error;
+	}
+
+	/*
+	 * If we have a high watermark configured then we don't want to
+	 * read more data than would make us reach the watermark.
+	 */
+	if (bufev->wm_read.high != 0)
+		howmuch = bufev->wm_read.high;
+
+	res = evbuffer_read(bufev->input, fd, howmuch);
+	if (res == -1) {
+		if (errno == EAGAIN || errno == EINTR)
+			goto reschedule;
+		/* error case */
+		what |= EVBUFFER_ERROR;
+	} else if (res == 0) {
+		/* eof case */
+		what |= EVBUFFER_EOF;
+	}
+
+	if (res <= 0)
+		goto error;
+
+	bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+
+	/* See if this callbacks meets the water marks */
+	len = EVBUFFER_LENGTH(bufev->input);
+	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+		return;
+	if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
+		struct evbuffer *buf = bufev->input;
+		event_del(&bufev->ev_read);
+
+		/* Now schedule a callback for us */
+		evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
+		return;
+	}
+
+	/* Invoke the user callback - must always be called last */
+	if (bufev->readcb != NULL)
+		(*bufev->readcb)(bufev, bufev->cbarg);
+	return;
+
+ reschedule:
+	bufferevent_add(&bufev->ev_read, bufev->timeout_read);
+	return;
+
+ error:
+	(*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+bufferevent_writecb(int fd, short event, void *arg)
+{
+	struct bufferevent *bufev = arg;
+	int res = 0;
+	short what = EVBUFFER_WRITE;
+
+	if (event == EV_TIMEOUT) {
+		what |= EVBUFFER_TIMEOUT;
+		goto error;
+	}
+
+	if (EVBUFFER_LENGTH(bufev->output)) {
+	    res = evbuffer_write(bufev->output, fd);
+	    if (res == -1) {
+#ifndef WIN32
+/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
+ *set errno. thus this error checking is not portable*/
+		    if (errno == EAGAIN ||
+			errno == EINTR ||
+			errno == EINPROGRESS)
+			    goto reschedule;
+		    /* error case */
+		    what |= EVBUFFER_ERROR;
+
+#else
+				goto reschedule;
+#endif
+
+	    } else if (res == 0) {
+		    /* eof case */
+		    what |= EVBUFFER_EOF;
+	    }
+	    if (res <= 0)
+		    goto error;
+	}
+
+	if (EVBUFFER_LENGTH(bufev->output) != 0)
+		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+
+	/*
+	 * Invoke the user callback if our buffer is drained or below the
+	 * low watermark.
+	 */
+	if (bufev->writecb != NULL &&
+	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+		(*bufev->writecb)(bufev, bufev->cbarg);
+
+	return;
+
+ reschedule:
+	if (EVBUFFER_LENGTH(bufev->output) != 0)
+		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+	return;
+
+ error:
+	(*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+/*
+ * Create a new buffered event object.
+ *
+ * The read callback is invoked whenever we read new data.
+ * The write callback is invoked whenever the output buffer is drained.
+ * The error callback is invoked on a write/read error or on EOF.
+ *
+ * Both read and write callbacks maybe NULL.  The error callback is not
+ * allowed to be NULL and have to be provided always.
+ */
+
+struct bufferevent *
+bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
+    everrorcb errorcb, void *cbarg)
+{
+	struct bufferevent *bufev;
+
+	if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
+		return (NULL);
+
+	if ((bufev->input = evbuffer_new()) == NULL) {
+		free(bufev);
+		return (NULL);
+	}
+
+	if ((bufev->output = evbuffer_new()) == NULL) {
+		evbuffer_free(bufev->input);
+		free(bufev);
+		return (NULL);
+	}
+
+	event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
+	event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
+
+	bufev->readcb = readcb;
+	bufev->writecb = writecb;
+	bufev->errorcb = errorcb;
+
+	bufev->cbarg = cbarg;
+
+	/*
+	 * Set to EV_WRITE so that using bufferevent_write is going to
+	 * trigger a callback.  Reading needs to be explicitly enabled
+	 * because otherwise no data will be available.
+	 */
+	bufev->enabled = EV_WRITE;
+
+	return (bufev);
+}
+
+int
+bufferevent_priority_set(struct bufferevent *bufev, int priority)
+{
+	if (event_priority_set(&bufev->ev_read, priority) == -1)
+		return (-1);
+	if (event_priority_set(&bufev->ev_write, priority) == -1)
+		return (-1);
+
+	return (0);
+}
+
+/* Closing the file descriptor is the responsibility of the caller */
+
+void
+bufferevent_free(struct bufferevent *bufev)
+{
+	event_del(&bufev->ev_read);
+	event_del(&bufev->ev_write);
+
+	evbuffer_free(bufev->input);
+	evbuffer_free(bufev->output);
+
+	free(bufev);
+}
+
+/*
+ * Returns 0 on success;
+ *        -1 on failure.
+ */
+
+int
+bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
+{
+	int res;
+
+	res = evbuffer_add(bufev->output, data, size);
+
+	if (res == -1)
+		return (res);
+
+	/* If everything is okay, we need to schedule a write */
+	if (size > 0 && (bufev->enabled & EV_WRITE))
+		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+
+	return (res);
+}
+
+int
+bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
+{
+	int res;
+
+	res = bufferevent_write(bufev, buf->buffer, buf->off);
+	if (res != -1)
+		evbuffer_drain(buf, buf->off);
+
+	return (res);
+}
+
+size_t
+bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
+{
+	struct evbuffer *buf = bufev->input;
+
+	if (buf->off < size)
+		size = buf->off;
+
+	/* Copy the available data to the user buffer */
+	memcpy(data, buf->buffer, size);
+
+	if (size)
+		evbuffer_drain(buf, size);
+
+	return (size);
+}
+
+int
+bufferevent_enable(struct bufferevent *bufev, short event)
+{
+	if (event & EV_READ) {
+		if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
+			return (-1);
+	}
+	if (event & EV_WRITE) {
+		if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
+			return (-1);
+	}
+
+	bufev->enabled |= event;
+	return (0);
+}
+
+int
+bufferevent_disable(struct bufferevent *bufev, short event)
+{
+	if (event & EV_READ) {
+		if (event_del(&bufev->ev_read) == -1)
+			return (-1);
+	}
+	if (event & EV_WRITE) {
+		if (event_del(&bufev->ev_write) == -1)
+			return (-1);
+	}
+
+	bufev->enabled &= ~event;
+	return (0);
+}
+
+/*
+ * Sets the read and write timeout for a buffered event.
+ */
+
+void
+bufferevent_settimeout(struct bufferevent *bufev,
+    int timeout_read, int timeout_write) {
+	bufev->timeout_read = timeout_read;
+	bufev->timeout_write = timeout_write;
+}
+
+/*
+ * Sets the water marks
+ */
+
+void
+bufferevent_setwatermark(struct bufferevent *bufev, short events,
+    size_t lowmark, size_t highmark)
+{
+	if (events & EV_READ) {
+		bufev->wm_read.low = lowmark;
+		bufev->wm_read.high = highmark;
+	}
+
+	if (events & EV_WRITE) {
+		bufev->wm_write.low = lowmark;
+		bufev->wm_write.high = highmark;
+	}
+
+	/* If the watermarks changed then see if we should call read again */
+	bufferevent_read_pressure_cb(bufev->input,
+	    0, EVBUFFER_LENGTH(bufev->input), bufev);
+}
+
+int
+bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
+{
+	int res;
+
+	res = event_base_set(base, &bufev->ev_read);
+	if (res == -1)
+		return (res);
+
+	res = event_base_set(base, &bufev->ev_write);
+	return (res);
+}
diff -Nrup a/extra/libevent/evdns.c b/extra/libevent/evdns.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/evdns.c	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,3116 @@
+/* $Id: evdns.c 6979 2006-08-04 18:31:13Z nickm $ */
+
+/* The original version of this module was written by Adam Langley; for
+ * a history of modifications, check out the subversion logs.
+ *
+ * When editing this module, try to keep it re-mergeable by Adam.  Don't
+ * reformat the whitespace, add Tor dependencies, or so on.
+ *
+ * TODO:
+ *   - Support IPv6 and PTR records.
+ *   - Replace all externally visible magic numbers with #defined constants.
+ *   - Write doccumentation for APIs of all external functions.
+ */
+
+/* Async DNS Library
+ * Adam Langley <agl@stripped>
+ * http://www.imperialviolet.org/eventdns.html
+ * Public Domain code
+ *
+ * This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * 	Parts developed by Adam Langley <agl@stripped>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ *
+ * Version: 0.1b
+ */
+
+#include <sys/types.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#include "misc.h"
+#endif
+
+/* #define NDEBUG */
+
+#ifndef DNS_USE_CPU_CLOCK_FOR_ID
+#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
+#ifndef DNS_USE_OPENSSL_FOR_ID
+#error Must configure at least one id generation method.
+#error Please see the documentation.
+#endif
+#endif
+#endif
+
+/* #define _POSIX_C_SOURCE 200507 */
+#define _GNU_SOURCE
+
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#error Multiple id options selected
+#endif
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <time.h>
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <openssl/rand.h>
+#endif
+
+#define _FORTIFY_SOURCE 3
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "evdns.h"
+#include "log.h"
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+#define EVDNS_LOG_DEBUG 0
+#define EVDNS_LOG_WARN 1
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#ifdef __USE_ISOC99B
+/* libevent doesn't work without this */
+typedef uint8_t u_char;
+typedef unsigned int uint;
+#endif
+#include <event.h>
+
+#define u64 uint64_t
+#define u32 uint32_t
+#define u16 uint16_t
+#define u8  uint8_t
+
+#define MAX_ADDRS 4  /* maximum number of addresses from a single packet */
+/* which we bother recording */
+
+#define TYPE_A         EVDNS_TYPE_A
+#define TYPE_CNAME     5
+#define TYPE_PTR       EVDNS_TYPE_PTR
+#define TYPE_AAAA      EVDNS_TYPE_AAAA
+
+#define CLASS_INET     EVDNS_CLASS_INET
+
+struct request {
+	u8 *request;  /* the dns packet data */
+	unsigned int request_len;
+	int reissue_count;
+	int tx_count;  /* the number of times that this packet has been sent */
+	unsigned int request_type; /* TYPE_PTR or TYPE_A */
+	void *user_pointer;  /* the pointer given to us for this request */
+	evdns_callback_type user_callback;
+	struct nameserver *ns;  /* the server which we last sent it */
+
+	/* elements used by the searching code */
+	int search_index;
+	struct search_state *search_state;
+	char *search_origname;  /* needs to be free()ed */
+	int search_flags;
+
+	/* these objects are kept in a circular list */
+	struct request *next, *prev;
+
+	struct event timeout_event;
+
+	u16 trans_id;  /* the transaction id */
+	char request_appended;  /* true if the request pointer is data which follows this struct */
+	char transmit_me;  /* needs to be transmitted */
+};
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+	u8 s6_addr[16];
+};
+#endif
+
+struct reply {
+	unsigned int type;
+	unsigned int have_answer;
+	union {
+		struct {
+			u32 addrcount;
+			u32 addresses[MAX_ADDRS];
+		} a;
+		struct {
+			u32 addrcount;
+			struct in6_addr addresses[MAX_ADDRS];
+		} aaaa;
+		struct {
+			char name[HOST_NAME_MAX];
+		} ptr;
+	} data;
+};
+
+struct nameserver {
+	int socket;  /* a connected UDP socket */
+	u32 address;
+	int failed_times;  /* number of times which we have given this server a chance */
+	int timedout;  /* number of times in a row a request has timed out */
+	struct event event;
+	/* these objects are kept in a circular list */
+	struct nameserver *next, *prev;
+	struct event timeout_event;  /* used to keep the timeout for */
+				     /* when we next probe this server. */
+				     /* Valid if state == 0 */
+	char state;  /* zero if we think that this server is down */
+	char choked;  /* true if we have an EAGAIN from this server's socket */
+	char write_waiting;  /* true if we are waiting for EV_WRITE events */
+};
+
+static struct request *req_head = NULL, *req_waiting_head = NULL;
+static struct nameserver *server_head = NULL;
+
+/* Represents a local port where we're listening for DNS requests. Right now, */
+/* only UDP is supported. */
+struct evdns_server_port {
+	int socket; /* socket we use to read queries and write replies. */
+	int refcnt; /* reference count. */
+	char choked; /* Are we currently blocked from writing? */
+	char closing; /* Are we trying to close this port, pending writes? */
+	evdns_request_callback_fn_type user_callback; /* Fn to handle requests */
+	void *user_data; /* Opaque pointer passed to user_callback */
+	struct event event; /* Read/write event */
+	/* circular list of replies that we want to write. */
+	struct server_request *pending_replies;
+};
+
+/* Represents part of a reply being built.	(That is, a single RR.) */
+struct server_reply_item {
+	struct server_reply_item *next; /* next item in sequence. */
+	char *name; /* name part of the RR */
+	u16 type : 16; /* The RR type */
+	u16 class : 16; /* The RR class (usually CLASS_INET) */
+	u32 ttl; /* The RR TTL */
+	char is_name; /* True iff data is a label */
+	u16 datalen; /* Length of data; -1 if data is a label */
+	void *data; /* The contents of the RR */
+};
+
+/* Represents a request that we've received as a DNS server, and holds */
+/* the components of the reply as we're constructing it. */
+struct server_request {
+	/* Pointers to the next and previous entries on the list of replies */
+	/* that we're waiting to write.	 Only set if we have tried to respond */
+	/* and gotten EAGAIN. */
+	struct server_request *next_pending;
+	struct server_request *prev_pending;
+
+	u16 trans_id; /* Transaction id. */
+	struct evdns_server_port *port; /* Which port received this request on? */
+	struct sockaddr_storage addr; /* Where to send the response */
+	socklen_t addrlen; /* length of addr */
+
+	int n_answer; /* how many answer RRs have been set? */
+	int n_authority; /* how many authority RRs have been set? */
+	int n_additional; /* how many additional RRs have been set? */
+
+	struct server_reply_item *answer; /* linked list of answer RRs */
+	struct server_reply_item *authority; /* linked list of authority RRs */
+	struct server_reply_item *additional; /* linked list of additional RRs */
+
+	/* Constructed response.  Only set once we're ready to send a reply. */
+	/* Once this is set, the RR fields are cleared, and no more should be set. */
+	char *response;
+	size_t response_len;
+
+	/* Caller-visible fields: flags, questions. */
+	struct evdns_server_request base;
+};
+
+/* helper macro */
+#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
+
+/* Given a pointer to an evdns_server_request, get the corresponding */
+/* server_request. */
+#define TO_SERVER_REQUEST(base_ptr)										\
+	((struct server_request*)											\
+	 (((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))
+
+/* The number of good nameservers that we have */
+static int global_good_nameservers = 0;
+
+/* inflight requests are contained in the req_head list */
+/* and are actually going out across the network */
+static int global_requests_inflight = 0;
+/* requests which aren't inflight are in the waiting list */
+/* and are counted here */
+static int global_requests_waiting = 0;
+
+static int global_max_requests_inflight = 64;
+
+static struct timeval global_timeout = {5, 0};  /* 5 seconds */
+static int global_max_reissues = 1;  /* a reissue occurs when we get some errors from the server */
+static int global_max_retransmits = 3;  /* number of times we'll retransmit a request which timed out */
+/* number of timeouts in a row before we consider this server to be down */
+static int global_max_nameserver_timeout = 3;
+
+/* These are the timeout values for nameservers. If we find a nameserver is down */
+/* we try to probe it at intervals as given below. Values are in seconds. */
+static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
+static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval);
+
+static struct nameserver *nameserver_pick(void);
+static void evdns_request_insert(struct request *req, struct request **head);
+static void nameserver_ready_callback(int fd, short events, void *arg);
+static int evdns_transmit(void);
+static int evdns_request_transmit(struct request *req);
+static void nameserver_send_probe(struct nameserver *const ns);
+static void search_request_finished(struct request *const);
+static int search_try_next(struct request *const req);
+static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg);
+static void evdns_requests_pump_waiting_queue(void);
+static u16 transaction_id_pick(void);
+static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr);
+static void request_submit(struct request *req);
+
+static int server_request_free(struct server_request *req);
+static void server_request_free_answers(struct server_request *req);
+static void server_port_free(struct evdns_server_port *port);
+static void server_port_ready_callback(int fd, short events, void *arg);
+
+static int strtoint(const char *const str);
+
+#ifdef WIN32
+static int
+last_error(int sock)
+{
+	int optval, optvallen=sizeof(optval);
+	int err = WSAGetLastError();
+	if (err == WSAEWOULDBLOCK && sock >= 0) {
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
+			       &optvallen))
+			return err;
+		if (optval)
+			return optval;
+	}
+	return err;
+
+}
+static int
+error_is_eagain(int err)
+{
+	return err == EAGAIN || err == WSAEWOULDBLOCK;
+}
+static int
+inet_aton(const char *c, struct in_addr *addr)
+{
+	uint32_t r;
+	if (strcmp(c, "255.255.255.255") == 0) {
+		addr->s_addr = 0xffffffffu;
+	} else {
+		r = inet_addr(c);
+		if (r == INADDR_NONE)
+			return 0;
+		addr->s_addr = r;
+	}
+	return 1;
+}
+#define CLOSE_SOCKET(x) closesocket(x)
+#else
+#define last_error(sock) (errno)
+#define error_is_eagain(err) ((err) == EAGAIN)
+#define CLOSE_SOCKET(x) close(x)
+#endif
+
+#define ISSPACE(c) isspace((int)(unsigned char)(c))
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+
+#ifndef NDEBUG
+static const char *
+debug_ntoa(u32 address)
+{
+	static char buf[32];
+	u32 a = ntohl(address);
+	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
+                      (int)(u8)((a>>24)&0xff),
+                      (int)(u8)((a>>16)&0xff),
+                      (int)(u8)((a>>8 )&0xff),
+  		      (int)(u8)((a    )&0xff));
+	return buf;
+}
+#endif
+
+static evdns_debug_log_fn_type evdns_log_fn = NULL;
+
+void
+evdns_set_log_fn(evdns_debug_log_fn_type fn)
+{
+  evdns_log_fn = fn;
+}
+
+#ifdef __GNUC__
+#define EVDNS_LOG_CHECK  __attribute__ ((format(printf, 2, 3)))
+#else
+#define EVDNS_LOG_CHECK
+#endif
+
+static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK;
+static void
+_evdns_log(int warn, const char *fmt, ...)
+{
+  va_list args;
+  static char buf[512];
+  if (!evdns_log_fn)
+    return;
+  va_start(args,fmt);
+#ifdef WIN32
+  _vsnprintf(buf, sizeof(buf), fmt, args);
+#else
+  vsnprintf(buf, sizeof(buf), fmt, args);
+#endif
+  buf[sizeof(buf)-1] = '\0';
+  evdns_log_fn(warn, buf);
+  va_end(args);
+}
+
+#define log _evdns_log
+
+/* This walks the list of inflight requests to find the */
+/* one with a matching transaction id. Returns NULL on */
+/* failure */
+static struct request *
+request_find_from_trans_id(u16 trans_id) {
+	struct request *req = req_head, *const started_at = req_head;
+
+	if (req) {
+		do {
+			if (req->trans_id == trans_id) return req;
+			req = req->next;
+		} while (req != started_at);
+	}
+
+	return NULL;
+}
+
+/* a libevent callback function which is called when a nameserver */
+/* has gone down and we want to test if it has came back to life yet */
+static void
+nameserver_prod_callback(int fd, short events, void *arg) {
+	struct nameserver *const ns = (struct nameserver *) arg;
+        (void)fd;
+        (void)events;
+
+	nameserver_send_probe(ns);
+}
+
+/* a libevent callback which is called when a nameserver probe (to see if */
+/* it has come back to life) times out. We increment the count of failed_times */
+/* and wait longer to send the next probe packet. */
+static void
+nameserver_probe_failed(struct nameserver *const ns) {
+	const struct timeval * timeout;
+	(void) evtimer_del(&ns->timeout_event);
+	if (ns->state == 1) {
+		/* This can happen if the nameserver acts in a way which makes us mark */
+		/* it as bad and then starts sending good replies. */
+		return;
+	}
+
+	timeout =
+	  &global_nameserver_timeouts[MIN(ns->failed_times,
+					  global_nameserver_timeouts_length - 1)];
+	ns->failed_times++;
+
+	evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+	if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) {
+          log(EVDNS_LOG_WARN,
+              "Error from libevent when adding timer event for %s",
+              debug_ntoa(ns->address));
+          /* ???? Do more? */
+        }
+}
+
+/* called when a nameserver has been deemed to have failed. For example, too */
+/* many packets have timed out etc */
+static void
+nameserver_failed(struct nameserver *const ns, const char *msg) {
+	struct request *req, *started_at;
+	/* if this nameserver has already been marked as failed */
+	/* then don't do anything */
+	if (!ns->state) return;
+
+	log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s",
+            debug_ntoa(ns->address), msg);
+	global_good_nameservers--;
+	assert(global_good_nameservers >= 0);
+	if (global_good_nameservers == 0) {
+		log(EVDNS_LOG_WARN, "All nameservers have failed");
+	}
+
+	ns->state = 0;
+	ns->failed_times = 1;
+
+	evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+	if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) {
+		log(EVDNS_LOG_WARN,
+		    "Error from libevent when adding timer event for %s",
+		    debug_ntoa(ns->address));
+		/* ???? Do more? */
+        }
+
+	/* walk the list of inflight requests to see if any can be reassigned to */
+	/* a different server. Requests in the waiting queue don't have a */
+	/* nameserver assigned yet */
+
+	/* if we don't have *any* good nameservers then there's no point */
+	/* trying to reassign requests to one */
+	if (!global_good_nameservers) return;
+
+	req = req_head;
+	started_at = req_head;
+	if (req) {
+		do {
+			if (req->tx_count == 0 && req->ns == ns) {
+				/* still waiting to go out, can be moved */
+				/* to another server */
+				req->ns = nameserver_pick();
+			}
+			req = req->next;
+		} while (req != started_at);
+	}
+}
+
+static void
+nameserver_up(struct nameserver *const ns) {
+	if (ns->state) return;
+	log(EVDNS_LOG_WARN, "Nameserver %s is back up",
+	    debug_ntoa(ns->address));
+	evtimer_del(&ns->timeout_event);
+	ns->state = 1;
+	ns->failed_times = 0;
+	ns->timedout = 0;
+	global_good_nameservers++;
+}
+
+static void
+request_trans_id_set(struct request *const req, const u16 trans_id) {
+	req->trans_id = trans_id;
+	*((u16 *) req->request) = htons(trans_id);
+}
+
+/* Called to remove a request from a list and dealloc it. */
+/* head is a pointer to the head of the list it should be */
+/* removed from or NULL if the request isn't in a list. */
+static void
+request_finished(struct request *const req, struct request **head) {
+	if (head) {
+		if (req->next == req) {
+			/* only item in the list */
+			*head = NULL;
+		} else {
+			req->next->prev = req->prev;
+			req->prev->next = req->next;
+			if (*head == req) *head = req->next;
+		}
+	}
+
+	log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx",
+	    (unsigned long) req);
+	evtimer_del(&req->timeout_event);
+
+	search_request_finished(req);
+	global_requests_inflight--;
+
+	if (!req->request_appended) {
+		/* need to free the request data on it's own */
+		free(req->request);
+	} else {
+		/* the request data is appended onto the header */
+		/* so everything gets free()ed when we: */
+	}
+
+	free(req);
+
+	evdns_requests_pump_waiting_queue();
+}
+
+/* This is called when a server returns a funny error code. */
+/* We try the request again with another server. */
+/* */
+/* return: */
+/*   0 ok */
+/*   1 failed/reissue is pointless */
+static int
+request_reissue(struct request *req) {
+	const struct nameserver *const last_ns = req->ns;
+	/* the last nameserver should have been marked as failing */
+	/* by the caller of this function, therefore pick will try */
+	/* not to return it */
+	req->ns = nameserver_pick();
+	if (req->ns == last_ns) {
+		/* ... but pick did return it */
+		/* not a lot of point in trying again with the */
+		/* same server */
+		return 1;
+	}
+
+	req->reissue_count++;
+	req->tx_count = 0;
+	req->transmit_me = 1;
+
+	return 0;
+}
+
+/* this function looks for space on the inflight queue and promotes */
+/* requests from the waiting queue if it can. */
+static void
+evdns_requests_pump_waiting_queue(void) {
+	while (global_requests_inflight < global_max_requests_inflight &&
+	    global_requests_waiting) {
+		struct request *req;
+		/* move a request from the waiting queue to the inflight queue */
+		assert(req_waiting_head);
+		if (req_waiting_head->next == req_waiting_head) {
+			/* only one item in the queue */
+			req = req_waiting_head;
+			req_waiting_head = NULL;
+		} else {
+			req = req_waiting_head;
+			req->next->prev = req->prev;
+			req->prev->next = req->next;
+			req_waiting_head = req->next;
+		}
+
+		global_requests_waiting--;
+		global_requests_inflight++;
+
+		req->ns = nameserver_pick();
+		request_trans_id_set(req, transaction_id_pick());
+
+		evdns_request_insert(req, &req_head);
+		evdns_request_transmit(req);
+		evdns_transmit();
+	}
+}
+
+static void
+reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) {
+	switch (req->request_type) {
+	case TYPE_A:
+		if (reply)
+			req->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
+							   reply->data.a.addrcount, ttl,
+						 reply->data.a.addresses,
+							   req->user_pointer);
+		else
+			req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
+		return;
+	case TYPE_PTR:
+		if (reply) {
+			char *name = reply->data.ptr.name;
+			req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl,
+							   &name, req->user_pointer);
+		} else {
+			req->user_callback(err, 0, 0, 0, NULL,
+							   req->user_pointer);
+		}
+		return;
+	case TYPE_AAAA:
+		if (reply)
+			req->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA,
+							   reply->data.aaaa.addrcount, ttl,
+							   reply->data.aaaa.addresses,
+							   req->user_pointer);
+		else
+			req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
+                return;
+	}
+	assert(0);
+}
+
+/* this processes a parsed reply packet */
+static void
+reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) {
+	int error;
+	static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED};
+
+	if (flags & 0x020f || !reply || !reply->have_answer) {
+		/* there was an error */
+		if (flags & 0x0200) {
+			error = DNS_ERR_TRUNCATED;
+		} else {
+			u16 error_code = (flags & 0x000f) - 1;
+			if (error_code > 4) {
+				error = DNS_ERR_UNKNOWN;
+			} else {
+				error = error_codes[error_code];
+			}
+		}
+
+		switch(error) {
+		case DNS_ERR_NOTIMPL:
+		case DNS_ERR_REFUSED:
+			/* we regard these errors as marking a bad nameserver */
+			if (req->reissue_count < global_max_reissues) {
+				char msg[64];
+				snprintf(msg, sizeof(msg), "Bad response %d (%s)",
+					 error, evdns_err_to_string(error));
+				nameserver_failed(req->ns, msg);
+				if (!request_reissue(req)) return;
+			}
+			break;
+		case DNS_ERR_SERVERFAILED:
+			/* rcode 2 (servfailed) sometimes means "we are broken" and
+			 * sometimes (with some binds) means "that request was very
+			 * confusing."  Treat this as a timeout, not a failure. 
+			 */
+			log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; "
+				"will allow the request to time out.",
+				debug_ntoa(req->ns->address));
+			break;
+		default:
+			/* we got a good reply from the nameserver */
+			nameserver_up(req->ns);
+		}
+
+		if (req->search_state && req->request_type != TYPE_PTR) {
+			/* if we have a list of domains to search in, try the next one */
+			if (!search_try_next(req)) {
+				/* a new request was issued so this request is finished and */
+				/* the user callback will be made when that request (or a */
+				/* child of it) finishes. */
+				request_finished(req, &req_head);
+				return;
+			}
+		}
+
+		/* all else failed. Pass the failure up */
+		reply_callback(req, 0, error, NULL);
+		request_finished(req, &req_head);
+	} else {
+		/* all ok, tell the user */
+		reply_callback(req, ttl, 0, reply);
+		nameserver_up(req->ns);
+		request_finished(req, &req_head);
+	}
+}
+
+static int
+name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
+	int name_end = -1;
+	int j = *idx;
+	int ptr_count = 0;
+#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0)
+#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0)
+#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0)
+
+	char *cp = name_out;
+	const char *const end = name_out + name_out_len;
+
+	/* Normally, names are a series of length prefixed strings terminated */
+	/* with a length of 0 (the lengths are u8's < 63). */
+	/* However, the length can start with a pair of 1 bits and that */
+	/* means that the next 14 bits are a pointer within the current */
+	/* packet. */
+
+	for(;;) {
+		u8 label_len;
+		if (j >= length) return -1;
+		GET8(label_len);
+		if (!label_len) break;
+		if (label_len & 0xc0) {
+			u8 ptr_low;
+			GET8(ptr_low);
+			if (name_end < 0) name_end = j;
+			j = (((int)label_len & 0x3f) << 8) + ptr_low;
+			/* Make sure that the target offset is in-bounds. */
+			if (j < 0 || j >= length) return -1;
+			/* If we've jumped more times than there are characters in the
+			 * message, we must have a loop. */
+			if (++ptr_count > length) return -1;
+			continue;
+		}
+		if (label_len > 63) return -1;
+		if (cp != name_out) {
+			if (cp + 1 >= end) return -1;
+			*cp++ = '.';
+		}
+		if (cp + label_len >= end) return -1;
+		memcpy(cp, packet + j, label_len);
+		cp += label_len;
+		j += label_len;
+	}
+	if (cp >= end) return -1;
+	*cp = '\0';
+	if (name_end < 0)
+		*idx = j;
+	else
+		*idx = name_end;
+	return 0;
+ err:
+	return -1;
+}
+
+/* parses a raw request from a nameserver */
+static int
+reply_parse(u8 *packet, int length) {
+	int j = 0;  /* index into packet */
+	u16 _t;  /* used by the macros */
+	u32 _t32;  /* used by the macros */
+	char tmp_name[256]; /* used by the macros */
+
+	u16 trans_id, questions, answers, authority, additional, datalength;
+        u16 flags = 0;
+	u32 ttl, ttl_r = 0xffffffff;
+	struct reply reply;
+	struct request *req = NULL;
+	unsigned int i;
+
+	GET16(trans_id);
+	GET16(flags);
+	GET16(questions);
+	GET16(answers);
+	GET16(authority);
+	GET16(additional);
+	(void) authority; /* suppress "unused variable" warnings. */
+	(void) additional; /* suppress "unused variable" warnings. */
+
+	req = request_find_from_trans_id(trans_id);
+	if (!req) return -1;
+
+	memset(&reply, 0, sizeof(reply));
+
+	/* If it's not an answer, it doesn't correspond to any request. */
+	if (!(flags & 0x8000)) return -1;  /* must be an answer */
+	if (flags & 0x020f) {
+		/* there was an error */
+		goto err;
+	}
+	/* if (!answers) return; */  /* must have an answer of some form */
+
+	/* This macro skips a name in the DNS reply. */
+#define SKIP_NAME \
+	do { tmp_name[0] = '\0';					\
+		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
+			goto err;													\
+	} while(0);
+
+	reply.type = req->request_type;
+
+	/* skip over each question in the reply */
+	for (i = 0; i < questions; ++i) {
+		/* the question looks like
+		 *   <label:name><u16:type><u16:class>
+		 */
+		SKIP_NAME;
+		j += 4;
+		if (j >= length) goto err;
+	}
+
+	/* now we have the answer section which looks like
+	 * <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
+	 */
+
+	for (i = 0; i < answers; ++i) {
+		u16 type, class;
+
+		SKIP_NAME;
+		GET16(type);
+		GET16(class);
+		GET32(ttl);
+		GET16(datalength);
+
+		if (type == TYPE_A && class == CLASS_INET) {
+			int addrcount, addrtocopy;
+			if (req->request_type != TYPE_A) {
+				j += datalength; continue;
+			}
+			if ((datalength & 3) != 0) /* not an even number of As. */
+			    goto err;
+			addrcount = datalength >> 2;
+			addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
+
+			ttl_r = MIN(ttl_r, ttl);
+			/* we only bother with the first four addresses. */
+			if (j + 4*addrtocopy > length) goto err;
+			memcpy(&reply.data.a.addresses[reply.data.a.addrcount],
+				   packet + j, 4*addrtocopy);
+			j += 4*addrtocopy;
+			reply.data.a.addrcount += addrtocopy;
+			reply.have_answer = 1;
+			if (reply.data.a.addrcount == MAX_ADDRS) break;
+		} else if (type == TYPE_PTR && class == CLASS_INET) {
+			if (req->request_type != TYPE_PTR) {
+				j += datalength; continue;
+			}
+			if (name_parse(packet, length, &j, reply.data.ptr.name,
+						   sizeof(reply.data.ptr.name))<0)
+				goto err;
+			ttl_r = MIN(ttl_r, ttl);
+			reply.have_answer = 1;
+			break;
+		} else if (type == TYPE_AAAA && class == CLASS_INET) {
+			int addrcount, addrtocopy;
+			if (req->request_type != TYPE_AAAA) {
+				j += datalength; continue;
+			}
+			if ((datalength & 15) != 0) /* not an even number of AAAAs. */
+				goto err;
+			addrcount = datalength >> 4;  /* each address is 16 bytes long */
+			addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount);
+			ttl_r = MIN(ttl_r, ttl);
+
+			/* we only bother with the first four addresses. */
+			if (j + 16*addrtocopy > length) goto err;
+			memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount],
+				   packet + j, 16*addrtocopy);
+			reply.data.aaaa.addrcount += addrtocopy;
+			j += 16*addrtocopy;
+			reply.have_answer = 1;
+			if (reply.data.aaaa.addrcount == MAX_ADDRS) break;
+		} else {
+			/* skip over any other type of resource */
+			j += datalength;
+		}
+	}
+
+	reply_handle(req, flags, ttl_r, &reply);
+	return 0;
+ err:
+	if (req)
+		reply_handle(req, flags, 0, NULL);
+	return -1;
+}
+
+/* Parse a raw request (packet,length) sent to a nameserver port (port) from */
+/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */
+/* callback. */
+static int
+request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen)
+{
+	int j = 0;	/* index into packet */
+	u16 _t;	 /* used by the macros */
+	char tmp_name[256]; /* used by the macros */
+
+	int i;
+	u16 trans_id, flags, questions, answers, authority, additional;
+	struct server_request *server_req = NULL;
+
+	/* Get the header fields */
+	GET16(trans_id);
+	GET16(flags);
+	GET16(questions);
+	GET16(answers);
+	GET16(authority);
+	GET16(additional);
+
+	if (flags & 0x8000) return -1; /* Must not be an answer. */
+	if (flags & 0x7800) return -1; /* only standard queries are supported */
+	flags &= 0x0300; /* Only TC and RD get preserved. */
+
+	server_req = malloc(sizeof(struct server_request));
+	if (server_req == NULL) return -1;
+	memset(server_req, 0, sizeof(struct server_request));
+
+	server_req->trans_id = trans_id;
+	memcpy(&server_req->addr, addr, addrlen);
+	server_req->addrlen = addrlen;
+
+	server_req->base.flags = flags;
+	server_req->base.nquestions = 0;
+	server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions);
+	if (server_req->base.questions == NULL)
+		goto err;
+
+	for (i = 0; i < questions; ++i) {
+		u16 type, class;
+		struct evdns_server_question *q;
+		int namelen;
+		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)
+			goto err;
+		GET16(type);
+		GET16(class);
+		namelen = strlen(tmp_name);
+		q = malloc(sizeof(struct evdns_server_question) + namelen);
+		if (!q)
+			goto err;
+		q->type = type;
+		q->class = class;
+		memcpy(q->name, tmp_name, namelen+1);
+		server_req->base.questions[server_req->base.nquestions++] = q;
+	}
+
+	/* Ignore answers, authority, and additional. */
+
+	server_req->port = port;
+	port->refcnt++;
+	port->user_callback(&(server_req->base), port->user_data);
+
+	return 0;
+err:
+	if (server_req) {
+		if (server_req->base.questions) {
+			for (i = 0; i < server_req->base.nquestions; ++i)
+				free(server_req->base.questions[i]);
+			free(server_req->base.questions);
+		}
+		free(server_req);
+	}
+	return -1;
+
+#undef SKIP_NAME
+#undef GET32
+#undef GET16
+#undef GET8
+}
+
+/* Try to choose a strong transaction id which isn't already in flight */
+static u16
+transaction_id_pick(void) {
+	for (;;) {
+		const struct request *req = req_head, *started_at;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+		struct timespec ts;
+		u16 trans_id;
+#ifdef CLOCK_MONOTONIC
+		if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+#else
+		if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
+#endif
+			event_err(1, "clock_gettime");
+                trans_id = ts.tv_nsec & 0xffff;
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+		struct timeval tv;
+		u16 trans_id;
+		gettimeofday(&tv, NULL);
+                trans_id = tv.tv_usec & 0xffff;
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+		u16 trans_id;
+		if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
+			/* in the case that the RAND call fails we back */
+			/* down to using gettimeofday. */
+			struct timeval tv;
+			gettimeofday(&tv, NULL);
+			trans_id = tv.tv_usec & 0xffff; */
+			abort();
+		}
+#endif
+
+		if (trans_id == 0xffff) continue;
+		/* now check to see if that id is already inflight */
+		req = started_at = req_head;
+		if (req) {
+			do {
+				if (req->trans_id == trans_id) break;
+				req = req->next;
+			} while (req != started_at);
+		}
+		/* we didn't find it, so this is a good id */
+		if (req == started_at) return trans_id;
+	}
+}
+
+/* choose a namesever to use. This function will try to ignore */
+/* nameservers which we think are down and load balance across the rest */
+/* by updating the server_head global each time. */
+static struct nameserver *
+nameserver_pick(void) {
+	struct nameserver *started_at = server_head, *picked;
+	if (!server_head) return NULL;
+
+	/* if we don't have any good nameservers then there's no */
+	/* point in trying to find one. */
+	if (!global_good_nameservers) {
+		server_head = server_head->next;
+		return server_head;
+	}
+
+	/* remember that nameservers are in a circular list */
+	for (;;) {
+		if (server_head->state) {
+			/* we think this server is currently good */
+			picked = server_head;
+			server_head = server_head->next;
+			return picked;
+		}
+
+		server_head = server_head->next;
+		if (server_head == started_at) {
+			/* all the nameservers seem to be down */
+			/* so we just return this one and hope for the */
+			/* best */
+			assert(global_good_nameservers == 0);
+			picked = server_head;
+			server_head = server_head->next;
+			return picked;
+		}
+	}
+}
+
+/* this is called when a namesever socket is ready for reading */
+static void
+nameserver_read(struct nameserver *ns) {
+	u8 packet[1500];
+
+	for (;;) {
+          	const int r = recv(ns->socket, packet, sizeof(packet), 0);
+		if (r < 0) {
+			int err = last_error(ns->socket);
+			if (error_is_eagain(err)) return;
+			nameserver_failed(ns, strerror(err));
+			return;
+		}
+		ns->timedout = 0;
+		reply_parse(packet, r);
+	}
+}
+
+/* Read a packet from a DNS client on a server port s, parse it, and */
+/* act accordingly. */
+static void
+server_port_read(struct evdns_server_port *s) {
+	u8 packet[1500];
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	int r;
+
+	for (;;) {
+		addrlen = sizeof(struct sockaddr_storage);
+		r = recvfrom(s->socket, packet, sizeof(packet), 0,
+					 (struct sockaddr*) &addr, &addrlen);
+		if (r < 0) {
+			int err = last_error(s->socket);
+			if (error_is_eagain(err)) return;
+			log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.",
+				strerror(err), err);
+			return;
+		}
+		request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen);
+	}
+}
+
+/* Try to write all pending replies on a given DNS server port. */
+static void
+server_port_flush(struct evdns_server_port *port)
+{
+	while (port->pending_replies) {
+		struct server_request *req = port->pending_replies;
+		int r = sendto(port->socket, req->response, req->response_len, 0,
+			   (struct sockaddr*) &req->addr, req->addrlen);
+		if (r < 0) {
+			int err = last_error(port->socket);
+			if (error_is_eagain(err))
+				return;
+			log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", strerror(err), err);
+		}
+		if (server_request_free(req)) {
+			/* we released the last reference to req->port. */
+			return;
+		}
+	}
+
+	/* We have no more pending requests; stop listening for 'writeable' events. */
+	(void) event_del(&port->event);
+	event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
+			  server_port_ready_callback, port);
+	if (event_add(&port->event, NULL) < 0) {
+		log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server.");
+		/* ???? Do more? */
+	}
+}
+
+/* set if we are waiting for the ability to write to this server. */
+/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */
+/* we stop these events. */
+static void
+nameserver_write_waiting(struct nameserver *ns, char waiting) {
+	if (ns->write_waiting == waiting) return;
+
+	ns->write_waiting = waiting;
+	(void) event_del(&ns->event);
+	event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST,
+			nameserver_ready_callback, ns);
+	if (event_add(&ns->event, NULL) < 0) {
+          log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s",
+              debug_ntoa(ns->address));
+          /* ???? Do more? */
+        }
+}
+
+/* a callback function. Called by libevent when the kernel says that */
+/* a nameserver socket is ready for writing or reading */
+static void
+nameserver_ready_callback(int fd, short events, void *arg) {
+	struct nameserver *ns = (struct nameserver *) arg;
+        (void)fd;
+
+	if (events & EV_WRITE) {
+		ns->choked = 0;
+		if (!evdns_transmit()) {
+			nameserver_write_waiting(ns, 0);
+		}
+	}
+	if (events & EV_READ) {
+		nameserver_read(ns);
+	}
+}
+
+/* a callback function. Called by libevent when the kernel says that */
+/* a server socket is ready for writing or reading. */
+static void
+server_port_ready_callback(int fd, short events, void *arg) {
+	struct evdns_server_port *port = (struct evdns_server_port *) arg;
+	(void) fd;
+
+	if (events & EV_WRITE) {
+		port->choked = 0;
+		server_port_flush(port);
+	}
+	if (events & EV_READ) {
+		server_port_read(port);
+	}
+}
+
+/* This is an inefficient representation; only use it via the dnslabel_table_*
+ * functions, so that is can be safely replaced with something smarter later. */
+#define MAX_LABELS 128
+/* Structures used to implement name compression */
+struct dnslabel_entry { char *v; off_t pos; };
+struct dnslabel_table {
+	int n_labels; /* number of current entries */
+	/* map from name to position in message */
+	struct dnslabel_entry labels[MAX_LABELS];
+};
+
+/* Initialize dnslabel_table. */
+static void
+dnslabel_table_init(struct dnslabel_table *table)
+{
+	table->n_labels = 0;
+}
+
+/* Free all storage held by table, but not the table itself. */
+static void
+dnslabel_clear(struct dnslabel_table *table)
+{
+	int i;
+	for (i = 0; i < table->n_labels; ++i)
+		free(table->labels[i].v);
+	table->n_labels = 0;
+}
+
+/* return the position of the label in the current message, or -1 if the label */
+/* hasn't been used yet. */
+static int
+dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label)
+{
+	int i;
+	for (i = 0; i < table->n_labels; ++i) {
+		if (!strcmp(label, table->labels[i].v))
+			return table->labels[i].pos;
+	}
+	return -1;
+}
+
+/* remember that we've used the label at position pos */
+static int
+dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos)
+{
+	char *v;
+	int p;
+	if (table->n_labels == MAX_LABELS)
+		return (-1);
+	v = strdup(label);
+	if (v == NULL)
+		return (-1);
+	p = table->n_labels++;
+	table->labels[p].v = v;
+	table->labels[p].pos = pos;
+
+	return (0);
+}
+
+/* Converts a string to a length-prefixed set of DNS labels, starting */
+/* at buf[j]. name and buf must not overlap. name_len should be the length */
+/* of name.	 table is optional, and is used for compression. */
+/* */
+/* Input: abc.def */
+/* Output: <3>abc<3>def<0> */
+/* */
+/* Returns the first index after the encoded name, or negative on error. */
+/*	 -1	 label was > 63 bytes */
+/*	 -2	 name too long to fit in buffer. */
+/* */
+static off_t
+dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j,
+				  const char *name, const int name_len,
+				  struct dnslabel_table *table) {
+	const char *end = name + name_len;
+	int ref = 0;
+	u16 _t;
+
+#define APPEND16(x) do {						   \
+		if (j + 2 > (off_t)buf_len)				   \
+			goto overflow;						   \
+		_t = htons(x);							   \
+		memcpy(buf + j, &_t, 2);				   \
+		j += 2;									   \
+	} while (0)
+#define APPEND32(x) do {						   \
+		if (j + 4 > (off_t)buf_len)				   \
+			goto overflow;						   \
+		_t32 = htonl(x);						   \
+		memcpy(buf + j, &_t32, 4);				   \
+		j += 4;									   \
+	} while (0)
+
+	if (name_len > 255) return -2;
+
+	for (;;) {
+		const char *const start = name;
+		if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) {
+			APPEND16(ref | 0xc000);
+			return j;
+		}
+		name = strchr(name, '.');
+		if (!name) {
+			const unsigned int label_len = end - start;
+			if (label_len > 63) return -1;
+			if ((size_t)(j+label_len+1) > buf_len) return -2;
+			if (table) dnslabel_table_add(table, start, j);
+			buf[j++] = label_len;
+
+			memcpy(buf + j, start, end - start);
+			j += end - start;
+			break;
+		} else {
+			/* append length of the label. */
+			const unsigned int label_len = name - start;
+			if (label_len > 63) return -1;
+			if ((size_t)(j+label_len+1) > buf_len) return -2;
+			if (table) dnslabel_table_add(table, start, j);
+			buf[j++] = label_len;
+
+			memcpy(buf + j, start, name - start);
+			j += name - start;
+			/* hop over the '.' */
+			name++;
+		}
+	}
+
+	/* the labels must be terminated by a 0. */
+	/* It's possible that the name ended in a . */
+	/* in which case the zero is already there */
+	if (!j || buf[j-1]) buf[j++] = 0;
+	return j;
+ overflow:
+	return (-2);
+}
+
+/* Finds the length of a dns request for a DNS name of the given */
+/* length. The actual request may be smaller than the value returned */
+/* here */
+static int
+evdns_request_len(const int name_len) {
+	return 96 + /* length of the DNS standard header */
+		name_len + 2 +
+		4;  /* space for the resource type */
+}
+
+/* build a dns request packet into buf. buf should be at least as long */
+/* as evdns_request_len told you it should be. */
+/* */
+/* Returns the amount of space used. Negative on error. */
+static int
+evdns_request_data_build(const char *const name, const int name_len,
+    const u16 trans_id, const u16 type, const u16 class,
+    u8 *const buf, size_t buf_len) {
+	off_t j = 0;  /* current offset into buf */
+	u16 _t;  /* used by the macros */
+
+	APPEND16(trans_id);
+	APPEND16(0x0100);  /* standard query, recusion needed */
+	APPEND16(1);  /* one question */
+	APPEND16(0);  /* no answers */
+	APPEND16(0);  /* no authority */
+	APPEND16(0);  /* no additional */
+
+	j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL);
+	if (j < 0) {
+		return (int)j;
+	}
+	
+	APPEND16(type);
+	APPEND16(class);
+
+	return (int)j;
+ overflow:
+	return (-1);
+}
+
+/* exported function */
+struct evdns_server_port *
+evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data)
+{
+	struct evdns_server_port *port;
+	if (!(port = malloc(sizeof(struct evdns_server_port))))
+		return NULL;
+	memset(port, 0, sizeof(struct evdns_server_port));
+
+	assert(!is_tcp); /* TCP sockets not yet implemented */
+	port->socket = socket;
+	port->refcnt = 1;
+	port->choked = 0;
+	port->closing = 0;
+	port->user_callback = cb;
+	port->user_data = user_data;
+	port->pending_replies = NULL;
+
+	event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
+			  server_port_ready_callback, port);
+	event_add(&port->event, NULL); /* check return. */
+	return port;
+}
+
+/* exported function */
+void
+evdns_close_server_port(struct evdns_server_port *port)
+{
+	if (--port->refcnt == 0)
+		server_port_free(port);
+	port->closing = 1;
+}
+
+/* exported function */
+int
+evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data)
+{
+	struct server_request *req = TO_SERVER_REQUEST(_req);
+	struct server_reply_item **itemp, *item;
+	int *countp;
+
+	if (req->response) /* have we already answered? */
+		return (-1);
+
+	switch (section) {
+	case EVDNS_ANSWER_SECTION:
+		itemp = &req->answer;
+		countp = &req->n_answer;
+		break;
+	case EVDNS_AUTHORITY_SECTION:
+		itemp = &req->authority;
+		countp = &req->n_authority;
+		break;
+	case EVDNS_ADDITIONAL_SECTION:
+		itemp = &req->additional;
+		countp = &req->n_additional;
+		break;
+	default:
+		return (-1);
+	}
+	while (*itemp) {
+		itemp = &((*itemp)->next);
+	}
+	item = malloc(sizeof(struct server_reply_item));
+	if (!item)
+		return -1;
+	item->next = NULL;
+	if (!(item->name = strdup(name))) {
+		free(item);
+		return -1;
+	}
+	item->type = type;
+	item->class = class;
+	item->ttl = ttl;
+	item->is_name = is_name != 0;
+	item->datalen = 0;
+	item->data = NULL;
+	if (data) {
+		if (item->is_name) {
+			if (!(item->data = strdup(data))) {
+				free(item->name);
+				free(item);
+				return -1;
+			}
+			item->datalen = (u16)-1;
+		} else {
+			if (!(item->data = malloc(datalen))) {
+				free(item->name);
+				free(item);
+				return -1;
+			}
+			item->datalen = datalen;
+			memcpy(item->data, data, datalen);
+		}
+	}
+
+	*itemp = item;
+	++(*countp);
+	return 0;
+}
+
+/* exported function */
+int
+evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
+{
+	return evdns_server_request_add_reply(
+		  req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
+		  ttl, n*4, 0, addrs);
+}
+
+/* exported function */
+int
+evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
+{
+	return evdns_server_request_add_reply(
+		  req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET,
+		  ttl, n*16, 0, addrs);
+}
+
+/* exported function */
+int
+evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl)
+{
+	u32 a;
+	char buf[32];
+	assert(in || inaddr_name);
+	assert(!(in && inaddr_name));
+	if (in) {
+		a = ntohl(in->s_addr);
+		snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+				(int)(u8)((a	)&0xff),
+				(int)(u8)((a>>8 )&0xff),
+				(int)(u8)((a>>16)&0xff),
+				(int)(u8)((a>>24)&0xff));
+		inaddr_name = buf;
+	}
+	return evdns_server_request_add_reply(
+		  req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET,
+		  ttl, -1, 1, hostname);
+}
+
+/* exported function */
+int
+evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl)
+{
+	return evdns_server_request_add_reply(
+		  req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
+		  ttl, -1, 1, cname);
+}
+
+
+static int
+evdns_server_request_format_response(struct server_request *req, int err)
+{
+	unsigned char buf[1500];
+	size_t buf_len = sizeof(buf);
+	off_t j = 0, r;
+	u16 _t;
+	u32 _t32;
+	int i;
+	u16 flags;
+	struct dnslabel_table table;
+
+	if (err < 0 || err > 15) return -1;
+
+	/* Set response bit and error code; copy OPCODE and RD fields from
+	 * question; copy RA and AA if set by caller. */
+	flags = req->base.flags;
+	flags |= (0x8000 | err);
+
+	dnslabel_table_init(&table);
+	APPEND16(req->trans_id);
+	APPEND16(flags);
+	APPEND16(req->base.nquestions);
+	APPEND16(req->n_answer);
+	APPEND16(req->n_authority);
+	APPEND16(req->n_additional);
+
+	/* Add questions. */
+	for (i=0; i < req->base.nquestions; ++i) {
+		const char *s = req->base.questions[i]->name;
+		j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table);
+		if (j < 0) {
+			dnslabel_clear(&table);
+			return (int) j;
+		}
+		APPEND16(req->base.questions[i]->type);
+		APPEND16(req->base.questions[i]->class);
+	}
+
+	/* Add answer, authority, and additional sections. */
+	for (i=0; i<3; ++i) {
+		struct server_reply_item *item;
+		if (i==0)
+			item = req->answer;
+		else if (i==1)
+			item = req->authority;
+		else
+			item = req->additional;
+		while (item) {
+			r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table);
+			if (r < 0)
+				goto overflow;
+			j = r;
+
+			APPEND16(item->type);
+			APPEND16(item->class);
+			APPEND32(item->ttl);
+			if (item->is_name) {
+				off_t len_idx = j, name_start;
+				j += 2;
+				name_start = j;
+				r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table);
+				if (r < 0)
+					goto overflow;
+				j = r;
+				_t = htons( (j-name_start) );
+				memcpy(buf+len_idx, &_t, 2);
+			} else {
+				APPEND16(item->datalen);
+				if (j+item->datalen > (off_t)buf_len)
+					goto overflow;
+				memcpy(buf+j, item->data, item->datalen);
+				j += item->datalen;
+			}
+			item = item->next;
+		}
+	}
+
+	if (j > 512) {
+overflow:
+		j = 512;
+		buf[3] |= 0x02; /* set the truncated bit. */
+	}
+
+	req->response_len = j;
+
+	if (!(req->response = malloc(req->response_len))) {
+		server_request_free_answers(req);
+		dnslabel_clear(&table);
+		return (-1);
+	}
+	memcpy(req->response, buf, req->response_len);
+	server_request_free_answers(req);
+	dnslabel_clear(&table);
+	return (0);
+}
+
+/* exported function */
+int
+evdns_server_request_respond(struct evdns_server_request *_req, int err)
+{
+	struct server_request *req = TO_SERVER_REQUEST(_req);
+	struct evdns_server_port *port = req->port;
+	int r;
+	if (!req->response) {
+		if ((r = evdns_server_request_format_response(req, err))<0)
+			return r;
+	}
+
+	r = sendto(port->socket, req->response, req->response_len, 0,
+			   (struct sockaddr*) &req->addr, req->addrlen);
+	if (r<0) {
+		int err = last_error(port->socket);
+		if (! error_is_eagain(err))
+			return -1;
+
+		if (port->pending_replies) {
+			req->prev_pending = port->pending_replies->prev_pending;
+			req->next_pending = port->pending_replies;
+			req->prev_pending->next_pending =
+				req->next_pending->prev_pending = req;
+		} else {
+			req->prev_pending = req->next_pending = req;
+			port->pending_replies = req;
+			port->choked = 1;
+
+			(void) event_del(&port->event);
+			event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port);
+
+			if (event_add(&port->event, NULL) < 0) {
+				log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server");
+			}
+
+		}
+
+		return 1;
+	}
+	if (server_request_free(req))
+		return 0;
+
+	if (port->pending_replies)
+		server_port_flush(port);
+
+	return 0;
+}
+
+/* Free all storage held by RRs in req. */
+static void
+server_request_free_answers(struct server_request *req)
+{
+	struct server_reply_item *victim, *next, **list;
+	int i;
+	for (i = 0; i < 3; ++i) {
+		if (i==0)
+			list = &req->answer;
+		else if (i==1)
+			list = &req->authority;
+		else
+			list = &req->additional;
+
+		victim = *list;
+		while (victim) {
+			next = victim->next;
+			free(victim->name);
+			if (victim->data)
+				free(victim->data);
+			free(victim);
+			victim = next;
+		}
+		*list = NULL;
+	}
+}
+
+/* Free all storage held by req, and remove links to it. */
+/* return true iff we just wound up freeing the server_port. */
+static int
+server_request_free(struct server_request *req)
+{
+	int i, rc=1;
+	if (req->base.questions) {
+		for (i = 0; i < req->base.nquestions; ++i)
+			free(req->base.questions[i]);
+		free(req->base.questions);
+	}
+
+	if (req->port) {
+		if (req->port->pending_replies == req) {
+			if (req->next_pending)
+				req->port->pending_replies = req->next_pending;
+			else
+				req->port->pending_replies = NULL;
+		}
+		rc = --req->port->refcnt;
+	}
+
+	if (req->response) {
+		free(req->response);
+	}
+
+	server_request_free_answers(req);
+
+	if (req->next_pending && req->next_pending != req) {
+		req->next_pending->prev_pending = req->prev_pending;
+		req->prev_pending->next_pending = req->next_pending;
+	}
+
+	if (rc == 0) {
+		server_port_free(req->port);
+		free(req);
+		return (1);
+	}
+	free(req);
+	return (0);
+}
+
+/* Free all storage held by an evdns_server_port.  Only called when  */
+static void
+server_port_free(struct evdns_server_port *port)
+{
+	assert(port);
+	assert(!port->refcnt);
+	assert(!port->pending_replies);
+	if (port->socket > 0) {
+		CLOSE_SOCKET(port->socket);
+		port->socket = -1;
+	}
+	(void) event_del(&port->event);
+	/* XXXX actually free the port? -NM */
+}
+
+/* exported function */
+int
+evdns_server_request_drop(struct evdns_server_request *_req)
+{
+	struct server_request *req = TO_SERVER_REQUEST(_req);
+	server_request_free(req);
+	return 0;
+}
+
+/* exported function */
+int
+evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len)
+{
+	struct server_request *req = TO_SERVER_REQUEST(_req);
+	if (addr_len < (int)req->addrlen)
+		return -1;
+	memcpy(sa, &(req->addr), req->addrlen);
+	return req->addrlen;
+}
+
+#undef APPEND16
+#undef APPEND32
+
+/* this is a libevent callback function which is called when a request */
+/* has timed out. */
+static void
+evdns_request_timeout_callback(int fd, short events, void *arg) {
+	struct request *const req = (struct request *) arg;
+        (void) fd;
+        (void) events;
+
+	log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg);
+
+	req->ns->timedout++;
+	if (req->ns->timedout > global_max_nameserver_timeout) {
+		req->ns->timedout = 0;
+		nameserver_failed(req->ns, "request timed out.");
+	}
+
+	(void) evtimer_del(&req->timeout_event);
+	if (req->tx_count >= global_max_retransmits) {
+		/* this request has failed */
+		reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
+		request_finished(req, &req_head);
+	} else {
+		/* retransmit it */
+		evdns_request_transmit(req);
+	}
+}
+
+/* try to send a request to a given server. */
+/* */
+/* return: */
+/*   0 ok */
+/*   1 temporary failure */
+/*   2 other failure */
+static int
+evdns_request_transmit_to(struct request *req, struct nameserver *server) {
+	const int r = send(server->socket, req->request, req->request_len, 0);
+	if (r < 0) {
+		int err = last_error(server->socket);
+		if (error_is_eagain(err)) return 1;
+		nameserver_failed(req->ns, strerror(err));
+		return 2;
+	} else if (r != (int)req->request_len) {
+		return 1;  /* short write */
+	} else {
+		return 0;
+	}
+}
+
+/* try to send a request, updating the fields of the request */
+/* as needed */
+/* */
+/* return: */
+/*   0 ok */
+/*   1 failed */
+static int
+evdns_request_transmit(struct request *req) {
+	int retcode = 0, r;
+
+	/* if we fail to send this packet then this flag marks it */
+	/* for evdns_transmit */
+	req->transmit_me = 1;
+	if (req->trans_id == 0xffff) abort();
+
+	if (req->ns->choked) {
+		/* don't bother trying to write to a socket */
+		/* which we have had EAGAIN from */
+		return 1;
+	}
+
+	r = evdns_request_transmit_to(req, req->ns);
+	switch (r) {
+	case 1:
+		/* temp failure */
+		req->ns->choked = 1;
+		nameserver_write_waiting(req->ns, 1);
+		return 1;
+	case 2:
+		/* failed in some other way */
+		retcode = 1;
+		/* fall through */
+	default:
+		/* all ok */
+		log(EVDNS_LOG_DEBUG,
+		    "Setting timeout for request %lx", (unsigned long) req);
+		evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req);
+		if (evtimer_add(&req->timeout_event, &global_timeout) < 0) {
+                  log(EVDNS_LOG_WARN,
+		      "Error from libevent when adding timer for request %lx",
+                      (unsigned long) req);
+                  /* ???? Do more? */
+                }
+		req->tx_count++;
+		req->transmit_me = 0;
+		return retcode;
+	}
+}
+
+static void
+nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
+	struct nameserver *const ns = (struct nameserver *) arg;
+        (void) type;
+        (void) count;
+        (void) ttl;
+        (void) addresses;
+
+	if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
+		/* this is a good reply */
+		nameserver_up(ns);
+	} else nameserver_probe_failed(ns);
+}
+
+static void
+nameserver_send_probe(struct nameserver *const ns) {
+	struct request *req;
+	/* here we need to send a probe to a given nameserver */
+	/* in the hope that it is up now. */
+
+  	log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address));
+
+	req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns);
+        if (!req) return;
+	/* we force this into the inflight queue no matter what */
+	request_trans_id_set(req, transaction_id_pick());
+	req->ns = ns;
+	request_submit(req);
+}
+
+/* returns: */
+/*   0 didn't try to transmit anything */
+/*   1 tried to transmit something */
+static int
+evdns_transmit(void) {
+	char did_try_to_transmit = 0;
+
+	if (req_head) {
+		struct request *const started_at = req_head, *req = req_head;
+		/* first transmit all the requests which are currently waiting */
+		do {
+			if (req->transmit_me) {
+				did_try_to_transmit = 1;
+				evdns_request_transmit(req);
+			}
+
+			req = req->next;
+		} while (req != started_at);
+	}
+
+	return did_try_to_transmit;
+}
+
+/* exported function */
+int
+evdns_count_nameservers(void)
+{
+	const struct nameserver *server = server_head;
+	int n = 0;
+	if (!server)
+		return 0;
+	do {
+		++n;
+		server = server->next;
+	} while (server != server_head);
+	return n;
+}
+
+/* exported function */
+int
+evdns_clear_nameservers_and_suspend(void)
+{
+	struct nameserver *server = server_head, *started_at = server_head;
+	struct request *req = req_head, *req_started_at = req_head;
+
+	if (!server)
+		return 0;
+	while (1) {
+		struct nameserver *next = server->next;
+		(void) event_del(&server->event);
+		(void) evtimer_del(&server->timeout_event);
+		if (server->socket >= 0)
+			CLOSE_SOCKET(server->socket);
+		free(server);
+		if (next == started_at)
+			break;
+		server = next;
+	}
+	server_head = NULL;
+	global_good_nameservers = 0;
+
+	while (req) {
+		struct request *next = req->next;
+		req->tx_count = req->reissue_count = 0;
+		req->ns = NULL;
+		/* ???? What to do about searches? */
+		(void) evtimer_del(&req->timeout_event);
+		req->trans_id = 0;
+		req->transmit_me = 0;
+
+		global_requests_waiting++;
+		evdns_request_insert(req, &req_waiting_head);
+		/* We want to insert these suspended elements at the front of
+		 * the waiting queue, since they were pending before any of
+		 * the waiting entries were added.  This is a circular list,
+		 * so we can just shift the start back by one.*/
+		req_waiting_head = req_waiting_head->prev;
+
+		if (next == req_started_at)
+			break;
+		req = next;
+	}
+	req_head = NULL;
+	global_requests_inflight = 0;
+
+	return 0;
+}
+
+
+/* exported function */
+int
+evdns_resume(void)
+{
+	evdns_requests_pump_waiting_queue();
+	return 0;
+}
+
+static int
+_evdns_nameserver_add_impl(unsigned long int address, int port) {
+	/* first check to see if we already have this nameserver */
+
+	const struct nameserver *server = server_head, *const started_at = server_head;
+	struct nameserver *ns;
+	struct sockaddr_in sin;
+	int err = 0;
+	if (server) {
+		do {
+			if (server->address == address) return 3;
+			server = server->next;
+		} while (server != started_at);
+	}
+
+	ns = (struct nameserver *) malloc(sizeof(struct nameserver));
+        if (!ns) return -1;
+
+	memset(ns, 0, sizeof(struct nameserver));
+
+	ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
+	if (ns->socket < 0) { err = 1; goto out1; }
+#ifdef WIN32
+        {
+		u_long nonblocking = 1;
+		ioctlsocket(ns->socket, FIONBIO, &nonblocking);
+	}
+#else
+        fcntl(ns->socket, F_SETFL, O_NONBLOCK);
+#endif
+	sin.sin_addr.s_addr = address;
+	sin.sin_port = htons(port);
+	sin.sin_family = AF_INET;
+	if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
+		err = 2;
+		goto out2;
+	}
+
+	ns->address = address;
+	ns->state = 1;
+	event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+	if (event_add(&ns->event, NULL) < 0) {
+          err = 2;
+          goto out2;
+        }
+
+	log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address));
+
+	/* insert this nameserver into the list of them */
+	if (!server_head) {
+		ns->next = ns->prev = ns;
+		server_head = ns;
+	} else {
+		ns->next = server_head->next;
+		ns->prev = server_head;
+		server_head->next = ns;
+		if (server_head->prev == server_head) {
+			server_head->prev = ns;
+		}
+	}
+
+	global_good_nameservers++;
+
+	return 0;
+
+out2:
+	CLOSE_SOCKET(ns->socket);
+out1:
+	free(ns);
+	log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err);
+	return err;
+}
+
+/* exported function */
+int
+evdns_nameserver_add(unsigned long int address) {
+	return _evdns_nameserver_add_impl(address, 53);
+}
+
+/* exported function */
+int
+evdns_nameserver_ip_add(const char *ip_as_string) {
+	struct in_addr ina;
+	int port;
+	char buf[20];
+	const char *cp;
+	cp = strchr(ip_as_string, ':');
+	if (! cp) {
+		cp = ip_as_string;
+		port = 53;
+	} else {
+		port = strtoint(cp+1);
+		if (port < 0 || port > 65535) {
+			return 4;
+		}
+		if ((cp-ip_as_string) >= (int)sizeof(buf)) {
+			return 4;
+		}
+		memcpy(buf, ip_as_string, cp-ip_as_string);
+		buf[cp-ip_as_string] = '\0';
+		cp = buf;
+	}
+	if (!inet_aton(cp, &ina)) {
+		return 4;
+	}
+	return _evdns_nameserver_add_impl(ina.s_addr, port);
+}
+
+/* insert into the tail of the queue */
+static void
+evdns_request_insert(struct request *req, struct request **head) {
+	if (!*head) {
+		*head = req;
+		req->next = req->prev = req;
+		return;
+	}
+
+	req->prev = (*head)->prev;
+	req->prev->next = req;
+	req->next = *head;
+	(*head)->prev = req;
+}
+
+static int
+string_num_dots(const char *s) {
+	int count = 0;
+	while ((s = strchr(s, '.'))) {
+		s++;
+		count++;
+	}
+	return count;
+}
+
+static struct request *
+request_new(int type, const char *name, int flags,
+    evdns_callback_type callback, void *user_ptr) {
+	const char issuing_now =
+	    (global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
+
+	const int name_len = strlen(name);
+	const int request_max_len = evdns_request_len(name_len);
+	const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
+	/* the request data is alloced in a single block with the header */
+	struct request *const req =
+	    (struct request *) malloc(sizeof(struct request) + request_max_len);
+	int rlen;
+        (void) flags;
+
+        if (!req) return NULL;
+	memset(req, 0, sizeof(struct request));
+
+	/* request data lives just after the header */
+	req->request = ((u8 *) req) + sizeof(struct request);
+	/* denotes that the request data shouldn't be free()ed */
+	req->request_appended = 1;
+	rlen = evdns_request_data_build(name, name_len, trans_id,
+	    type, CLASS_INET, req->request, request_max_len);
+	if (rlen < 0)
+		goto err1;
+	req->request_len = rlen;
+	req->trans_id = trans_id;
+	req->tx_count = 0;
+	req->request_type = type;
+	req->user_pointer = user_ptr;
+	req->user_callback = callback;
+	req->ns = issuing_now ? nameserver_pick() : NULL;
+	req->next = req->prev = NULL;
+
+	return req;
+err1:
+	free(req);
+	return NULL;
+}
+
+static void
+request_submit(struct request *const req) {
+	if (req->ns) {
+		/* if it has a nameserver assigned then this is going */
+		/* straight into the inflight queue */
+		evdns_request_insert(req, &req_head);
+		global_requests_inflight++;
+		evdns_request_transmit(req);
+	} else {
+		evdns_request_insert(req, &req_waiting_head);
+		global_requests_waiting++;
+	}
+}
+
+/* exported function */
+int evdns_resolve_ipv4(const char *name, int flags,
+    evdns_callback_type callback, void *ptr) {
+	log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
+	if (flags & DNS_QUERY_NO_SEARCH) {
+		struct request *const req =
+			request_new(TYPE_A, name, flags, callback, ptr);
+		if (req == NULL)
+			return (1);
+		request_submit(req);
+		return (0);
+	} else {
+		return (search_request_new(TYPE_A, name, flags, callback, ptr));
+	}
+}
+
+/* exported function */
+int evdns_resolve_ipv6(const char *name, int flags,
+					   evdns_callback_type callback, void *ptr) {
+	log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
+	if (flags & DNS_QUERY_NO_SEARCH) {
+		struct request *const req =
+			request_new(TYPE_AAAA, name, flags, callback, ptr);
+		if (req == NULL)
+			return (1);
+		request_submit(req);
+		return (0);
+	} else {
+		return (search_request_new(TYPE_AAAA, name, flags, callback, ptr));
+	}
+}
+
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) {
+	char buf[32];
+	struct request *req;
+	u32 a;
+	assert(in);
+	a = ntohl(in->s_addr);
+	snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+			(int)(u8)((a	)&0xff),
+			(int)(u8)((a>>8 )&0xff),
+			(int)(u8)((a>>16)&0xff),
+			(int)(u8)((a>>24)&0xff));
+	log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+	req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+	if (!req) return 1;
+	request_submit(req);
+	return 0;
+}
+
+int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) {
+	char buf[64];
+	char *cp;
+	struct request *req;
+	int i;
+	assert(in);
+	cp = buf;
+	for (i=15; i >= 0; --i) {
+		u8 byte = in->s6_addr[i];
+		*cp++ = "0123456789abcdef"[byte & 0x0f];
+		*cp++ = '.';
+		*cp++ = "0123456789abcdef"[byte >> 4];
+		*cp++ = '.';
+	}
+	assert(cp + strlen(".ip6.arpa") < buf+sizeof(buf));
+	memcpy(cp, ".ip6.arpa", strlen(".ip6.arpa")+1);
+	log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+	req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+	if (!req) return 1;
+	request_submit(req);
+	return 0;
+}
+
+/*/////////////////////////////////////////////////////////////////// */
+/* Search support */
+/* */
+/* the libc resolver has support for searching a number of domains */
+/* to find a name. If nothing else then it takes the single domain */
+/* from the gethostname() call. */
+/* */
+/* It can also be configured via the domain and search options in a */
+/* resolv.conf. */
+/* */
+/* The ndots option controls how many dots it takes for the resolver */
+/* to decide that a name is non-local and so try a raw lookup first. */
+
+struct search_domain {
+	int len;
+	struct search_domain *next;
+	/* the text string is appended to this structure */
+};
+
+struct search_state {
+	int refcount;
+	int ndots;
+	int num_domains;
+	struct search_domain *head;
+};
+
+static struct search_state *global_search_state = NULL;
+
+static void
+search_state_decref(struct search_state *const state) {
+	if (!state) return;
+	state->refcount--;
+	if (!state->refcount) {
+		struct search_domain *next, *dom;
+		for (dom = state->head; dom; dom = next) {
+			next = dom->next;
+			free(dom);
+		}
+		free(state);
+	}
+}
+
+static struct search_state *
+search_state_new(void) {
+	struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state));
+        if (!state) return NULL;
+	memset(state, 0, sizeof(struct search_state));
+	state->refcount = 1;
+	state->ndots = 1;
+
+	return state;
+}
+
+static void
+search_postfix_clear(void) {
+	search_state_decref(global_search_state);
+
+	global_search_state = search_state_new();
+}
+
+/* exported function */
+void
+evdns_search_clear(void) {
+	search_postfix_clear();
+}
+
+static void
+search_postfix_add(const char *domain) {
+	int domain_len;
+	struct search_domain *sdomain;
+	while (domain[0] == '.') domain++;
+	domain_len = strlen(domain);
+
+	if (!global_search_state) global_search_state = search_state_new();
+        if (!global_search_state) return;
+	global_search_state->num_domains++;
+
+	sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len);
+        if (!sdomain) return;
+	memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len);
+	sdomain->next = global_search_state->head;
+	sdomain->len = domain_len;
+
+	global_search_state->head = sdomain;
+}
+
+/* reverse the order of members in the postfix list. This is needed because, */
+/* when parsing resolv.conf we push elements in the wrong order */
+static void
+search_reverse(void) {
+	struct search_domain *cur, *prev = NULL, *next;
+	cur = global_search_state->head;
+	while (cur) {
+		next = cur->next;
+		cur->next = prev;
+		prev = cur;
+		cur = next;
+	}
+
+	global_search_state->head = prev;
+}
+
+/* exported function */
+void
+evdns_search_add(const char *domain) {
+	search_postfix_add(domain);
+}
+
+/* exported function */
+void
+evdns_search_ndots_set(const int ndots) {
+	if (!global_search_state) global_search_state = search_state_new();
+        if (!global_search_state) return;
+	global_search_state->ndots = ndots;
+}
+
+static void
+search_set_from_hostname(void) {
+	char hostname[HOST_NAME_MAX + 1], *domainname;
+
+	search_postfix_clear();
+	if (gethostname(hostname, sizeof(hostname))) return;
+	domainname = strchr(hostname, '.');
+	if (!domainname) return;
+	search_postfix_add(domainname);
+}
+
+/* warning: returns malloced string */
+static char *
+search_make_new(const struct search_state *const state, int n, const char *const base_name) {
+	const int base_len = strlen(base_name);
+	const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+	struct search_domain *dom;
+
+	for (dom = state->head; dom; dom = dom->next) {
+		if (!n--) {
+			/* this is the postfix we want */
+			/* the actual postfix string is kept at the end of the structure */
+			const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain);
+			const int postfix_len = dom->len;
+			char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1);
+                        if (!newname) return NULL;
+			memcpy(newname, base_name, base_len);
+			if (need_to_append_dot) newname[base_len] = '.';
+			memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len);
+			newname[base_len + need_to_append_dot + postfix_len] = 0;
+			return newname;
+		}
+	}
+
+	/* we ran off the end of the list and still didn't find the requested string */
+	abort();
+	return NULL; /* unreachable; stops warnings in some compilers. */
+}
+
+static int
+search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) {
+	assert(type == TYPE_A || type == TYPE_AAAA);
+	if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) &&
+	     global_search_state &&
+		 global_search_state->num_domains) {
+		/* we have some domains to search */
+		struct request *req;
+		if (string_num_dots(name) >= global_search_state->ndots) {
+			req = request_new(type, name, flags, user_callback, user_arg);
+			if (!req) return 1;
+			req->search_index = -1;
+		} else {
+			char *const new_name = search_make_new(global_search_state, 0, name);
+                        if (!new_name) return 1;
+			req = request_new(type, new_name, flags, user_callback, user_arg);
+			free(new_name);
+			if (!req) return 1;
+			req->search_index = 0;
+		}
+		req->search_origname = strdup(name);
+		req->search_state = global_search_state;
+		req->search_flags = flags;
+		global_search_state->refcount++;
+		request_submit(req);
+		return 0;
+	} else {
+		struct request *const req = request_new(type, name, flags, user_callback, user_arg);
+		if (!req) return 1;
+		request_submit(req);
+		return 0;
+	}
+}
+
+/* this is called when a request has failed to find a name. We need to check */
+/* if it is part of a search and, if so, try the next name in the list */
+/* returns: */
+/*   0 another request has been submitted */
+/*   1 no more requests needed */
+static int
+search_try_next(struct request *const req) {
+	if (req->search_state) {
+		/* it is part of a search */
+		char *new_name;
+		struct request *newreq;
+		req->search_index++;
+		if (req->search_index >= req->search_state->num_domains) {
+			/* no more postfixes to try, however we may need to try */
+			/* this name without a postfix */
+			if (string_num_dots(req->search_origname) < req->search_state->ndots) {
+				/* yep, we need to try it raw */
+				struct request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer);
+				log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
+				if (newreq) {
+					request_submit(newreq);
+					return 0;
+				}
+			}
+			return 1;
+		}
+
+		new_name = search_make_new(req->search_state, req->search_index, req->search_origname);
+                if (!new_name) return 1;
+		log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index);
+		newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer);
+		free(new_name);
+		if (!newreq) return 1;
+		newreq->search_origname = req->search_origname;
+		req->search_origname = NULL;
+		newreq->search_state = req->search_state;
+		newreq->search_flags = req->search_flags;
+		newreq->search_index = req->search_index;
+		newreq->search_state->refcount++;
+		request_submit(newreq);
+		return 0;
+	}
+	return 1;
+}
+
+static void
+search_request_finished(struct request *const req) {
+	if (req->search_state) {
+		search_state_decref(req->search_state);
+		req->search_state = NULL;
+	}
+	if (req->search_origname) {
+		free(req->search_origname);
+		req->search_origname = NULL;
+	}
+}
+
+/*/////////////////////////////////////////////////////////////////// */
+/* Parsing resolv.conf files */
+
+static void
+evdns_resolv_set_defaults(int flags) {
+	/* if the file isn't found then we assume a local resolver */
+	if (flags & DNS_OPTION_SEARCH) search_set_from_hostname();
+	if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1");
+}
+
+#ifndef HAVE_STRTOK_R
+static char *
+strtok_r(char *s, const char *delim, char **state) {
+	return strtok(s, delim);
+}
+#endif
+
+/* helper version of atoi which returns -1 on error */
+static int
+strtoint(const char *const str) {
+	char *endptr;
+	const int r = strtol(str, &endptr, 10);
+	if (*endptr) return -1;
+	return r;
+}
+
+/* helper version of atoi that returns -1 on error and clips to bounds. */
+static int
+strtoint_clipped(const char *const str, int min, int max)
+{
+	int r = strtoint(str);
+	if (r == -1)
+		return r;
+	else if (r<min)
+		return min;
+	else if (r>max)
+		return max;
+	else
+		return r;
+}
+
+/* exported function */
+int
+evdns_set_option(const char *option, const char *val, int flags)
+{
+	if (!strncmp(option, "ndots:", 6)) {
+		const int ndots = strtoint(val);
+		if (ndots == -1) return -1;
+		if (!(flags & DNS_OPTION_SEARCH)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots);
+		if (!global_search_state) global_search_state = search_state_new();
+		if (!global_search_state) return -1;
+		global_search_state->ndots = ndots;
+	} else if (!strncmp(option, "timeout:", 8)) {
+		const int timeout = strtoint(val);
+		if (timeout == -1) return -1;
+		if (!(flags & DNS_OPTION_MISC)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout);
+		global_timeout.tv_sec = timeout;
+	} else if (!strncmp(option, "max-timeouts:", 12)) {
+		const int maxtimeout = strtoint_clipped(val, 1, 255);
+		if (maxtimeout == -1) return -1;
+		if (!(flags & DNS_OPTION_MISC)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d",
+			maxtimeout);
+		global_max_nameserver_timeout = maxtimeout;
+	} else if (!strncmp(option, "max-inflight:", 13)) {
+		const int maxinflight = strtoint_clipped(val, 1, 65000);
+		if (maxinflight == -1) return -1;
+		if (!(flags & DNS_OPTION_MISC)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d",
+			maxinflight);
+		global_max_requests_inflight = maxinflight;
+	} else if (!strncmp(option, "attempts:", 9)) {
+		int retries = strtoint(val);
+		if (retries == -1) return -1;
+		if (retries > 255) retries = 255;
+		if (!(flags & DNS_OPTION_MISC)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
+		global_max_retransmits = retries;
+	}
+	return 0;
+}
+
+static void
+resolv_conf_parse_line(char *const start, int flags) {
+	char *strtok_state;
+	static const char *const delims = " \t";
+#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
+
+	char *const first_token = strtok_r(start, delims, &strtok_state);
+	if (!first_token) return;
+
+	if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) {
+		const char *const nameserver = NEXT_TOKEN;
+		struct in_addr ina;
+
+		if (inet_aton(nameserver, &ina)) {
+			/* address is valid */
+			evdns_nameserver_add(ina.s_addr);
+		}
+	} else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) {
+		const char *const domain = NEXT_TOKEN;
+		if (domain) {
+			search_postfix_clear();
+			search_postfix_add(domain);
+		}
+	} else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) {
+		const char *domain;
+		search_postfix_clear();
+
+		while ((domain = NEXT_TOKEN)) {
+			search_postfix_add(domain);
+		}
+		search_reverse();
+	} else if (!strcmp(first_token, "options")) {
+		const char *option;
+		while ((option = NEXT_TOKEN)) {
+			const char *val = strchr(option, ':');
+			evdns_set_option(option, val ? val+1 : "", flags);
+		}
+	}
+#undef NEXT_TOKEN
+}
+
+/* exported function */
+/* returns: */
+/*   0 no errors */
+/*   1 failed to open file */
+/*   2 failed to stat file */
+/*   3 file too large */
+/*   4 out of memory */
+/*   5 short read from file */
+int
+evdns_resolv_conf_parse(int flags, const char *const filename) {
+	struct stat st;
+	int fd, n, r;
+	u8 *resolv;
+	char *start;
+	int err = 0;
+
+	log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		evdns_resolv_set_defaults(flags);
+		return 1;
+	}
+
+	if (fstat(fd, &st)) { err = 2; goto out1; }
+	if (!st.st_size) {
+		evdns_resolv_set_defaults(flags);
+		err = (flags & DNS_OPTION_NAMESERVERS) ? 6 : 0;
+		goto out1;
+	}
+	if (st.st_size > 65535) { err = 3; goto out1; }  /* no resolv.conf should be any bigger */
+
+	resolv = (u8 *) malloc((size_t)st.st_size + 1);
+	if (!resolv) { err = 4; goto out1; }
+
+	n = 0;
+	while ((r = read(fd, resolv+n, (size_t)st.st_size-n)) > 0) {
+		n += r;
+		if (n == st.st_size)
+			break;
+		assert(n < st.st_size);
+ 	}
+	if (r < 0) { err = 5; goto out2; }
+	resolv[n] = 0;	 /* we malloced an extra byte; this should be fine. */
+
+	start = (char *) resolv;
+	for (;;) {
+		char *const newline = strchr(start, '\n');
+		if (!newline) {
+			resolv_conf_parse_line(start, flags);
+			break;
+		} else {
+			*newline = 0;
+			resolv_conf_parse_line(start, flags);
+			start = newline + 1;
+		}
+	}
+
+	if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) {
+		/* no nameservers were configured. */
+		evdns_nameserver_ip_add("127.0.0.1");
+		err = 6;
+	}
+	if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) {
+		search_set_from_hostname();
+	}
+
+out2:
+	free(resolv);
+out1:
+	close(fd);
+	return err;
+}
+
+#ifdef WIN32
+/* Add multiple nameservers from a space-or-comma-separated list. */
+static int
+evdns_nameserver_ip_add_line(const char *ips) {
+	const char *addr;
+	char *buf;
+	int r;
+	while (*ips) {
+		while (ISSPACE(*ips) || *ips == ',' || *ips == '\t')
+			++ips;
+		addr = ips;
+		while (ISDIGIT(*ips) || *ips == '.' || *ips == ':')
+			++ips;
+		buf = malloc(ips-addr+1);
+		if (!buf) return 4;
+		memcpy(buf, addr, ips-addr);
+		buf[ips-addr] = '\0';
+		r = evdns_nameserver_ip_add(buf);
+		free(buf);
+		if (r) return r;
+	}
+	return 0;
+}
+
+typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*);
+
+/* Use the windows GetNetworkParams interface in iphlpapi.dll to */
+/* figure out what our nameservers are. */
+static int
+load_nameservers_with_getnetworkparams(void)
+{
+	/* Based on MSDN examples and inspection of  c-ares code. */
+	FIXED_INFO *fixed;
+	HMODULE handle = 0;
+	ULONG size = sizeof(FIXED_INFO);
+	void *buf = NULL;
+	int status = 0, r, added_any;
+	IP_ADDR_STRING *ns;
+	GetNetworkParams_fn_t fn;
+
+	if (!(handle = LoadLibrary("iphlpapi.dll"))) {
+		log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll");
+		status = -1;
+		goto done;
+	}
+	if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) {
+		log(EVDNS_LOG_WARN, "Could not get address of function.");
+		status = -1;
+		goto done;
+	}
+
+	buf = malloc(size);
+	if (!buf) { status = 4; goto done; }
+	fixed = buf;
+	r = fn(fixed, &size);
+	if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) {
+		status = -1;
+		goto done;
+	}
+	if (r != ERROR_SUCCESS) {
+		free(buf);
+		buf = malloc(size);
+		if (!buf) { status = 4; goto done; }
+		fixed = buf;
+		r = fn(fixed, &size);
+		if (r != ERROR_SUCCESS) {
+			log(EVDNS_LOG_DEBUG, "fn() failed.");
+			status = -1;
+			goto done;
+		}
+	}
+
+	assert(fixed);
+	added_any = 0;
+	ns = &(fixed->DnsServerList);
+	while (ns) {
+		r = evdns_nameserver_ip_add_line(ns->IpAddress.String);
+		if (r) {
+			log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d",
+				(ns->IpAddress.String),(int)GetLastError());
+			status = r;
+			goto done;
+		} else {
+			log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String);
+		}
+
+		added_any++;
+		ns = ns->Next;
+	}
+
+	if (!added_any) {
+		log(EVDNS_LOG_DEBUG, "No nameservers added.");
+		status = -1;
+	}
+
+ done:
+	if (buf)
+		free(buf);
+	if (handle)
+		FreeLibrary(handle);
+	return status;
+}
+
+static int
+config_nameserver_from_reg_key(HKEY key, const char *subkey)
+{
+	char *buf;
+	DWORD bufsz = 0, type = 0;
+	int status = 0;
+
+	if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz)
+	    != ERROR_MORE_DATA)
+		return -1;
+	if (!(buf = malloc(bufsz)))
+		return -1;
+
+	if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz)
+	    == ERROR_SUCCESS && bufsz > 1) {
+		status = evdns_nameserver_ip_add_line(buf);
+	}
+
+	free(buf);
+	return status;
+}
+
+#define SERVICES_KEY "System\\CurrentControlSet\\Services\\"
+#define WIN_NS_9X_KEY  SERVICES_KEY "VxD\\MSTCP"
+#define WIN_NS_NT_KEY  SERVICES_KEY "Tcpip\\Parameters"
+
+static int
+load_nameservers_from_registry(void)
+{
+	int found = 0;
+	int r;
+#define TRY(k, name) \
+	if (!found && config_nameserver_from_reg_key(k,name) == 0) {	\
+		log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \
+		found = 1;						\
+	} else if (!found) {						\
+		log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \
+		    #k,#name);						\
+	}
+
+	if (((int)GetVersion()) > 0) { /* NT */
+		HKEY nt_key = 0, interfaces_key = 0;
+
+		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
+				 KEY_READ, &nt_key) != ERROR_SUCCESS) {
+			log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError());
+			return -1;
+		}
+		r = RegOpenKeyEx(nt_key, "Interfaces", 0,
+			     KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
+			     &interfaces_key);
+		if (r != ERROR_SUCCESS) {
+			log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError());
+			return -1;
+		}
+		TRY(nt_key, "NameServer");
+		TRY(nt_key, "DhcpNameServer");
+		TRY(interfaces_key, "NameServer");
+		TRY(interfaces_key, "DhcpNameServer");
+		RegCloseKey(interfaces_key);
+		RegCloseKey(nt_key);
+	} else {
+		HKEY win_key = 0;
+		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0,
+				 KEY_READ, &win_key) != ERROR_SUCCESS) {
+			log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError());
+			return -1;
+		}
+		TRY(win_key, "NameServer");
+		RegCloseKey(win_key);
+	}
+
+	if (found == 0) {
+		log(EVDNS_LOG_WARN,"Didn't find any nameservers.");
+	}
+
+	return found ? 0 : -1;
+#undef TRY
+}
+
+int
+evdns_config_windows_nameservers(void)
+{
+	if (load_nameservers_with_getnetworkparams() == 0)
+		return 0;
+	return load_nameservers_from_registry();
+}
+#endif
+
+int
+evdns_init(void)
+{
+	int res = 0;
+#ifdef WIN32
+	evdns_config_windows_nameservers();
+#else
+	res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+#endif
+
+	return (res);
+}
+
+const char *
+evdns_err_to_string(int err)
+{
+    switch (err) {
+	case DNS_ERR_NONE: return "no error";
+	case DNS_ERR_FORMAT: return "misformatted query";
+	case DNS_ERR_SERVERFAILED: return "server failed";
+	case DNS_ERR_NOTEXIST: return "name does not exist";
+	case DNS_ERR_NOTIMPL: return "query not implemented";
+	case DNS_ERR_REFUSED: return "refused";
+
+	case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed";
+	case DNS_ERR_UNKNOWN: return "unknown";
+	case DNS_ERR_TIMEOUT: return "request timed out";
+	case DNS_ERR_SHUTDOWN: return "dns subsystem shut down";
+	default: return "[Unknown error code]";
+    }
+}
+
+void
+evdns_shutdown(int fail_requests)
+{
+	struct nameserver *server, *server_next;
+	struct search_domain *dom, *dom_next;
+
+	while (req_head) {
+		if (fail_requests)
+			reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL);
+		request_finished(req_head, &req_head);
+	}
+	while (req_waiting_head) {
+		if (fail_requests)
+			reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
+		request_finished(req_waiting_head, &req_waiting_head);
+	}
+	global_requests_inflight = global_requests_waiting = 0;
+
+	for (server = server_head; server; server = server_next) {
+		server_next = server->next;
+		if (server->socket >= 0)
+			CLOSE_SOCKET(server->socket);
+		(void) event_del(&server->event);
+		if (server->state == 0)
+                        (void) event_del(&server->timeout_event);
+		free(server);
+		if (server_next == server_head)
+			break;
+	}
+	server_head = NULL;
+	global_good_nameservers = 0;
+
+	if (global_search_state) {
+		for (dom = global_search_state->head; dom; dom = dom_next) {
+			dom_next = dom->next;
+			free(dom);
+		}
+		free(global_search_state);
+		global_search_state = NULL;
+	}
+	evdns_log_fn = NULL;
+}
+
+#ifdef EVDNS_MAIN
+void
+main_callback(int result, char type, int count, int ttl,
+			  void *addrs, void *orig) {
+	char *n = (char*)orig;
+	int i;
+	for (i = 0; i < count; ++i) {
+		if (type == DNS_IPv4_A) {
+			printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
+		} else if (type == DNS_PTR) {
+			printf("%s: %s\n", n, ((char**)addrs)[i]);
+		}
+	}
+	if (!count) {
+		printf("%s: No answer (%d)\n", n, result);
+	}
+	fflush(stdout);
+}
+void
+evdns_server_callback(struct evdns_server_request *req, void *data)
+{
+	int i, r;
+	(void)data;
+	/* dummy; give 192.168.11.11 as an answer for all A questions,
+	 *	give foo.bar.example.com as an answer for all PTR questions. */
+	for (i = 0; i < req->nquestions; ++i) {
+		u32 ans = htonl(0xc0a80b0bUL);
+		if (req->questions[i]->type == EVDNS_TYPE_A &&
+			req->questions[i]->class == EVDNS_CLASS_INET) {
+			printf(" -- replying for %s (A)\n", req->questions[i]->name);
+			r = evdns_server_request_add_a_reply(req, req->questions[i]->name,
+										  1, &ans, 10);
+			if (r<0)
+				printf("eeep, didn't work.\n");
+		} else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
+				   req->questions[i]->class == EVDNS_CLASS_INET) {
+			printf(" -- replying for %s (PTR)\n", req->questions[i]->name);
+			r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name,
+											"foo.bar.example.com", 10);
+		} else {
+			printf(" -- skipping %s [%d %d]\n", req->questions[i]->name,
+				   req->questions[i]->type, req->questions[i]->class);
+		}
+	}
+
+	r = evdns_request_respond(req, 0);
+	if (r<0)
+		printf("eeek, couldn't send reply.\n");
+}
+
+void
+logfn(int is_warn, const char *msg) {
+  (void) is_warn;
+  fprintf(stderr, "%s\n", msg);
+}
+int
+main(int c, char **v) {
+	int idx;
+	int reverse = 0, verbose = 1, servertest = 0;
+	if (c<2) {
+		fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
+		fprintf(stderr, "syntax: %s [-servertest]\n", v[0]);
+		return 1;
+	}
+	idx = 1;
+	while (idx < c && v[idx][0] == '-') {
+		if (!strcmp(v[idx], "-x"))
+			reverse = 1;
+		else if (!strcmp(v[idx], "-v"))
+			verbose = 1;
+		else if (!strcmp(v[idx], "-servertest"))
+			servertest = 1;
+		else
+			fprintf(stderr, "Unknown option %s\n", v[idx]);
+		++idx;
+	}
+	event_init();
+	if (verbose)
+		evdns_set_log_fn(logfn);
+	evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
+	if (servertest) {
+		int sock;
+		struct sockaddr_in my_addr;
+		sock = socket(PF_INET, SOCK_DGRAM, 0);
+		fcntl(sock, F_SETFL, O_NONBLOCK);
+		my_addr.sin_family = AF_INET;
+		my_addr.sin_port = htons(10053);
+		my_addr.sin_addr.s_addr = INADDR_ANY;
+		if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) {
+			perror("bind");
+			exit(1);
+		}
+		evdns_add_server_port(sock, 0, evdns_server_callback, NULL);
+	}
+	for (; idx < c; ++idx) {
+		if (reverse) {
+			struct in_addr addr;
+			if (!inet_aton(v[idx], &addr)) {
+				fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
+				continue;
+			}
+			fprintf(stderr, "resolving %s...\n",v[idx]);
+			evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
+		} else {
+			fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+			evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
+		}
+	}
+	fflush(stdout);
+	event_dispatch();
+	return 0;
+}
+#endif
diff -Nrup a/extra/libevent/evdns.h b/extra/libevent/evdns.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/evdns.h	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * The original DNS code is due to Adam Langley with heavy
+ * modifications by Nick Mathewson.  Adam put his DNS software in the
+ * public domain.  You can find his original copyright below.  Please,
+ * aware that the code as part of libevent is governed by the 3-clause
+ * BSD license above.
+ *
+ * This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * 	Parts developed by Adam Langley <agl@stripped>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ */
+
+/*
+ * Welcome, gentle reader
+ *
+ * Async DNS lookups are really a whole lot harder than they should be,
+ * mostly stemming from the fact that the libc resolver has never been
+ * very good at them. Before you use this library you should see if libc
+ * can do the job for you with the modern async call getaddrinfo_a
+ * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
+ * please continue.
+ *
+ * This code is based on libevent and you must call event_init before
+ * any of the APIs in this file. You must also seed the OpenSSL random
+ * source if you are using OpenSSL for ids (see below).
+ *
+ * This library is designed to be included and shipped with your source
+ * code. You statically link with it. You should also test for the
+ * existence of strtok_r and define HAVE_STRTOK_R if you have it.
+ *
+ * The DNS protocol requires a good source of id numbers and these
+ * numbers should be unpredictable for spoofing reasons. There are
+ * three methods for generating them here and you must define exactly
+ * one of them. In increasing order of preference:
+ *
+ * DNS_USE_GETTIMEOFDAY_FOR_ID:
+ *   Using the bottom 16 bits of the usec result from gettimeofday. This
+ *   is a pretty poor solution but should work anywhere.
+ * DNS_USE_CPU_CLOCK_FOR_ID:
+ *   Using the bottom 16 bits of the nsec result from the CPU's time
+ *   counter. This is better, but may not work everywhere. Requires
+ *   POSIX realtime support and you'll need to link against -lrt on
+ *   glibc systems at least.
+ * DNS_USE_OPENSSL_FOR_ID:
+ *   Uses the OpenSSL RAND_bytes call to generate the data. You must
+ *   have seeded the pool before making any calls to this library.
+ *
+ * The library keeps track of the state of nameservers and will avoid
+ * them when they go down. Otherwise it will round robin between them.
+ *
+ * Quick start guide:
+ *   #include "evdns.h"
+ *   void callback(int result, char type, int count, int ttl,
+ *		 void *addresses, void *arg);
+ *   evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ *   evdns_resolve("www.hostname.com", 0, callback, NULL);
+ *
+ * When the lookup is complete the callback function is called. The
+ * first argument will be one of the DNS_ERR_* defines in evdns.h.
+ * Hopefully it will be DNS_ERR_NONE, in which case type will be
+ * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
+ * which the data can be cached for (in seconds), addresses will point
+ * to an array of uint32_t's and arg will be whatever you passed to
+ * evdns_resolve.
+ *
+ * Searching:
+ *
+ * In order for this library to be a good replacement for glibc's resolver it
+ * supports searching. This involves setting a list of default domains, in
+ * which names will be queried for. The number of dots in the query name
+ * determines the order in which this list is used.
+ *
+ * Searching appears to be a single lookup from the point of view of the API,
+ * although many DNS queries may be generated from a single call to
+ * evdns_resolve. Searching can also drastically slow down the resolution
+ * of names.
+ *
+ * To disable searching:
+ *   1. Never set it up. If you never call evdns_resolv_conf_parse or
+ *   evdns_search_add then no searching will occur.
+ *
+ *   2. If you do call evdns_resolv_conf_parse then don't pass
+ *   DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
+ *
+ *   3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
+ *
+ * The order of searches depends on the number of dots in the name. If the
+ * number is greater than the ndots setting then the names is first tried
+ * globally. Otherwise each search domain is appended in turn.
+ *
+ * The ndots setting can either be set from a resolv.conf, or by calling
+ * evdns_search_ndots_set.
+ *
+ * For example, with ndots set to 1 (the default) and a search domain list of
+ * ["myhome.net"]:
+ *  Query: www
+ *  Order: www.myhome.net, www.
+ *
+ *  Query: www.abc
+ *  Order: www.abc., www.abc.myhome.net
+ *
+ * API reference:
+ *
+ * int evdns_nameserver_add(unsigned long int address)
+ *   Add a nameserver. The address should be an IP address in
+ *   network byte order. The type of address is chosen so that
+ *   it matches in_addr.s_addr.
+ *   Returns non-zero on error.
+ *
+ * int evdns_nameserver_ip_add(const char *ip_as_string)
+ *   This wraps the above function by parsing a string as an IP
+ *   address and adds it as a nameserver.
+ *   Returns non-zero on error
+ *
+ * int evdns_resolve(const char *name, int flags,
+ *		      evdns_callback_type callback,
+ *		      void *ptr)
+ *   Resolve a name. The name parameter should be a DNS name.
+ *   The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+ *   which disables searching for this query. (see defn of
+ *   searching above).
+ *
+ *   The callback argument is a function which is called when
+ *   this query completes and ptr is an argument which is passed
+ *   to that callback function.
+ *
+ *   Returns non-zero on error
+ *
+ * void evdns_search_clear()
+ *   Clears the list of search domains
+ *
+ * void evdns_search_add(const char *domain)
+ *   Add a domain to the list of search domains
+ *
+ * void evdns_search_ndots_set(int ndots)
+ *   Set the number of dots which, when found in a name, causes
+ *   the first query to be without any search domain.
+ *
+ * int evdns_count_nameservers(void)
+ *   Return the number of configured nameservers (not necessarily the
+ *   number of running nameservers).  This is useful for double-checking
+ *   whether our calls to the various nameserver configuration functions
+ *   have been successful.
+ *
+ * int evdns_clear_nameservers_and_suspend(void)
+ *   Remove all currently configured nameservers, and suspend all pending
+ *   resolves.  Resolves will not necessarily be re-attempted until
+ *   evdns_resume() is called.
+ *
+ * int evdns_resume(void)
+ *   Re-attempt resolves left in limbo after an earlier call to
+ *   evdns_clear_nameservers_and_suspend().
+ *
+ * int evdns_config_windows_nameservers(void)
+ *   Attempt to configure a set of nameservers based on platform settings on
+ *   a win32 host.  Preferentially tries to use GetNetworkParams; if that fails,
+ *   looks in the registry.  Returns 0 on success, nonzero on failure.
+ *
+ * int evdns_resolv_conf_parse(int flags, const char *filename)
+ *   Parse a resolv.conf like file from the given filename.
+ *
+ *   See the man page for resolv.conf for the format of this file.
+ *   The flags argument determines what information is parsed from
+ *   this file:
+ *     DNS_OPTION_SEARCH - domain, search and ndots options
+ *     DNS_OPTION_NAMESERVERS - nameserver lines
+ *     DNS_OPTION_MISC - timeout and attempts options
+ *     DNS_OPTIONS_ALL - all of the above
+ *   The following directives are not parsed from the file:
+ *     sortlist, rotate, no-check-names, inet6, debug
+ *
+ *   Returns non-zero on error:
+ *    0 no errors
+ *    1 failed to open file
+ *    2 failed to stat file
+ *    3 file too large
+ *    4 out of memory
+ *    5 short read from file
+ *    6 no nameservers in file
+ *
+ * Internals:
+ *
+ * Requests are kept in two queues. The first is the inflight queue. In
+ * this queue requests have an allocated transaction id and nameserver.
+ * They will soon be transmitted if they haven't already been.
+ *
+ * The second is the waiting queue. The size of the inflight ring is
+ * limited and all other requests wait in waiting queue for space. This
+ * bounds the number of concurrent requests so that we don't flood the
+ * nameserver. Several algorithms require a full walk of the inflight
+ * queue and so bounding its size keeps thing going nicely under huge
+ * (many thousands of requests) loads.
+ *
+ * If a nameserver loses too many requests it is considered down and we
+ * try not to use it. After a while we send a probe to that nameserver
+ * (a lookup for google.com) and, if it replies, we consider it working
+ * again. If the nameserver fails a probe we wait longer to try again
+ * with the next probe.
+ */
+
+#ifndef EVENTDNS_H
+#define EVENTDNS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Error codes 0-5 are as described in RFC 1035. */
+#define DNS_ERR_NONE 0
+/* The name server was unable to interpret the query */
+#define DNS_ERR_FORMAT 1
+/* The name server was unable to process this query due to a problem with the
+ * name server */
+#define DNS_ERR_SERVERFAILED 2
+/* The domain name does not exist */
+#define DNS_ERR_NOTEXIST 3
+/* The name server does not support the requested kind of query */
+#define DNS_ERR_NOTIMPL 4
+/* The name server refuses to reform the specified operation for policy
+ * reasons */
+#define DNS_ERR_REFUSED 5
+/* The reply was truncated or ill-formated */
+#define DNS_ERR_TRUNCATED 65
+/* An unknown error occurred */
+#define DNS_ERR_UNKNOWN 66
+/* Communication with the server timed out */
+#define DNS_ERR_TIMEOUT 67
+/* The request was canceled because the DNS subsystem was shut down. */
+#define DNS_ERR_SHUTDOWN 68
+
+#define DNS_IPv4_A 1
+#define DNS_PTR 2
+#define DNS_IPv6_AAAA 3
+
+#define DNS_QUERY_NO_SEARCH 1
+
+#define DNS_OPTION_SEARCH 1
+#define DNS_OPTION_NAMESERVERS 2
+#define DNS_OPTION_MISC 4
+#define DNS_OPTIONS_ALL 7
+
+/* 
+ * The callback that contains the results from a lookup.
+ * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA
+ * - count contains the number of addresses of form type
+ * - ttl is the number of seconds the resolution may be cached for.
+ * - addresses needs to be cast according to type
+ */
+typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
+
+int evdns_init(void);
+void evdns_shutdown(int fail_requests);
+const char *evdns_err_to_string(int err);
+int evdns_nameserver_add(unsigned long int address);
+int evdns_count_nameservers(void);
+int evdns_clear_nameservers_and_suspend(void);
+int evdns_resume(void);
+int evdns_nameserver_ip_add(const char *ip_as_string);
+int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
+int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr);
+struct in_addr;
+struct in6_addr;
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
+int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr);
+int evdns_set_option(const char *option, const char *val, int flags);
+int evdns_resolv_conf_parse(int flags, const char *);
+#ifdef MS_WINDOWS
+int evdns_config_windows_nameservers(void);
+#endif
+void evdns_search_clear(void);
+void evdns_search_add(const char *domain);
+void evdns_search_ndots_set(const int ndots);
+
+typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
+void evdns_set_log_fn(evdns_debug_log_fn_type fn);
+
+#define DNS_NO_SEARCH 1
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Structures and functions used to implement a DNS server.
+ */
+
+struct evdns_server_request {
+	int flags;
+	int nquestions;
+	struct evdns_server_question **questions;
+};
+struct evdns_server_question {
+	int type;
+	int class;
+	char name[1];
+};
+typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
+#define EVDNS_ANSWER_SECTION 0
+#define EVDNS_AUTHORITY_SECTION 1
+#define EVDNS_ADDITIONAL_SECTION 2
+
+#define EVDNS_TYPE_A	   1
+#define EVDNS_TYPE_NS	   2
+#define EVDNS_TYPE_CNAME   5
+#define EVDNS_TYPE_SOA	   6
+#define EVDNS_TYPE_PTR	  12
+#define EVDNS_TYPE_MX	  15
+#define EVDNS_TYPE_TXT	  16
+#define EVDNS_TYPE_AAAA	  28
+
+#define EVDNS_QTYPE_AXFR 252
+#define EVDNS_QTYPE_ALL	 255
+
+#define EVDNS_CLASS_INET   1
+
+struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data);
+void evdns_close_server_port(struct evdns_server_port *port);
+
+int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data);
+int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
+int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
+int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl);
+int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl);
+
+int evdns_server_request_respond(struct evdns_server_request *req, int err);
+int evdns_server_request_drop(struct evdns_server_request *req);
+struct sockaddr;
+int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len);
+
+#endif  /* !EVENTDNS_H */
diff -Nrup a/extra/libevent/event-internal.h b/extra/libevent/event-internal.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/event-internal.h	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef _EVENT_INTERNAL_H_
+#define _EVENT_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "evsignal.h"
+
+struct event_base {
+	const struct eventop *evsel;
+	void *evbase;
+	int event_count;		/* counts number of total events */
+	int event_count_active;	/* counts number of active events */
+
+	int event_gotterm;		/* Set to terminate loop */
+
+	/* active event management */
+	struct event_list **activequeues;
+	int nactivequeues;
+
+	/* signal handling info */
+	struct evsignal_info sig;
+
+	struct event_list eventqueue;
+	struct timeval event_tv;
+
+	RB_HEAD(event_tree, event) timetree;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_INTERNAL_H_ */
diff -Nrup a/extra/libevent/event.c b/extra/libevent/event.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/event.c	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,928 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include "misc.h"
+#endif
+#include <sys/types.h>
+#include <sys/tree.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else 
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+
+#ifdef HAVE_EVENT_PORTS
+extern const struct eventop evportops;
+#endif
+#ifdef HAVE_SELECT
+extern const struct eventop selectops;
+#endif
+#ifdef HAVE_POLL
+extern const struct eventop pollops;
+#endif
+#ifdef HAVE_RTSIG
+extern const struct eventop rtsigops;
+#endif
+#ifdef HAVE_EPOLL
+extern const struct eventop epollops;
+#endif
+#ifdef HAVE_WORKING_KQUEUE
+extern const struct eventop kqops;
+#endif
+#ifdef HAVE_DEVPOLL
+extern const struct eventop devpollops;
+#endif
+#ifdef WIN32
+extern const struct eventop win32ops;
+#endif
+
+/* In order of preference */
+const struct eventop *eventops[] = {
+#ifdef HAVE_EVENT_PORTS
+	&evportops,
+#endif
+#ifdef HAVE_WORKING_KQUEUE
+	&kqops,
+#endif
+#ifdef HAVE_EPOLL
+	&epollops,
+#endif
+#ifdef HAVE_DEVPOLL
+	&devpollops,
+#endif
+#ifdef HAVE_RTSIG
+	&rtsigops,
+#endif
+#ifdef HAVE_POLL
+	&pollops,
+#endif
+#ifdef HAVE_SELECT
+	&selectops,
+#endif
+#ifdef WIN32
+	&win32ops,
+#endif
+	NULL
+};
+
+/* Global state */
+struct event_base *current_base = NULL;
+extern struct event_base *evsignal_base;
+static int use_monotonic;
+
+/* Handle signals - This is a deprecated interface */
+int (*event_sigcb)(void);		/* Signal callback when gotsig is set */
+volatile sig_atomic_t event_gotsig;	/* Set in signal handler */
+
+/* Prototypes */
+static void	event_queue_insert(struct event_base *, struct event *, int);
+static void	event_queue_remove(struct event_base *, struct event *, int);
+static int	event_haveevents(struct event_base *);
+
+static void	event_process_active(struct event_base *);
+
+static int	timeout_next(struct event_base *, struct timeval **);
+static void	timeout_process(struct event_base *);
+static void	timeout_correct(struct event_base *, struct timeval *);
+
+static int
+compare(struct event *a, struct event *b)
+{
+	if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
+		return (-1);
+	else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
+		return (1);
+	if (a < b)
+		return (-1);
+	else if (a > b)
+		return (1);
+	return (0);
+}
+
+static void
+detect_monotonic(void)
+{
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+	struct timespec	ts;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+		use_monotonic = 1;
+#endif
+}
+
+static int
+gettime(struct timeval *tp)
+{
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+	struct timespec	ts;
+
+	if (use_monotonic) {
+		if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+			return (-1);
+
+		tp->tv_sec = ts.tv_sec;
+		tp->tv_usec = ts.tv_nsec / 1000;
+		return (0);
+	}
+#endif
+
+	return (gettimeofday(tp, NULL));
+}
+
+RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
+
+RB_GENERATE(event_tree, event, ev_timeout_node, compare);
+
+
+void *
+event_init(void)
+{
+	int i;
+	struct event_base *base;
+
+	if ((base = calloc(1, sizeof(struct event_base))) == NULL)
+		event_err(1, "%s: calloc");
+
+	event_sigcb = NULL;
+	event_gotsig = 0;
+
+	detect_monotonic();
+	gettime(&base->event_tv);
+	
+	RB_INIT(&base->timetree);
+	TAILQ_INIT(&base->eventqueue);
+	TAILQ_INIT(&base->sig.signalqueue);
+	base->sig.ev_signal_pair[0] = -1;
+	base->sig.ev_signal_pair[1] = -1;
+	
+	base->evbase = NULL;
+	for (i = 0; eventops[i] && !base->evbase; i++) {
+		base->evsel = eventops[i];
+
+		base->evbase = base->evsel->init(base);
+	}
+
+	if (base->evbase == NULL)
+		event_errx(1, "%s: no event mechanism available", __func__);
+
+	if (getenv("EVENT_SHOW_METHOD")) 
+		event_msgx("libevent using: %s\n",
+			   base->evsel->name);
+
+	/* allocate a single active event queue */
+	event_base_priority_init(base, 1);
+
+	current_base = base;
+	return (base);
+}
+
+void
+event_base_free(struct event_base *base)
+{
+	int i;
+
+	if (base == NULL && current_base)
+		base = current_base;
+        if (base == current_base)
+		current_base = NULL;
+
+	assert(base);
+	if (base->evsel->dealloc != NULL)
+		base->evsel->dealloc(base, base->evbase);
+	for (i=0; i < base->nactivequeues; ++i)
+		assert(TAILQ_EMPTY(base->activequeues[i]));
+
+	assert(RB_EMPTY(&base->timetree));
+
+	for (i = 0; i < base->nactivequeues; ++i)
+		free(base->activequeues[i]);
+	free(base->activequeues);
+
+	assert(TAILQ_EMPTY(&base->eventqueue));
+
+	free(base);
+}
+
+int
+event_priority_init(int npriorities)
+{
+  return event_base_priority_init(current_base, npriorities);
+}
+
+int
+event_base_priority_init(struct event_base *base, int npriorities)
+{
+	int i;
+
+	if (base->event_count_active)
+		return (-1);
+
+	if (base->nactivequeues && npriorities != base->nactivequeues) {
+		for (i = 0; i < base->nactivequeues; ++i) {
+			free(base->activequeues[i]);
+		}
+		free(base->activequeues);
+	}
+
+	/* Allocate our priority queues */
+	base->nactivequeues = npriorities;
+	base->activequeues = (struct event_list **)calloc(base->nactivequeues,
+	    npriorities * sizeof(struct event_list *));
+	if (base->activequeues == NULL)
+		event_err(1, "%s: calloc", __func__);
+
+	for (i = 0; i < base->nactivequeues; ++i) {
+		base->activequeues[i] = malloc(sizeof(struct event_list));
+		if (base->activequeues[i] == NULL)
+			event_err(1, "%s: malloc", __func__);
+		TAILQ_INIT(base->activequeues[i]);
+	}
+
+	return (0);
+}
+
+int
+event_haveevents(struct event_base *base)
+{
+	return (base->event_count > 0);
+}
+
+/*
+ * Active events are stored in priority queues.  Lower priorities are always
+ * process before higher priorities.  Low priority events can starve high
+ * priority ones.
+ */
+
+static void
+event_process_active(struct event_base *base)
+{
+	struct event *ev;
+	struct event_list *activeq = NULL;
+	int i;
+	short ncalls;
+
+	if (!base->event_count_active)
+		return;
+
+	for (i = 0; i < base->nactivequeues; ++i) {
+		if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
+			activeq = base->activequeues[i];
+			break;
+		}
+	}
+
+	assert(activeq != NULL);
+
+	for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
+		event_queue_remove(base, ev, EVLIST_ACTIVE);
+		
+		/* Allows deletes to work */
+		ncalls = ev->ev_ncalls;
+		ev->ev_pncalls = &ncalls;
+		while (ncalls) {
+			ncalls--;
+			ev->ev_ncalls = ncalls;
+			(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
+			if (event_gotsig)
+				return;
+		}
+	}
+}
+
+/*
+ * Wait continously for events.  We exit only if no events are left.
+ */
+
+int
+event_dispatch(void)
+{
+	return (event_loop(0));
+}
+
+int
+event_base_dispatch(struct event_base *event_base)
+{
+  return (event_base_loop(event_base, 0));
+}
+
+static void
+event_loopexit_cb(int fd, short what, void *arg)
+{
+	struct event_base *base = arg;
+	base->event_gotterm = 1;
+}
+
+/* not thread safe */
+int
+event_loopexit(struct timeval *tv)
+{
+	return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
+		    current_base, tv));
+}
+
+int
+event_base_loopexit(struct event_base *event_base, struct timeval *tv)
+{
+	return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
+		    event_base, tv));
+}
+
+/* not thread safe */
+
+int
+event_loop(int flags)
+{
+	return event_base_loop(current_base, flags);
+}
+
+int
+event_base_loop(struct event_base *base, int flags)
+{
+	const struct eventop *evsel = base->evsel;
+	void *evbase = base->evbase;
+	struct timeval tv;
+	struct timeval *tv_p;
+	int res, done;
+
+#ifndef WIN32
+	if(!TAILQ_EMPTY(&base->sig.signalqueue))
+		evsignal_base = base;
+#endif
+	done = 0;
+	while (!done) {
+		/* Calculate the initial events that we are waiting for */
+		if (evsel->recalc(base, evbase, 0) == -1)
+			return (-1);
+
+		/* Terminate the loop if we have been asked to */
+		if (base->event_gotterm) {
+			base->event_gotterm = 0;
+			break;
+		}
+
+		/* You cannot use this interface for multi-threaded apps */
+		while (event_gotsig) {
+			event_gotsig = 0;
+			if (event_sigcb) {
+				res = (*event_sigcb)();
+				if (res == -1) {
+					errno = EINTR;
+					return (-1);
+				}
+			}
+		}
+
+		timeout_correct(base, &tv);
+
+		tv_p = &tv;
+		if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
+			timeout_next(base, &tv_p);
+		} else {
+			/* 
+			 * if we have active events, we just poll new events
+			 * without waiting.
+			 */
+			timerclear(&tv);
+		}
+		
+		/* If we have no events, we just exit */
+		if (!event_haveevents(base)) {
+			event_debug(("%s: no events registered.", __func__));
+			return (1);
+		}
+
+		res = evsel->dispatch(base, evbase, tv_p);
+
+
+		if (res == -1)
+			return (-1);
+
+		timeout_process(base);
+
+		if (base->event_count_active) {
+			event_process_active(base);
+			if (!base->event_count_active && (flags & EVLOOP_ONCE))
+				done = 1;
+		} else if (flags & EVLOOP_NONBLOCK)
+			done = 1;
+	}
+
+	event_debug(("%s: asked to terminate loop.", __func__));
+	return (0);
+}
+
+/* Sets up an event for processing once */
+
+struct event_once {
+	struct event ev;
+
+	void (*cb)(int, short, void *);
+	void *arg;
+};
+
+/* One-time callback, it deletes itself */
+
+static void
+event_once_cb(int fd, short events, void *arg)
+{
+	struct event_once *eonce = arg;
+
+	(*eonce->cb)(fd, events, eonce->arg);
+	free(eonce);
+}
+
+/* not threadsafe, event scheduled once. */
+int
+event_once(int fd, short events,
+    void (*callback)(int, short, void *), void *arg, struct timeval *tv)
+{
+	return event_base_once(current_base, fd, events, callback, arg, tv);
+}
+
+/* Schedules an event once */
+int
+event_base_once(struct event_base *base, int fd, short events,
+    void (*callback)(int, short, void *), void *arg, struct timeval *tv)
+{
+	struct event_once *eonce;
+	struct timeval etv;
+	int res;
+
+	/* We cannot support signals that just fire once */
+	if (events & EV_SIGNAL)
+		return (-1);
+
+	if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
+		return (-1);
+
+	eonce->cb = callback;
+	eonce->arg = arg;
+
+	if (events == EV_TIMEOUT) {
+		if (tv == NULL) {
+			timerclear(&etv);
+			tv = &etv;
+		}
+
+		evtimer_set(&eonce->ev, event_once_cb, eonce);
+	} else if (events & (EV_READ|EV_WRITE)) {
+		events &= EV_READ|EV_WRITE;
+
+		event_set(&eonce->ev, fd, events, event_once_cb, eonce);
+	} else {
+		/* Bad event combination */
+		free(eonce);
+		return (-1);
+	}
+
+	res = event_base_set(base, &eonce->ev);
+	if (res == 0)
+		res = event_add(&eonce->ev, tv);
+	if (res != 0) {
+		free(eonce);
+		return (res);
+	}
+
+	return (0);
+}
+
+void
+event_set(struct event *ev, int fd, short events,
+	  void (*callback)(int, short, void *), void *arg)
+{
+	/* Take the current base - caller needs to set the real base later */
+	ev->ev_base = current_base;
+
+	ev->ev_callback = callback;
+	ev->ev_arg = arg;
+	ev->ev_fd = fd;
+	ev->ev_events = events;
+	ev->ev_res = 0;
+	ev->ev_flags = EVLIST_INIT;
+	ev->ev_ncalls = 0;
+	ev->ev_pncalls = NULL;
+
+	/* by default, we put new events into the middle priority */
+	if(current_base)
+		ev->ev_pri = current_base->nactivequeues/2;
+}
+
+int
+event_base_set(struct event_base *base, struct event *ev)
+{
+	/* Only innocent events may be assigned to a different base */
+	if (ev->ev_flags != EVLIST_INIT)
+		return (-1);
+
+	ev->ev_base = base;
+	ev->ev_pri = base->nactivequeues/2;
+
+	return (0);
+}
+
+/*
+ * Set's the priority of an event - if an event is already scheduled
+ * changing the priority is going to fail.
+ */
+
+int
+event_priority_set(struct event *ev, int pri)
+{
+	if (ev->ev_flags & EVLIST_ACTIVE)
+		return (-1);
+	if (pri < 0 || pri >= ev->ev_base->nactivequeues)
+		return (-1);
+
+	ev->ev_pri = pri;
+
+	return (0);
+}
+
+/*
+ * Checks if a specific event is pending or scheduled.
+ */
+
+int
+event_pending(struct event *ev, short event, struct timeval *tv)
+{
+	struct timeval	now, res;
+	int flags = 0;
+
+	if (ev->ev_flags & EVLIST_INSERTED)
+		flags |= (ev->ev_events & (EV_READ|EV_WRITE));
+	if (ev->ev_flags & EVLIST_ACTIVE)
+		flags |= ev->ev_res;
+	if (ev->ev_flags & EVLIST_TIMEOUT)
+		flags |= EV_TIMEOUT;
+	if (ev->ev_flags & EVLIST_SIGNAL)
+		flags |= EV_SIGNAL;
+
+	event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
+
+	/* See if there is a timeout that we should report */
+	if (tv != NULL && (flags & event & EV_TIMEOUT)) {
+		gettime(&now);
+		timersub(&ev->ev_timeout, &now, &res);
+		/* correctly remap to real time */
+		gettimeofday(&now, NULL);
+		timeradd(&now, &res, tv);
+	}
+
+	return (flags & event);
+}
+
+int
+event_add(struct event *ev, struct timeval *tv)
+{
+	struct event_base *base = ev->ev_base;
+	const struct eventop *evsel = base->evsel;
+	void *evbase = base->evbase;
+
+	event_debug((
+		 "event_add: event: %p, %s%s%scall %p",
+		 ev,
+		 ev->ev_events & EV_READ ? "EV_READ " : " ",
+		 ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
+		 tv ? "EV_TIMEOUT " : " ",
+		 ev->ev_callback));
+
+	assert(!(ev->ev_flags & ~EVLIST_ALL));
+
+	if (tv != NULL) {
+		struct timeval now;
+
+		if (ev->ev_flags & EVLIST_TIMEOUT)
+			event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+		/* Check if it is active due to a timeout.  Rescheduling
+		 * this timeout before the callback can be executed
+		 * removes it from the active list. */
+		if ((ev->ev_flags & EVLIST_ACTIVE) &&
+		    (ev->ev_res & EV_TIMEOUT)) {
+			/* See if we are just active executing this
+			 * event in a loop
+			 */
+			if (ev->ev_ncalls && ev->ev_pncalls) {
+				/* Abort loop */
+				*ev->ev_pncalls = 0;
+			}
+			
+			event_queue_remove(base, ev, EVLIST_ACTIVE);
+		}
+
+		gettime(&now);
+		timeradd(&now, tv, &ev->ev_timeout);
+
+		event_debug((
+			 "event_add: timeout in %d seconds, call %p",
+			 tv->tv_sec, ev->ev_callback));
+
+		event_queue_insert(base, ev, EVLIST_TIMEOUT);
+	}
+
+	if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
+	    !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
+		event_queue_insert(base, ev, EVLIST_INSERTED);
+
+		return (evsel->add(evbase, ev));
+	} else if ((ev->ev_events & EV_SIGNAL) &&
+	    !(ev->ev_flags & EVLIST_SIGNAL)) {
+		event_queue_insert(base, ev, EVLIST_SIGNAL);
+
+		return (evsel->add(evbase, ev));
+	}
+
+	return (0);
+}
+
+int
+event_del(struct event *ev)
+{
+	struct event_base *base;
+	const struct eventop *evsel;
+	void *evbase;
+
+	event_debug(("event_del: %p, callback %p",
+		 ev, ev->ev_callback));
+
+	/* An event without a base has not been added */
+	if (ev->ev_base == NULL)
+		return (-1);
+
+	base = ev->ev_base;
+	evsel = base->evsel;
+	evbase = base->evbase;
+
+	assert(!(ev->ev_flags & ~EVLIST_ALL));
+
+	/* See if we are just active executing this event in a loop */
+	if (ev->ev_ncalls && ev->ev_pncalls) {
+		/* Abort loop */
+		*ev->ev_pncalls = 0;
+	}
+
+	if (ev->ev_flags & EVLIST_TIMEOUT)
+		event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+	if (ev->ev_flags & EVLIST_ACTIVE)
+		event_queue_remove(base, ev, EVLIST_ACTIVE);
+
+	if (ev->ev_flags & EVLIST_INSERTED) {
+		event_queue_remove(base, ev, EVLIST_INSERTED);
+		return (evsel->del(evbase, ev));
+	} else if (ev->ev_flags & EVLIST_SIGNAL) {
+		event_queue_remove(base, ev, EVLIST_SIGNAL);
+		return (evsel->del(evbase, ev));
+	}
+
+	return (0);
+}
+
+void
+event_active(struct event *ev, int res, short ncalls)
+{
+	/* We get different kinds of events, add them together */
+	if (ev->ev_flags & EVLIST_ACTIVE) {
+		ev->ev_res |= res;
+		return;
+	}
+
+	ev->ev_res = res;
+	ev->ev_ncalls = ncalls;
+	ev->ev_pncalls = NULL;
+	event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
+}
+
+static int
+timeout_next(struct event_base *base, struct timeval **tv_p)
+{
+	struct timeval now;
+	struct event *ev;
+	struct timeval *tv = *tv_p;
+
+	if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
+		/* if no time-based events are active wait for I/O */
+		*tv_p = NULL;
+		return (0);
+	}
+
+	if (gettime(&now) == -1)
+		return (-1);
+
+	if (timercmp(&ev->ev_timeout, &now, <=)) {
+		timerclear(tv);
+		return (0);
+	}
+
+	timersub(&ev->ev_timeout, &now, tv);
+
+	assert(tv->tv_sec >= 0);
+	assert(tv->tv_usec >= 0);
+
+	event_debug(("timeout_next: in %d seconds", tv->tv_sec));
+	return (0);
+}
+
+/*
+ * Determines if the time is running backwards by comparing the current
+ * time against the last time we checked.  Not needed when using clock
+ * monotonic.
+ */
+
+static void
+timeout_correct(struct event_base *base, struct timeval *tv)
+{
+	struct event *ev;
+	struct timeval off;
+
+	if (use_monotonic)
+		return;
+
+	/* Check if time is running backwards */
+	gettime(tv);
+	if (timercmp(tv, &base->event_tv, >=)) {
+		base->event_tv = *tv;
+		return;
+	}
+
+	event_debug(("%s: time is running backwards, corrected",
+		    __func__));
+	timersub(&base->event_tv, tv, &off);
+
+	/*
+	 * We can modify the key element of the node without destroying
+	 * the key, beause we apply it to all in the right order.
+	 */
+	RB_FOREACH(ev, event_tree, &base->timetree)
+		timersub(&ev->ev_timeout, &off, &ev->ev_timeout);
+}
+
+void
+timeout_process(struct event_base *base)
+{
+	struct timeval now;
+	struct event *ev, *next;
+
+	gettime(&now);
+
+	for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
+		if (timercmp(&ev->ev_timeout, &now, >))
+			break;
+		next = RB_NEXT(event_tree, &base->timetree, ev);
+
+		event_queue_remove(base, ev, EVLIST_TIMEOUT);
+
+		/* delete this event from the I/O queues */
+		event_del(ev);
+
+		event_debug(("timeout_process: call %p",
+			 ev->ev_callback));
+		event_active(ev, EV_TIMEOUT, 1);
+	}
+}
+
+void
+event_queue_remove(struct event_base *base, struct event *ev, int queue)
+{
+	int docount = 1;
+
+	if (!(ev->ev_flags & queue))
+		event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
+			   ev, ev->ev_fd, queue);
+
+	if (ev->ev_flags & EVLIST_INTERNAL)
+		docount = 0;
+
+	if (docount)
+		base->event_count--;
+
+	ev->ev_flags &= ~queue;
+	switch (queue) {
+	case EVLIST_ACTIVE:
+		if (docount)
+			base->event_count_active--;
+		TAILQ_REMOVE(base->activequeues[ev->ev_pri],
+		    ev, ev_active_next);
+		break;
+	case EVLIST_SIGNAL:
+		TAILQ_REMOVE(&base->sig.signalqueue, ev, ev_signal_next);
+		break;
+	case EVLIST_TIMEOUT:
+		RB_REMOVE(event_tree, &base->timetree, ev);
+		break;
+	case EVLIST_INSERTED:
+		TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
+		break;
+	default:
+		event_errx(1, "%s: unknown queue %x", __func__, queue);
+	}
+}
+
+void
+event_queue_insert(struct event_base *base, struct event *ev, int queue)
+{
+	int docount = 1;
+
+	if (ev->ev_flags & queue) {
+		/* Double insertion is possible for active events */
+		if (queue & EVLIST_ACTIVE)
+			return;
+
+		event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
+			   ev, ev->ev_fd, queue);
+	}
+
+	if (ev->ev_flags & EVLIST_INTERNAL)
+		docount = 0;
+
+	if (docount)
+		base->event_count++;
+
+	ev->ev_flags |= queue;
+	switch (queue) {
+	case EVLIST_ACTIVE:
+		if (docount)
+			base->event_count_active++;
+		TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
+		    ev,ev_active_next);
+		break;
+	case EVLIST_SIGNAL:
+		TAILQ_INSERT_TAIL(&base->sig.signalqueue, ev, ev_signal_next);
+		break;
+	case EVLIST_TIMEOUT: {
+		struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
+		assert(tmp == NULL);
+		break;
+	}
+	case EVLIST_INSERTED:
+		TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
+		break;
+	default:
+		event_errx(1, "%s: unknown queue %x", __func__, queue);
+	}
+}
+
+/* Functions for debugging */
+
+const char *
+event_get_version(void)
+{
+	return (VERSION);
+}
+
+/* 
+ * No thread-safe interface needed - the information should be the same
+ * for all threads.
+ */
+
+const char *
+event_get_method(void)
+{
+	return (current_base->evsel->name);
+}
diff -Nrup a/extra/libevent/event.h b/extra/libevent/event.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/event.h	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef _EVENT_H_
+#define _EVENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+#endif
+
+#define EVLIST_TIMEOUT	0x01
+#define EVLIST_INSERTED	0x02
+#define EVLIST_SIGNAL	0x04
+#define EVLIST_ACTIVE	0x08
+#define EVLIST_INTERNAL	0x10
+#define EVLIST_INIT	0x80
+
+/* EVLIST_X_ Private space: 0x1000-0xf000 */
+#define EVLIST_ALL	(0xf000 | 0x9f)
+
+#define EV_TIMEOUT	0x01
+#define EV_READ		0x02
+#define EV_WRITE	0x04
+#define EV_SIGNAL	0x08
+#define EV_PERSIST	0x10	/* Persistant event */
+
+/* Fix so that ppl dont have to run with <sys/queue.h> */
+#ifndef TAILQ_ENTRY
+#define _EVENT_DEFINED_TQENTRY
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+#endif /* !TAILQ_ENTRY */
+#ifndef RB_ENTRY
+#define _EVENT_DEFINED_RBENTRY
+#define RB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+#endif /* !RB_ENTRY */
+
+struct event_base;
+struct event {
+	TAILQ_ENTRY (event) ev_next;
+	TAILQ_ENTRY (event) ev_active_next;
+	TAILQ_ENTRY (event) ev_signal_next;
+	RB_ENTRY (event) ev_timeout_node;
+
+	struct event_base *ev_base;
+	int ev_fd;
+	short ev_events;
+	short ev_ncalls;
+	short *ev_pncalls;	/* Allows deletes in callback */
+
+	struct timeval ev_timeout;
+
+	int ev_pri;		/* smaller numbers are higher priority */
+
+	void (*ev_callback)(int, short, void *arg);
+	void *ev_arg;
+
+	int ev_res;		/* result passed to event callback */
+	int ev_flags;
+};
+
+#define EVENT_SIGNAL(ev)	(int)(ev)->ev_fd
+#define EVENT_FD(ev)		(int)(ev)->ev_fd
+
+/*
+ * Key-Value pairs.  Can be used for HTTP headers but also for
+ * query argument parsing.
+ */
+struct evkeyval {
+	TAILQ_ENTRY(evkeyval) next;
+
+	char *key;
+	char *value;
+};
+
+#ifdef _EVENT_DEFINED_TQENTRY
+#undef TAILQ_ENTRY
+struct event_list;
+struct evkeyvalq;
+#undef _EVENT_DEFINED_TQENTRY
+#else
+TAILQ_HEAD (event_list, event);
+TAILQ_HEAD (evkeyvalq, evkeyval);
+#endif /* _EVENT_DEFINED_TQENTRY */
+#ifdef _EVENT_DEFINED_RBENTRY
+#undef RB_ENTRY
+#undef _EVENT_DEFINED_RBENTRY
+#endif /* _EVENT_DEFINED_RBENTRY */
+
+struct eventop {
+	char *name;
+	void *(*init)(struct event_base *);
+	int (*add)(void *, struct event *);
+	int (*del)(void *, struct event *);
+	int (*recalc)(struct event_base *, void *, int);
+	int (*dispatch)(struct event_base *, void *, struct timeval *);
+	void (*dealloc)(struct event_base *, void *);
+};
+
+void *event_init(void);
+int event_dispatch(void);
+int event_base_dispatch(struct event_base *);
+void event_base_free(struct event_base *);
+
+#define _EVENT_LOG_DEBUG 0
+#define _EVENT_LOG_MSG   1
+#define _EVENT_LOG_WARN  2
+#define _EVENT_LOG_ERR   3
+typedef void (*event_log_cb)(int severity, const char *msg);
+void event_set_log_callback(event_log_cb cb);
+
+/* Associate a different event base with an event */
+int event_base_set(struct event_base *, struct event *);
+
+#define EVLOOP_ONCE	0x01
+#define EVLOOP_NONBLOCK	0x02
+int event_loop(int);
+int event_base_loop(struct event_base *, int);
+int event_loopexit(struct timeval *);	/* Causes the loop to exit */
+int event_base_loopexit(struct event_base *, struct timeval *);
+
+#define evtimer_add(ev, tv)		event_add(ev, tv)
+#define evtimer_set(ev, cb, arg)	event_set(ev, -1, 0, cb, arg)
+#define evtimer_del(ev)			event_del(ev)
+#define evtimer_pending(ev, tv)		event_pending(ev, EV_TIMEOUT, tv)
+#define evtimer_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
+
+#define timeout_add(ev, tv)		event_add(ev, tv)
+#define timeout_set(ev, cb, arg)	event_set(ev, -1, 0, cb, arg)
+#define timeout_del(ev)			event_del(ev)
+#define timeout_pending(ev, tv)		event_pending(ev, EV_TIMEOUT, tv)
+#define timeout_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
+
+#define signal_add(ev, tv)		event_add(ev, tv)
+#define signal_set(ev, x, cb, arg)	\
+	event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
+#define signal_del(ev)			event_del(ev)
+#define signal_pending(ev, tv)		event_pending(ev, EV_SIGNAL, tv)
+#define signal_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
+
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
+int event_base_once(struct event_base *, int, short, void (*)(int, short, void *), void *, struct timeval *);
+
+int event_add(struct event *, struct timeval *);
+int event_del(struct event *);
+void event_active(struct event *, int, short);
+
+int event_pending(struct event *, short, struct timeval *);
+
+#ifdef WIN32
+#define event_initialized(ev)		((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != (int)INVALID_HANDLE_VALUE)
+#else
+#define event_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
+#endif
+
+/* Some simple debugging functions */
+const char *event_get_version(void);
+const char *event_get_method(void);
+
+/* These functions deal with event priorities */
+
+int	event_priority_init(int);
+int	event_base_priority_init(struct event_base *, int);
+int	event_priority_set(struct event *, int);
+
+/* These functions deal with buffering input and output */
+
+struct evbuffer {
+	u_char *buffer;
+	u_char *orig_buffer;
+
+	size_t misalign;
+	size_t totallen;
+	size_t off;
+
+	void (*cb)(struct evbuffer *, size_t, size_t, void *);
+	void *cbarg;
+};
+
+/* Just for error reporting - use other constants otherwise */
+#define EVBUFFER_READ		0x01
+#define EVBUFFER_WRITE		0x02
+#define EVBUFFER_EOF		0x10
+#define EVBUFFER_ERROR		0x20
+#define EVBUFFER_TIMEOUT	0x40
+
+struct bufferevent;
+typedef void (*evbuffercb)(struct bufferevent *, void *);
+typedef void (*everrorcb)(struct bufferevent *, short what, void *);
+
+struct event_watermark {
+	size_t low;
+	size_t high;
+};
+
+struct bufferevent {
+	struct event ev_read;
+	struct event ev_write;
+
+	struct evbuffer *input;
+	struct evbuffer *output;
+
+	struct event_watermark wm_read;
+	struct event_watermark wm_write;
+
+	evbuffercb readcb;
+	evbuffercb writecb;
+	everrorcb errorcb;
+	void *cbarg;
+
+	int timeout_read;	/* in seconds */
+	int timeout_write;	/* in seconds */
+
+	short enabled;	/* events that are currently enabled */
+};
+
+struct bufferevent *bufferevent_new(int fd,
+    evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
+int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
+int bufferevent_priority_set(struct bufferevent *bufev, int pri);
+void bufferevent_free(struct bufferevent *bufev);
+int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
+int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
+size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
+int bufferevent_enable(struct bufferevent *bufev, short event);
+int bufferevent_disable(struct bufferevent *bufev, short event);
+void bufferevent_settimeout(struct bufferevent *bufev,
+    int timeout_read, int timeout_write);
+
+#define EVBUFFER_LENGTH(x)	(x)->off
+#define EVBUFFER_DATA(x)	(x)->buffer
+#define EVBUFFER_INPUT(x)	(x)->input
+#define EVBUFFER_OUTPUT(x)	(x)->output
+
+struct evbuffer *evbuffer_new(void);
+void evbuffer_free(struct evbuffer *);
+int evbuffer_expand(struct evbuffer *, size_t);
+int evbuffer_add(struct evbuffer *, const void *, size_t);
+int evbuffer_remove(struct evbuffer *, void *, size_t);
+char *evbuffer_readline(struct evbuffer *);
+int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
+int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
+int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
+void evbuffer_drain(struct evbuffer *, size_t);
+int evbuffer_write(struct evbuffer *, int);
+int evbuffer_read(struct evbuffer *, int, int);
+u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
+void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
+
+/* 
+ * Marshaling tagged data - We assume that all tags are inserted in their
+ * numeric order - so that unknown tags will always be higher than the
+ * known ones - and we can just ignore the end of an event buffer.
+ */
+
+void evtag_init(void);
+
+void evtag_marshal(struct evbuffer *evbuf, uint8_t tag, const void *data,
+    uint32_t len);
+
+void encode_int(struct evbuffer *evbuf, uint32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, uint8_t tag,
+    const char *string);
+
+void evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag,
+    struct timeval *tv);
+
+void evtag_test(void);
+
+int evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, uint8_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength);
+int evtag_consume(struct evbuffer *evbuf);
+
+int evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag,
+    uint32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data,
+    size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag,
+    char **pstring);
+
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag,
+    struct timeval *ptv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_H_ */
diff -Nrup a/extra/libevent/event_tagging.c b/extra/libevent/event_tagging.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/event_tagging.c	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2003, 2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#else
+#include <sys/ioctl.h>
+#endif
+
+#include <sys/tree.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#endif
+#include <unistd.h>
+
+#include "event.h"
+#include "log.h"
+
+int decode_int(uint32_t *pnumber, struct evbuffer *evbuf);
+
+static struct evbuffer *_buf;	/* not thread safe */
+
+void
+evtag_init()
+{
+	if (_buf != NULL)
+		return;
+
+	if ((_buf = evbuffer_new()) == NULL)
+		event_err(1, "%s: malloc", __func__);
+}
+
+/* 
+ * We encode integer's by nibbles; the first nibble contains the number
+ * of significant nibbles - 1;  this allows us to encode up to 64-bit
+ * integers.  This function is byte-order independent.
+ */
+
+void
+encode_int(struct evbuffer *evbuf, uint32_t number)
+{
+	int off = 1, nibbles = 0;
+	uint8_t data[5];
+
+	memset(data, 0, sizeof(data));
+	while (number) {
+		if (off & 0x1)
+			data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f);
+		else
+			data[off/2] = (data[off/2] & 0x0f) |
+			    ((number & 0x0f) << 4);
+		number >>= 4;
+		off++;
+	}
+
+	if (off > 2)
+		nibbles = off - 2;
+
+	/* Off - 1 is the number of encoded nibbles */
+	data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4);
+
+	evbuffer_add(evbuf, data, (off + 1) / 2);
+}
+
+/*
+ * Marshal a data type, the general format is as follows:
+ *
+ * tag number: one byte; length: var bytes; payload: var bytes
+ */
+
+void
+evtag_marshal(struct evbuffer *evbuf, uint8_t tag,
+    const void *data, uint32_t len)
+{
+	evbuffer_add(evbuf, &tag, sizeof(tag));
+	encode_int(evbuf, len);
+	evbuffer_add(evbuf, (void *)data, len);
+}
+
+/* Marshaling for integers */
+void
+evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer)
+{
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+	encode_int(_buf, integer);
+
+	evbuffer_add(evbuf, &tag, sizeof(tag));
+	encode_int(evbuf, EVBUFFER_LENGTH(_buf));
+	evbuffer_add_buffer(evbuf, _buf);
+}
+
+void
+evtag_marshal_string(struct evbuffer *buf, uint8_t tag, const char *string)
+{
+	evtag_marshal(buf, tag, string, strlen(string));
+}
+
+void
+evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag, struct timeval *tv)
+{
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+	encode_int(_buf, tv->tv_sec);
+	encode_int(_buf, tv->tv_usec);
+
+	evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf),
+	    EVBUFFER_LENGTH(_buf));
+}
+
+static int
+decode_int_internal(uint32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+{
+	uint32_t number = 0;
+	uint8_t *data = EVBUFFER_DATA(evbuf);
+	int len = EVBUFFER_LENGTH(evbuf);
+	int nibbles = 0, off;
+
+	if (!len)
+		return (-1);
+
+	nibbles = ((data[0] & 0xf0) >> 4) + 1;
+	if (nibbles > 8 || (nibbles >> 1) > len - 1)
+		return (-1);
+
+	off = nibbles;
+	while (off > 0) {
+		number <<= 4;
+		if (off & 0x1)
+			number |= data[off >> 1] & 0x0f;
+		else
+			number |= (data[off >> 1] & 0xf0) >> 4;
+		off--;
+	}
+
+	len = (nibbles >> 1) + 1;
+	if (dodrain)
+		evbuffer_drain(evbuf, len);
+
+	*pnumber = number;
+
+	return (len);
+}
+
+int
+decode_int(uint32_t *pnumber, struct evbuffer *evbuf)
+{
+	return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
+}
+
+int
+evtag_peek(struct evbuffer *evbuf, uint8_t *ptag)
+{
+	if (EVBUFFER_LENGTH(evbuf) < 2)
+		return (-1);
+	*ptag = EVBUFFER_DATA(evbuf)[0];
+
+	return (0);
+}
+
+int
+evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength)
+{
+	struct evbuffer tmp;
+	int res;
+
+	if (EVBUFFER_LENGTH(evbuf) < 2)
+		return (-1);
+
+	tmp = *evbuf;
+	tmp.buffer += 1;
+	tmp.off -= 1;
+
+	res = decode_int_internal(plength, &tmp, 0);
+	if (res == -1)
+		return (-1);
+
+	*plength += res + 1;
+
+	return (0);
+}
+
+int
+evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength)
+{
+	struct evbuffer tmp;
+	int res;
+
+	if (EVBUFFER_LENGTH(evbuf) < 2)
+		return (-1);
+
+	tmp = *evbuf;
+	tmp.buffer += 1;
+	tmp.off -= 1;
+
+	res = decode_int_internal(plength, &tmp, 0);
+	if (res == -1)
+		return (-1);
+
+	return (0);
+}
+
+int
+evtag_consume(struct evbuffer *evbuf)
+{
+	uint32_t len;
+	evbuffer_drain(evbuf, 1);
+	if (decode_int(&len, evbuf) == -1)
+		return (-1);
+	evbuffer_drain(evbuf, len);
+
+	return (0);
+}
+
+/* Reads the data type from an event buffer */
+
+int
+evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst)
+{
+	uint8_t tag;
+	uint32_t len;
+	uint32_t integer;
+
+	if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag))
+		return (-1);
+	if (decode_int(&integer, src) == -1)
+		return (-1);
+	len = integer;
+
+	if (EVBUFFER_LENGTH(src) < len)
+		return (-1);
+
+	if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1)
+		return (-1);
+
+	evbuffer_drain(src, len);
+
+	*ptag = tag;
+	return (len);
+}
+
+/* Marshaling for integers */
+
+int
+evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag,
+    uint32_t *pinteger)
+{
+	uint8_t tag;
+	uint32_t len;
+	uint32_t integer;
+
+	if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) ||
+	    tag != need_tag)
+		return (-1);
+	if (decode_int(&integer, evbuf) == -1)
+		return (-1);
+	len = integer;
+
+	if (EVBUFFER_LENGTH(evbuf) < len)
+		return (-1);
+	
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+	if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1)
+		return (-1);
+
+	evbuffer_drain(evbuf, len);
+
+	return (decode_int(pinteger, _buf));
+}
+
+/* Unmarshal a fixed length tag */
+
+int
+evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data,
+    size_t len)
+{
+	uint8_t tag;
+
+	/* Initialize this event buffer so that we can read into it */
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+	/* Now unmarshal a tag and check that it matches the tag we want */
+	if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag)
+		return (-1);
+
+	if (EVBUFFER_LENGTH(_buf) != len)
+		return (-1);
+
+	memcpy(data, EVBUFFER_DATA(_buf), len);
+	return (0);
+}
+
+int
+evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag,
+    char **pstring)
+{
+	uint8_t tag;
+
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+	if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+		return (-1);
+
+	*pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1);
+	if (*pstring == NULL)
+		event_err(1, "%s: calloc", __func__);
+	evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf));
+
+	return (0);
+}
+
+int
+evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag,
+    struct timeval *ptv)
+{
+	uint8_t tag;
+	uint32_t integer;
+
+	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+	if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+		return (-1);
+
+	if (decode_int(&integer, _buf) == -1)
+		return (-1);
+	ptv->tv_sec = integer;
+	if (decode_int(&integer, _buf) == -1)
+		return (-1);
+	ptv->tv_usec = integer;
+
+	return (0);
+}
diff -Nrup a/extra/libevent/evhttp.h b/extra/libevent/evhttp.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/evhttp.h	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef _EVHTTP_H_
+#define _EVHTTP_H_
+
+#include <event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+/*
+ * Basic support for HTTP serving.
+ *
+ * As libevent is a library for dealing with event notification and most
+ * interesting applications are networked today, I have often found the
+ * need to write HTTP code.  The following prototypes and definitions provide
+ * an application with a minimal interface for making HTTP requests and for
+ * creating a very simple HTTP server.
+ */
+
+/* Response codes */
+#define HTTP_OK			200
+#define HTTP_NOCONTENT		204
+#define HTTP_MOVEPERM		301
+#define HTTP_MOVETEMP		302
+#define HTTP_NOTMODIFIED	304
+#define HTTP_BADREQUEST		400
+#define HTTP_NOTFOUND		404
+#define HTTP_SERVUNAVAIL	503
+
+struct evhttp;
+struct evhttp_request;
+struct evkeyvalq;
+
+/* Start an HTTP server on the specified address and port */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * Free the previously create HTTP server.  Works only if no requests are
+ * currently being served.
+ */
+void evhttp_free(struct evhttp* http);
+
+/* Set a callback for a specified URI */
+void evhttp_set_cb(struct evhttp *, const char *,
+    void (*)(struct evhttp_request *, void *), void *);
+
+/* Removes the callback for a specified URI */
+int evhttp_del_cb(struct evhttp *, const char *);
+
+/* Set a callback for all requests that are not caught by specific callbacks */
+void evhttp_set_gencb(struct evhttp *,
+    void (*)(struct evhttp_request *, void *), void *);
+
+void evhttp_set_timeout(struct evhttp *, int timeout_in_secs);
+
+/* Request/Response functionality */
+
+void evhttp_send_error(struct evhttp_request *, int, const char *);
+void evhttp_send_reply(struct evhttp_request *, int, const char *,
+    struct evbuffer *);
+
+/* Low-level response interface, for streaming/chunked replies */
+void evhttp_send_reply_start(struct evhttp_request *, int, const char *);
+void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *);
+void evhttp_send_reply_end(struct evhttp_request *);
+	
+/* Interfaces for making requests */
+enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
+
+enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
+
+/* 
+ * the request structure that a server receives.
+ * WARNING: expect this structure to change.  I will try to provide
+ * reasonable accessors.
+ */
+struct evhttp_request {
+	TAILQ_ENTRY(evhttp_request) next;
+
+	/* the connection object that this request belongs to */
+	struct evhttp_connection *evcon;
+	int flags;
+#define EVHTTP_REQ_OWN_CONNECTION	0x0001	
+#define EVHTTP_PROXY_REQUEST		0x0002
+	
+	struct evkeyvalq *input_headers;
+	struct evkeyvalq *output_headers;
+
+	/* address of the remote host and the port connection came from */
+	char *remote_host;
+	u_short remote_port;
+
+	enum evhttp_request_kind kind;
+	enum evhttp_cmd_type type;
+
+	char *uri;			/* uri after HTTP request was parsed */
+
+	char major;			/* HTTP Major number */
+	char minor;			/* HTTP Minor number */
+	
+	int got_firstline;
+	int response_code;		/* HTTP Response code */
+	char *response_code_line;	/* Readable response */
+
+	struct evbuffer *input_buffer;	/* read data */
+	int ntoread;
+	int chunked;
+
+	struct evbuffer *output_buffer;	/* outgoing post or data */
+
+	/* Callback */
+	void (*cb)(struct evhttp_request *, void *);
+	void *cb_arg;
+
+	/* 
+	 * Chunked data callback - call for each completed chunk if
+	 * specified.  If not specified, all the data is delivered via
+	 * the regular callback.
+	 */
+	void (*chunk_cb)(struct evhttp_request *, void *);
+};
+
+/* 
+ * Creates a new request object that needs to be filled in with the request
+ * parameters.  The callback is executed when the request completed or an
+ * error occurred.
+ */
+struct evhttp_request *evhttp_request_new(
+	void (*cb)(struct evhttp_request *, void *), void *arg);
+
+/* enable delivery of chunks to requestor */
+void evhttp_request_set_chunked_cb(struct evhttp_request *,
+    void (*cb)(struct evhttp_request *, void *));
+
+/* Frees the request object and removes associated events. */
+void evhttp_request_free(struct evhttp_request *req);
+
+/*
+ * A connection object that can be used to for making HTTP requests.  The
+ * connection object tries to establish the connection when it is given an
+ * http request object.
+ */
+struct evhttp_connection *evhttp_connection_new(
+	const char *address, unsigned short port);
+
+/* Frees an http connection */
+void evhttp_connection_free(struct evhttp_connection *evcon);
+
+/* Sets the timeout for events related to this connection */
+void evhttp_connection_set_timeout(struct evhttp_connection *evcon,
+    int timeout_in_secs);
+
+/* Sets the retry limit for this connection - -1 repeats indefnitely */
+void evhttp_connection_set_retries(struct evhttp_connection *evcon,
+    int retry_max);
+
+/* Set a callback for connection close. */
+void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
+    void (*)(struct evhttp_connection *, void *), void *);
+
+/* Get the remote address and port associated with this connection. */
+void evhttp_connection_get_peer(struct evhttp_connection *evcon,
+    char **address, u_short *port);
+
+/* The connection gets ownership of the request */
+int evhttp_make_request(struct evhttp_connection *evcon,
+    struct evhttp_request *req,
+    enum evhttp_cmd_type type, const char *uri);
+
+const char *evhttp_request_uri(struct evhttp_request *req);
+
+/* Interfaces for dealing with HTTP headers */
+
+const char *evhttp_find_header(const struct evkeyvalq *, const char *);
+int evhttp_remove_header(struct evkeyvalq *, const char *);
+int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
+void evhttp_clear_headers(struct evkeyvalq *);
+
+/* Miscellaneous utility functions */
+char *evhttp_encode_uri(const char *uri);
+char *evhttp_decode_uri(const char *uri);
+void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+char *evhttp_htmlescape(const char *html);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVHTTP_H_ */
diff -Nrup a/extra/libevent/evport.c b/extra/libevent/evport.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/extra/libevent/evport.c	2007-10-31 15:36:51 -04:00
@@ -0,0 +1,531 @@
+/*
+ * Submitted by David Pacheco (dp.spambait@stripped)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, T