summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
Diffstat (limited to 'extra')
-rw-r--r--extra/CMakeLists.txt3
-rw-r--r--extra/Makefile.am10
-rw-r--r--extra/comp_err.c3
-rw-r--r--extra/libevent/CMakeLists.txt35
-rw-r--r--extra/libevent/Makefile.am39
-rw-r--r--extra/libevent/README57
-rw-r--r--extra/libevent/WIN32-Code/config.h247
-rw-r--r--extra/libevent/WIN32-Code/misc.c38
-rw-r--r--extra/libevent/WIN32-Code/misc.h11
-rw-r--r--extra/libevent/WIN32-Code/tree.h1354
-rw-r--r--extra/libevent/WIN32-Code/win32.c471
-rw-r--r--extra/libevent/buffer.c455
-rw-r--r--extra/libevent/compat/sys/_time.h163
-rw-r--r--extra/libevent/compat/sys/queue.h488
-rw-r--r--extra/libevent/compat/sys/tree.h677
-rw-r--r--extra/libevent/devpoll.c421
-rw-r--r--extra/libevent/epoll.c360
-rw-r--r--extra/libevent/epoll_sub.c60
-rw-r--r--extra/libevent/evbuffer.c418
-rw-r--r--extra/libevent/evdns.c3150
-rw-r--r--extra/libevent/evdns.h528
-rw-r--r--extra/libevent/event-internal.h103
-rw-r--r--extra/libevent/event.c989
-rw-r--r--extra/libevent/event.h1129
-rw-r--r--extra/libevent/event_tagging.c443
-rw-r--r--extra/libevent/evhttp.h340
-rw-r--r--extra/libevent/evport.c517
-rw-r--r--extra/libevent/evrpc-internal.h87
-rw-r--r--extra/libevent/evrpc.c658
-rw-r--r--extra/libevent/evrpc.h477
-rw-r--r--extra/libevent/evsignal.h52
-rw-r--r--extra/libevent/evutil.c198
-rw-r--r--extra/libevent/evutil.h174
-rw-r--r--extra/libevent/http-internal.h133
-rw-r--r--extra/libevent/http.c2512
-rw-r--r--extra/libevent/kqueue.c425
-rw-r--r--extra/libevent/log.c218
-rw-r--r--extra/libevent/log.h51
-rw-r--r--extra/libevent/min_heap.h138
-rw-r--r--extra/libevent/poll.c378
-rw-r--r--extra/libevent/select.c356
-rw-r--r--extra/libevent/signal.c304
-rw-r--r--extra/libevent/strlcpy-internal.h23
-rw-r--r--extra/libevent/strlcpy.c76
-rw-r--r--extra/my_print_defaults.c10
-rw-r--r--extra/replace.c1
-rw-r--r--extra/yassl/src/crypto_wrapper.cpp7
-rw-r--r--extra/yassl/src/yassl_error.cpp2
-rw-r--r--extra/yassl/src/yassl_imp.cpp3
-rw-r--r--extra/yassl/taocrypt/include/block.hpp3
-rw-r--r--extra/yassl/taocrypt/src/aes.cpp3
-rw-r--r--extra/yassl/taocrypt/src/algebra.cpp1
-rw-r--r--extra/yassl/taocrypt/src/blowfish.cpp3
-rw-r--r--extra/yassl/taocrypt/src/misc.cpp9
-rw-r--r--extra/yassl/taocrypt/src/twofish.cpp3
55 files changed, 18792 insertions, 22 deletions
diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt
index a569641a198..ef2a8bedcbb 100644
--- a/extra/CMakeLists.txt
+++ b/extra/CMakeLists.txt
@@ -66,7 +66,6 @@ IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
TARGET_LINK_LIBRARIES(resolveip ${SOLARIS_NSL})
ENDIF()
-
MYSQL_ADD_EXECUTABLE(replace replace.c)
TARGET_LINK_LIBRARIES(replace mysys)
IF(UNIX)
@@ -79,3 +78,5 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(mysql_waitpid mysys)
ENDIF()
+MESSAGE("TODO: Support libevent")
+
diff --git a/extra/Makefile.am b/extra/Makefile.am
index ff62749ac4c..0b829f6ad33 100644
--- a/extra/Makefile.am
+++ b/extra/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (C) 2000-2006 MySQL AB
+# Copyright (C) 2000-2006 MySQL AB, 2009 Monty Program AB
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,9 +22,10 @@ BUILT_SOURCES= $(top_builddir)/include/mysqld_error.h \
$(top_builddir)/include/sql_state.h \
$(top_builddir)/include/mysqld_ername.h
pkginclude_HEADERS= $(BUILT_SOURCES)
-DISTCLEANFILES = $(BUILT_SOURCES)
-SUBDIRS = @yassl_dir@
-DIST_SUBDIRS = yassl
+EXTRA_PROGRAMS = comp_err
+DISTCLEANFILES = $(BUILT_SOURCES) $(EXTRA_PROGRAMS)
+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
@@ -50,7 +51,6 @@ bin_PROGRAMS += innochecksum
endif
noinst_PROGRAMS = charset2html
-EXTRA_PROGRAMS = comp_err
EXTRA_DIST = CMakeLists.txt
perror.o: perror.c
diff --git a/extra/comp_err.c b/extra/comp_err.c
index 4c40e70f5b7..50dbba97c1b 100644
--- a/extra/comp_err.c
+++ b/extra/comp_err.c
@@ -108,7 +108,8 @@ static struct my_option my_long_options[]=
NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Prints version", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
- {"charset", 'C', "Charset dir", &charsets_dir, &charsets_dir,
+ {"charset", 'C', "Charset dir",
+ (char**) &charsets_dir, (char**) &charsets_dir,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"in_file", 'F', "Input file", &TXTFILE, &TXTFILE,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
diff --git a/extra/libevent/CMakeLists.txt b/extra/libevent/CMakeLists.txt
new file mode 100644
index 00000000000..83a6cf0f220
--- /dev/null
+++ b/extra/libevent/CMakeLists.txt
@@ -0,0 +1,35 @@
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/extra/libevent
+ ${CMAKE_SOURCE_DIR}/extra/libevent/compat
+ ${CMAKE_SOURCE_DIR}/extra/libevent/WIN32-Code
+ ${CMAKE_SOURCE_DIR}/include)
+
+IF(MSVC)
+ ADD_DEFINITIONS("-DWIN32 -DHAVE_CONFIG_H")
+ENDIF(MSVC)
+
+SET(LIBEVENT_SOURCES
+ buffer.c
+ evbuffer.c
+ event.c
+ evutil.c
+ log.c
+ signal.c
+ strlcpy.c
+ WIN32-Code/win32.c
+ WIN32-Code/config.h
+ WIN32-Code/misc.c
+ WIN32-Code/misc.h
+ event-internal.h
+ event.h
+ evsignal.h
+ evutil.h
+ log.h
+ min_heap.h
+ strlcpy-internal.h
+ )
+
+CONFIGURE_FILE(WIN32-Code/config.h ${CMAKE_SOURCE_DIR}/extra/libevent/event-config.h COPYONLY)
+IF(NOT SOURCE_SUBLIBS)
+ ADD_LIBRARY(libevent ${LIBEVENT_SOURCES})
+ENDIF(NOT SOURCE_SUBLIBS)
diff --git a/extra/libevent/Makefile.am b/extra/libevent/Makefile.am
new file mode 100644
index 00000000000..1c9066c3122
--- /dev/null
+++ b/extra/libevent/Makefile.am
@@ -0,0 +1,39 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+EXTRA_DIST = README compat/sys \
+ buffer.c epoll.c evbuffer.c event.c evport.c evutil.c kqueue.c poll.c \
+ signal.c devpoll.c epoll_sub.c evdns.c event_tagging.c evrpc.c http.c \
+ log.c select.c strlcpy.c \
+ evdns.h event.h evrpc-internal.h evsignal.h http-internal.h log.h \
+ min_heap.h event-internal.h evhttp.h evrpc.h evutil.h strlcpy-internal.h \
+ WIN32-Code/misc.c WIN32-Code/misc.h WIN32-Code/config.h WIN32-Code/tree.h \
+ WIN32-Code/win32.c CMakeLists.txt
+
+
+DISTCLEANFILES = event-config.h
+
+noinst_LIBRARIES = libevent.a
+
+libevent_a_SOURCES = event.c buffer.c evbuffer.c log.c evutil.c \
+ select.c poll.c epoll.c epoll_sub.c devpoll.c kqueue.c \
+ evport.c signal.c
+
+include_HEADERS = event.h evutil.h event-config.h
+
+BUILT_SOURCES = event-config.h
+
+event-config.h: $(top_builddir)/include/config.h
+ echo '/* event-config.h' > $@
+ echo ' * Generated by autoconf; post-processed by libevent.' >> $@
+ echo ' * Do not edit this file.' >> $@
+ echo ' * Do not rely on macros in this file existing in later versions.'>> $@
+ echo ' */' >> $@
+ echo '#ifndef _EVENT_CONFIG_H_' >> $@
+ echo '#define _EVENT_CONFIG_H_' >> $@
+
+ sed -e 's/#define /#define _EVENT_/' \
+ -e 's/#undef /#undef _EVENT_/' \
+ -e 's/#ifndef /#ifndef _EVENT_/' < $(top_builddir)/include/config.h >> $@
+ echo "#endif" >> $@
+
+AM_CPPFLAGS = -I$(srcdir)/compat -I$(top_srcdir)/include
diff --git a/extra/libevent/README b/extra/libevent/README
new file mode 100644
index 00000000000..b0650392ed4
--- /dev/null
+++ b/extra/libevent/README
@@ -0,0 +1,57 @@
+To build libevent, type
+
+$ ./configure && make
+
+ (If you got libevent from the subversion repository, you will
+ first need to run the included "autogen.sh" script in order to
+ generate the configure script.)
+
+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 --git a/extra/libevent/WIN32-Code/config.h b/extra/libevent/WIN32-Code/config.h
new file mode 100644
index 00000000000..99365edd96a
--- /dev/null
+++ b/extra/libevent/WIN32-Code/config.h
@@ -0,0 +1,247 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.in by autoheader. */
+
+/* Define if clock_gettime is available in libc */
+/* #undef DNS_USE_CPU_CLOCK_FOR_ID */
+
+/* Define if no secure id variant is available */
+#define DNS_USE_FTIME_FOR_ID 1
+
+/* Define if no secure id variant is available */
+/* #define DNS_USE_GETTIMEOFDAY_FOR_ID 1 */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+/* #undef HAVE_CLOCK_GETTIME */
+
+/* Define if /dev/poll is available */
+/* #undef HAVE_DEVPOLL */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define if your system supports the epoll system calls */
+/* #undef HAVE_EPOLL */
+
+/* Define to 1 if you have the `epoll_ctl' function. */
+/* #undef HAVE_EPOLL_CTL */
+
+/* Define if your system supports event ports */
+/* #undef HAVE_EVENT_PORTS */
+
+/* Define to 1 if you have the `fcntl' function. */
+/* #undef HAVE_FCNTL */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getaddrinfo' function. */
+/* #undef HAVE_GETADDRINFO */
+
+/* Define to 1 if you have the `getnameinfo' function. */
+/* #undef HAVE_GETNAMEINFO */
+
+/* Define to 1 if you have the `gettimeofday' function. */
+/* #define HAVE_GETTIMEOFDAY 1 */
+
+/* Define to 1 if you have the `inet_ntop' function. */
+/* #undef HAVE_INET_NTOP */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H 1 */
+
+/* Define to 1 if you have the `kqueue' function. */
+/* #undef HAVE_KQUEUE */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+/* #undef HAVE_LIBRESOLV */
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netinet/in6.h> header file. */
+/* #undef HAVE_NETINET_IN6_H */
+
+/* Define to 1 if you have the `poll' function. */
+/* #undef HAVE_POLL */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Define to 1 if you have the `port_create' function. */
+/* #undef HAVE_PORT_CREATE */
+
+/* Define to 1 if you have the <port.h> header file. */
+/* #undef HAVE_PORT_H */
+
+/* Define to 1 if you have the `select' function. */
+/* #undef HAVE_SELECT */
+
+/* Define if F_SETFD is defined in <fcntl.h> */
+/* #undef HAVE_SETFD */
+
+/* Define to 1 if you have the `sigaction' function. */
+/* #undef HAVE_SIGACTION */
+
+/* Define to 1 if you have the `signal' function. */
+#define HAVE_SIGNAL 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #define HAVE_STDINT_H 1 */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+/* #undef HAVE_STRLCPY */
+
+/* Define to 1 if you have the `strsep' function. */
+/* #undef HAVE_STRSEP */
+
+/* Define to 1 if you have the `strtok_r' function. */
+/* #undef HAVE_STRTOK_R */
+
+/* Define to 1 if the system has the type `struct in6_addr'. */
+#define HAVE_STRUCT_IN6_ADDR 1
+
+/* Define to 1 if you have the <sys/devpoll.h> header file. */
+/* #undef HAVE_SYS_DEVPOLL_H */
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+/* #undef HAVE_SYS_EPOLL_H */
+
+/* Define to 1 if you have the <sys/event.h> header file. */
+/* #undef HAVE_SYS_EVENT_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+/* #undef HAVE_SYS_QUEUE_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+/* #undef HAVE_SYS_SELECT_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #define HAVE_SYS_STAT_H 1 */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #define HAVE_SYS_TIME_H 1 */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #define HAVE_SYS_TYPES_H 1 */
+
+/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
+/* #undef HAVE_TAILQFOREACH */
+
+/* Define if timeradd is defined in <sys/time.h> */
+/* #undef HAVE_TIMERADD */
+
+/* Define if timerclear is defined in <sys/time.h> */
+/* #define HAVE_TIMERCLEAR 1 */
+
+/* Define if timercmp is defined in <sys/time.h> */
+#define HAVE_TIMERCMP 1
+
+/* Define if timerisset is defined in <sys/time.h> */
+#define HAVE_TIMERISSET 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #define HAVE_UNISTD_H 1 */
+
+/* Define to 1 if you have the `vasprintf' function. */
+/* #undef HAVE_VASPRINTF */
+
+/* Define if kqueue works correctly with pipes */
+/* #undef HAVE_WORKING_KQUEUE */
+
+/* Name of package */
+#ifndef PACKAGE
+#define PACKAGE "libevent"
+#endif
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Version number of package */
+#define VERSION "1.3.99-trunk"
+
+#ifndef __func__
+/* Define to appropriate substitue if compiler doesnt have __func__ */
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#define __func__ "??"
+#else
+#define __func__ __FUNCTION__
+#endif
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#define inline __inline
+#endif
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to unsigned int if you dont have it */
+#define socklen_t unsigned int
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+#define uint16_t unsigned short
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#define uint32_t unsigned int
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+#define uint64_t __uint64_t
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+#define uint8_t unsigned char
diff --git a/extra/libevent/WIN32-Code/misc.c b/extra/libevent/WIN32-Code/misc.c
new file mode 100644
index 00000000000..271c9bb399e
--- /dev/null
+++ b/extra/libevent/WIN32-Code/misc.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+#include <sys/timeb.h>
+#include <time.h>
+
+#ifdef __GNUC__
+/*our prototypes for timeval and timezone are in here, just in case the above
+ headers don't have them*/
+#include "misc.h"
+#endif
+
+/****************************************************************************
+ *
+ * Function: gettimeofday(struct timeval *, struct timezone *)
+ *
+ * Purpose: Get current time of day.
+ *
+ * Arguments: tv => Place to store the curent time of day.
+ * tz => Ignored.
+ *
+ * Returns: 0 => Success.
+ *
+ ****************************************************************************/
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ struct _timeb tb;
+
+ if(tv == NULL)
+ return -1;
+
+ _ftime(&tb);
+ tv->tv_sec = (long) tb.time;
+ tv->tv_usec = ((int) tb.millitm) * 1000;
+ return 0;
+}
+#endif
diff --git a/extra/libevent/WIN32-Code/misc.h b/extra/libevent/WIN32-Code/misc.h
new file mode 100644
index 00000000000..aced574687c
--- /dev/null
+++ b/extra/libevent/WIN32-Code/misc.h
@@ -0,0 +1,11 @@
+#ifndef MISC_H
+#define MISC_H
+
+struct timezone;
+struct timeval;
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *,struct timezone *);
+#endif
+
+#endif
diff --git a/extra/libevent/WIN32-Code/tree.h b/extra/libevent/WIN32-Code/tree.h
new file mode 100644
index 00000000000..79e8d91f0eb
--- /dev/null
+++ b/extra/libevent/WIN32-Code/tree.h
@@ -0,0 +1,1354 @@
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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_ */
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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 --git a/extra/libevent/WIN32-Code/win32.c b/extra/libevent/WIN32-Code/win32.c
new file mode 100644
index 00000000000..0d7dcfb276f
--- /dev/null
+++ b/extra/libevent/WIN32-Code/win32.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * Copyright 2003 Michael A. Davis <mike@datanerds.net>
+ * 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 _MSC_VER
+#include "./config.h"
+#else
+/* Avoid the windows/msvc thing. */
+#include "../config.h"
+#endif
+
+#include <winsock2.h>
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#define RB_AUGMENT(x) (void)(x)
+#include "./tree.h"
+#include "log.h"
+#include "event.h"
+#include "event-internal.h"
+
+#define XFREE(ptr) do { if (ptr) free(ptr); } while(0)
+
+extern struct event_list timequeue;
+extern struct event_list addqueue;
+#if 0
+extern struct event_list signalqueue;
+#endif
+
+struct win_fd_set {
+ u_int fd_count;
+ SOCKET fd_array[1];
+};
+
+int evsigcaught[NSIG];
+volatile sig_atomic_t signal_caught = 0;
+/* MSDN says this is required to handle SIGFPE */
+volatile double SIGFPE_REQ = 0.0f;
+
+#if 0
+static void signal_handler(int sig);
+
+void signal_process(void);
+int signal_recalc(void);
+#endif
+
+struct event_entry {
+ RB_ENTRY(event_entry) node;
+ SOCKET sock;
+ int read_pos;
+ int write_pos;
+ struct event *read_event;
+ struct event *write_event;
+};
+
+static int
+compare(struct event_entry *a, struct event_entry *b)
+{
+ if (a->sock < b->sock)
+ return -1;
+ else if (a->sock > b->sock)
+ return 1;
+ else
+ return 0;
+}
+
+struct win32op {
+ int fd_setsz;
+ struct win_fd_set *readset_in;
+ struct win_fd_set *writeset_in;
+ struct win_fd_set *readset_out;
+ struct win_fd_set *writeset_out;
+ struct win_fd_set *exset_out;
+ RB_HEAD(event_map, event_entry) event_root;
+};
+
+RB_PROTOTYPE(event_map, event_entry, node, compare);
+RB_GENERATE(event_map, event_entry, node, compare);
+
+void *win32_init (struct event_base *);
+int win32_insert (void *, struct event *);
+int win32_del (void *, struct event *);
+int win32_dispatch (struct event_base *base, void *, struct timeval *);
+void win32_dealloc (struct event_base *, void *);
+
+struct eventop win32ops = {
+ "win32",
+ win32_init,
+ win32_insert,
+ win32_del,
+ win32_dispatch,
+ win32_dealloc,
+ 0
+};
+
+#define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET)))
+
+static int
+realloc_fd_sets(struct win32op *op, size_t new_size)
+{
+ size_t size;
+
+ assert(new_size >= op->readset_in->fd_count &&
+ new_size >= op->writeset_in->fd_count);
+ assert(new_size >= 1);
+
+ size = FD_SET_ALLOC_SIZE(new_size);
+ if (!(op->readset_in = realloc(op->readset_in, size)))
+ return (-1);
+ if (!(op->writeset_in = realloc(op->writeset_in, size)))
+ return (-1);
+ if (!(op->readset_out = realloc(op->readset_out, size)))
+ return (-1);
+ if (!(op->exset_out = realloc(op->exset_out, size)))
+ return (-1);
+ if (!(op->writeset_out = realloc(op->writeset_out, size)))
+ return (-1);
+ op->fd_setsz = (int)new_size;
+ return (0);
+}
+
+static int
+timeval_to_ms(struct timeval *tv)
+{
+ return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000));
+}
+
+static struct event_entry*
+get_event_entry(struct win32op *op, SOCKET s, int create)
+{
+ struct event_entry key, *val;
+ key.sock = s;
+ val = RB_FIND(event_map, &op->event_root, &key);
+ if (val || !create)
+ return val;
+ if (!(val = calloc(1, sizeof(struct event_entry)))) {
+ event_warn("%s: calloc", __func__);
+ return NULL;
+ }
+ val->sock = s;
+ val->read_pos = val->write_pos = -1;
+ RB_INSERT(event_map, &op->event_root, val);
+ return val;
+}
+
+static int
+do_fd_set(struct win32op *op, struct event_entry *ent, int read)
+{
+ SOCKET s = ent->sock;
+ struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
+ if (read) {
+ if (ent->read_pos >= 0)
+ return (0);
+ } else {
+ if (ent->write_pos >= 0)
+ return (0);
+ }
+ if (set->fd_count == op->fd_setsz) {
+ if (realloc_fd_sets(op, op->fd_setsz*2))
+ return (-1);
+ /* set pointer will have changed and needs reiniting! */
+ set = read ? op->readset_in : op->writeset_in;
+ }
+ set->fd_array[set->fd_count] = s;
+ if (read)
+ ent->read_pos = set->fd_count;
+ else
+ ent->write_pos = set->fd_count;
+ return (set->fd_count++);
+}
+
+static int
+do_fd_clear(struct win32op *op, struct event_entry *ent, int read)
+{
+ int i;
+ struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
+ if (read) {
+ i = ent->read_pos;
+ ent->read_pos = -1;
+ } else {
+ i = ent->write_pos;
+ ent->write_pos = -1;
+ }
+ if (i < 0)
+ return (0);
+ if (--set->fd_count != i) {
+ struct event_entry *ent2;
+ SOCKET s2;
+ s2 = set->fd_array[i] = set->fd_array[set->fd_count];
+ ent2 = get_event_entry(op, s2, 0);
+ if (!ent) /* This indicates a bug. */
+ return (0);
+ if (read)
+ ent2->read_pos = i;
+ else
+ ent2->write_pos = i;
+ }
+ return (0);
+}
+
+#define NEVENT 64
+void *
+win32_init(struct event_base *_base)
+{
+ struct win32op *winop;
+ size_t size;
+ if (!(winop = calloc(1, sizeof(struct win32op))))
+ return NULL;
+ winop->fd_setsz = NEVENT;
+ size = FD_SET_ALLOC_SIZE(NEVENT);
+ if (!(winop->readset_in = malloc(size)))
+ goto err;
+ if (!(winop->writeset_in = malloc(size)))
+ goto err;
+ if (!(winop->readset_out = malloc(size)))
+ goto err;
+ if (!(winop->writeset_out = malloc(size)))
+ goto err;
+ if (!(winop->exset_out = malloc(size)))
+ goto err;
+ RB_INIT(&winop->event_root);
+ winop->readset_in->fd_count = winop->writeset_in->fd_count = 0;
+ winop->readset_out->fd_count = winop->writeset_out->fd_count
+ = winop->exset_out->fd_count = 0;
+
+ evsignal_init(_base);
+
+ return (winop);
+ err:
+ XFREE(winop->readset_in);
+ XFREE(winop->writeset_in);
+ XFREE(winop->readset_out);
+ XFREE(winop->writeset_out);
+ XFREE(winop->exset_out);
+ XFREE(winop);
+ return (NULL);
+}
+
+int
+win32_insert(void *op, struct event *ev)
+{
+ struct win32op *win32op = op;
+ struct event_entry *ent;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ return (evsignal_add(ev));
+ }
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+ ent = get_event_entry(win32op, ev->ev_fd, 1);
+ if (!ent)
+ return (-1); /* out of memory */
+
+ event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd));
+ if (ev->ev_events & EV_READ) {
+ if (do_fd_set(win32op, ent, 1)<0)
+ return (-1);
+ ent->read_event = ev;
+ }
+ if (ev->ev_events & EV_WRITE) {
+ if (do_fd_set(win32op, ent, 0)<0)
+ return (-1);
+ ent->write_event = ev;
+ }
+ return (0);
+}
+
+int
+win32_del(void *op, struct event *ev)
+{
+ struct win32op *win32op = op;
+ struct event_entry *ent;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(ev));
+
+ if (!(ent = get_event_entry(win32op, ev->ev_fd, 0)))
+ return (-1);
+ event_debug(("%s: Removing event for %d", __func__, ev->ev_fd));
+ if (ev == ent->read_event) {
+ do_fd_clear(win32op, ent, 1);
+ ent->read_event = NULL;
+ }
+ if (ev == ent->write_event) {
+ do_fd_clear(win32op, ent, 0);
+ ent->write_event = NULL;
+ }
+ if (!ent->read_event && !ent->write_event) {
+ RB_REMOVE(event_map, &win32op->event_root, ent);
+ free(ent);
+ }
+
+ return 0;
+}
+
+static void
+fd_set_copy(struct win_fd_set *out, const struct win_fd_set *in)
+{
+ out->fd_count = in->fd_count;
+ memcpy(out->fd_array, in->fd_array, in->fd_count * (sizeof(SOCKET)));
+}
+
+/*
+ static void dump_fd_set(struct win_fd_set *s)
+ {
+ unsigned int i;
+ printf("[ ");
+ for(i=0;i<s->fd_count;++i)
+ printf("%d ",(int)s->fd_array[i]);
+ printf("]\n");
+ }
+*/
+
+int
+win32_dispatch(struct event_base *base, void *op,
+ struct timeval *tv)
+{
+ struct win32op *win32op = op;
+ int res = 0;
+ u_int i;
+ int fd_count;
+
+ fd_set_copy(win32op->readset_out, win32op->readset_in);
+ fd_set_copy(win32op->exset_out, win32op->readset_in);
+ fd_set_copy(win32op->writeset_out, win32op->writeset_in);
+
+ fd_count =
+ (win32op->readset_out->fd_count > win32op->writeset_out->fd_count) ?
+ win32op->readset_out->fd_count : win32op->writeset_out->fd_count;
+
+ if (!fd_count) {
+ /* Windows doesn't like you to call select() with no sockets */
+ Sleep(timeval_to_ms(tv));
+ evsignal_process(base);
+ return (0);
+ }
+
+ res = select(fd_count,
+ (struct fd_set*)win32op->readset_out,
+ (struct fd_set*)win32op->writeset_out,
+ (struct fd_set*)win32op->exset_out, tv);
+
+ event_debug(("%s: select returned %d", __func__, res));
+
+ if(res <= 0) {
+ evsignal_process(base);
+ return res;
+ } else if (base->sig.evsignal_caught) {
+ evsignal_process(base);
+ }
+
+ for (i=0; i<win32op->readset_out->fd_count; ++i) {
+ struct event_entry *ent;
+ SOCKET s = win32op->readset_out->fd_array[i];
+ if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event)
+ event_active(ent->read_event, EV_READ, 1);
+ }
+ for (i=0; i<win32op->exset_out->fd_count; ++i) {
+ struct event_entry *ent;
+ SOCKET s = win32op->exset_out->fd_array[i];
+ if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event)
+ event_active(ent->read_event, EV_READ, 1);
+ }
+ for (i=0; i<win32op->writeset_out->fd_count; ++i) {
+ struct event_entry *ent;
+ SOCKET s = win32op->writeset_out->fd_array[i];
+ if ((ent = get_event_entry(win32op, s, 0)) && ent->write_event)
+ event_active(ent->write_event, EV_WRITE, 1);
+ }
+
+#if 0
+ if (signal_recalc() == -1)
+ return (-1);
+#endif
+
+ return (0);
+}
+
+void
+win32_dealloc(struct event_base *_base, void *arg)
+{
+ struct win32op *win32op = arg;
+
+ evsignal_dealloc(_base);
+ if (win32op->readset_in)
+ free(win32op->readset_in);
+ if (win32op->writeset_in)
+ free(win32op->writeset_in);
+ if (win32op->readset_out)
+ free(win32op->readset_out);
+ if (win32op->writeset_out)
+ free(win32op->writeset_out);
+ if (win32op->exset_out)
+ free(win32op->exset_out);
+ /* XXXXX free the tree. */
+
+ memset(win32op, 0, sizeof(win32op));
+ free(win32op);
+}
+
+#if 0
+static void
+signal_handler(int sig)
+{
+ evsigcaught[sig]++;
+ signal_caught = 1;
+}
+
+int
+signal_recalc(void)
+{
+ struct event *ev;
+
+ /* Reinstall our signal handler. */
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+void
+signal_process(void)
+{
+ struct event *ev;
+ short ncalls;
+
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+ ncalls = evsigcaught[EVENT_SIGNAL(ev)];
+ if (ncalls) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, EV_SIGNAL, ncalls);
+ }
+ }
+
+ memset(evsigcaught, 0, sizeof(evsigcaught));
+ signal_caught = 0;
+}
+#endif
+
diff --git a/extra/libevent/buffer.c b/extra/libevent/buffer.c
new file mode 100644
index 00000000000..d3394dae470
--- /dev/null
+++ b/extra/libevent/buffer.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
+ * 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
+#include <winsock2.h>
+#include <windows.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"
+#include "config.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 ((size_t)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 (int)(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;
+
+#if defined(FIONREAD)
+#ifdef WIN32
+ long lng = (long)n;
+ if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) {
+#else
+ if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
+#endif
+ 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 ((size_t)n > (buf->totallen << 2))
+ n = (int)(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);
+#else
+ n = recv(fd, p, howmuch, 0);
+#endif
+ if (n == -1)
+ return (-1);
+ if (n == 0)
+ return (0);
+
+ 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;
+
+#ifndef WIN32
+ n = write(fd, buffer->buffer, buffer->off);
+#else
+ n = send(fd, buffer->buffer, (int)buffer->off, 0);
+#endif
+ if (n == -1)
+ return (-1);
+ if (n == 0)
+ return (0);
+ 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 --git a/extra/libevent/compat/sys/_time.h b/extra/libevent/compat/sys/_time.h
new file mode 100644
index 00000000000..8cabb0d55e7
--- /dev/null
+++ b/extra/libevent/compat/sys/_time.h
@@ -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 --git a/extra/libevent/compat/sys/queue.h b/extra/libevent/compat/sys/queue.h
new file mode 100644
index 00000000000..c0956ddce43
--- /dev/null
+++ b/extra/libevent/compat/sys/queue.h
@@ -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 --git a/extra/libevent/compat/sys/tree.h b/extra/libevent/compat/sys/tree.h
new file mode 100644
index 00000000000..6a3381caee4
--- /dev/null
+++ b/extra/libevent/compat/sys/tree.h
@@ -0,0 +1,677 @@
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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 --git a/extra/libevent/devpoll.c b/extra/libevent/devpoll.c
new file mode 100644
index 00000000000..82427c9e229
--- /dev/null
+++ b/extra/libevent/devpoll.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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_DEVPOLL
+
+#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/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;
+};
+
+static void *devpoll_init (struct event_base *);
+static int devpoll_add (void *, struct event *);
+static int devpoll_del (void *, struct event *);
+static int devpoll_dispatch (struct event_base *, void *, struct timeval *);
+static void devpoll_dealloc (struct event_base *, void *);
+
+struct eventop devpollops = {
+ "devpoll",
+ devpoll_init,
+ devpoll_add,
+ devpoll_del,
+ devpoll_dispatch,
+ devpoll_dealloc,
+ 1 /* need reinit */
+};
+
+#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);
+}
+
+static 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 &&
+ (unsigned long long) rl.rlim_cur != (unsigned long long) 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+#endif /* HAVE_DEVPOLL */
diff --git a/extra/libevent/epoll.c b/extra/libevent/epoll.c
new file mode 100644
index 00000000000..7c8c60bdffa
--- /dev/null
+++ b/extra/libevent/epoll.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
+ * 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_EPOLL
+
+#include <stdint.h>
+#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/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;
+};
+
+static void *epoll_init (struct event_base *);
+static int epoll_add (void *, struct event *);
+static int epoll_del (void *, struct event *);
+static int epoll_dispatch (struct event_base *, void *, struct timeval *);
+static void epoll_dealloc (struct event_base *, void *);
+
+struct eventop epollops = {
+ "epoll",
+ epoll_init,
+ epoll_add,
+ epoll_del,
+ epoll_dispatch,
+ epoll_dealloc,
+ 1 /* need reinit */
+};
+
+#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
+
+static 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);
+}
+
+static int
+epoll_recalc(struct event_base *base __attribute__((unused)),
+ 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);
+}
+
+static 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 what = events[i].events;
+ struct event *evread = NULL, *evwrite = NULL;
+
+ evep = (struct evepoll *)events[i].data.ptr;
+
+ if (what & (EPOLLHUP|EPOLLERR)) {
+ evread = evep->evread;
+ evwrite = evep->evwrite;
+ } else {
+ if (what & EPOLLIN) {
+ evread = evep->evread;
+ }
+
+ if (what & EPOLLOUT) {
+ evwrite = evep->evwrite;
+ }
+ }
+
+ if (!(evread||evwrite))
+ continue;
+
+ if (evread != NULL)
+ event_active(evread, EV_READ, 1);
+ if (evwrite != NULL)
+ event_active(evwrite, EV_WRITE, 1);
+ }
+
+ return (0);
+}
+
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+#endif /* HAVE_EPOLL */
diff --git a/extra/libevent/epoll_sub.c b/extra/libevent/epoll_sub.c
new file mode 100644
index 00000000000..95daac3b276
--- /dev/null
+++ b/extra/libevent/epoll_sub.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003 Niels Provos <provos@citi.umich.edu>
+ * 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_EPOLL
+
+#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));
+}
+
+#endif /* HAVE_EPOLL */
diff --git a/extra/libevent/evbuffer.c b/extra/libevent/evbuffer.c
new file mode 100644
index 00000000000..432d75d1c53
--- /dev/null
+++ b/extra/libevent/evbuffer.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
+ * 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>
+
+#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
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include "evutil.h"
+#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) {
+ evutil_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 __attribute__((unused)), 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 = (int)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, const 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 --git a/extra/libevent/evdns.c b/extra/libevent/evdns.c
new file mode 100644
index 00000000000..de08a18ca19
--- /dev/null
+++ b/extra/libevent/evdns.c
@@ -0,0 +1,3150 @@
+/* $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@imperialviolet.org>
+ * 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@imperialviolet.org>
+ *
+ * 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
+
+#ifdef DNS_USE_FTIME_FOR_ID
+#include <sys/timeb.h>
+#endif
+
+#ifndef DNS_USE_CPU_CLOCK_FOR_ID
+#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
+#ifndef DNS_USE_OPENSSL_FOR_ID
+#ifndef DNS_USE_FTIME_FOR_ID
+#error Must configure at least one id generation method.
+#error Please see the documentation.
+#endif
+#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>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "evdns.h"
+#include "evutil.h"
+#include "log.h"
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#include <iphlpapi.h>
+#include <io.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
+
+#define EVDNS_LOG_DEBUG 0
+#define EVDNS_LOG_WARN 1
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+#include <stdio.h>
+
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#ifdef __USE_ISOC99B
+/* libevent doesn't work without this */
+typedef ev_uint8_t u_char;
+typedef unsigned int uint;
+#endif
+#include <event.h>
+
+#define u64 ev_uint64_t
+#define u32 ev_uint32_t
+#define u16 ev_uint16_t
+#define u8 ev_uint8_t
+
+#ifdef WIN32
+#define snprintf _snprintf
+#define open _open
+#define read _read
+#define close _close
+#define strdup _strdup
+#endif
+
+#define MAX_ADDRS 32 /* 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 *const 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)
+{
+ ev_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;
+}
+#else
+#define last_error(sock) (errno)
+#define error_is_eagain(err) ((err) == EAGAIN)
+#endif
+#define CLOSE_SOCKET(s) EVUTIL_CLOSESOCKET(s)
+
+#define ISSPACE(c) isspace((int)(unsigned char)(c))
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+
+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;
+}
+
+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. */
+ flags &= 0x0110; /* Only RD and CD 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->dns_question_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++;
+
+ /* Only standard queries are supported. */
+ if (flags & 0x7800) {
+ evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL);
+ return -1;
+ }
+
+ 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
+}
+
+static u16
+default_transaction_id_fn(void)
+{
+ u16 trans_id;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+ struct timespec ts;
+#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_FTIME_FOR_ID
+ struct _timeb tb;
+ _ftime(&tb);
+ trans_id = tb.millitm & 0xffff;
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ trans_id = tv.tv_usec & 0xffff;
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_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
+ return trans_id;
+}
+
+static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn;
+
+void
+evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void))
+{
+ if (fn)
+ trans_id_function = fn;
+ else
+ trans_id_function = default_transaction_id_fn;
+}
+
+/* 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;
+ u16 trans_id = trans_id_function();
+
+ 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->dns_question_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]->dns_question_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->dns_question_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( (short) (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 sock_err = last_error(port->socket);
+ if (! error_is_eagain(sock_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);
+ if (evtimer_initialized(&server->timeout_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; }
+ evutil_make_socket_nonblocking(ns->socket);
+ 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) {
+ /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */
+ char buf[73];
+ 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 */
+ 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
+}
+
+static 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
+ res = 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]->dns_question_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]->dns_question_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]->dns_question_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);
+ evutil_make_socket_nonblocking(sock);
+ 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 --git a/extra/libevent/evdns.h b/extra/libevent/evdns.h
new file mode 100644
index 00000000000..0ad22712962
--- /dev/null
+++ b/extra/libevent/evdns.h
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
+ * 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@imperialviolet.org>
+ *
+ * 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)
+ */
+
+/** @file evdns.h
+ *
+ * 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
+ *
+ * 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
+
+/* For integer types. */
+#include <evutil.h>
+
+/** 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);
+
+/**
+ Initialize the asynchronous DNS library.
+
+ This function initializes support for non-blocking name resolution by
+ calling evdns_resolv_conf_parse() on UNIX and
+ evdns_config_windows_nameservers() on Windows.
+
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_shutdown()
+ */
+int evdns_init(void);
+
+
+/**
+ Shut down the asynchronous DNS resolver and terminate all active requests.
+
+ If the 'fail_requests' option is enabled, all active requests will return
+ an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise,
+ the requests will be silently discarded.
+
+ @param fail_requests if zero, active requests will be aborted; if non-zero,
+ active requests will return DNS_ERR_SHUTDOWN.
+ @see evdns_init()
+ */
+void evdns_shutdown(int fail_requests);
+
+
+/**
+ Convert a DNS error code to a string.
+
+ @param err the DNS error code
+ @return a string containing an explanation of the error code
+*/
+const char *evdns_err_to_string(int err);
+
+
+/**
+ Add a nameserver.
+
+ The address should be an IPv4 address in network byte order.
+ The type of address is chosen so that it matches in_addr.s_addr.
+
+ @param address an IP address in network byte order
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_nameserver_ip_add()
+ */
+int evdns_nameserver_add(unsigned long int address);
+
+
+/**
+ Get the number of configured nameservers.
+
+ This returns 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.
+
+ @return the number of configured nameservers
+ @see evdns_nameserver_add()
+ */
+int evdns_count_nameservers(void);
+
+
+/**
+ Remove all configured nameservers, and suspend all pending resolves.
+
+ Resolves will not necessarily be re-attempted until evdns_resume() is called.
+
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resume()
+ */
+int evdns_clear_nameservers_and_suspend(void);
+
+
+/**
+ Resume normal operation and continue any suspended resolve requests.
+
+ Re-attempt resolves left in limbo after an earlier call to
+ evdns_clear_nameservers_and_suspend().
+
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_clear_nameservers_and_suspend()
+ */
+int evdns_resume(void);
+
+
+/**
+ Add a nameserver.
+
+ This wraps the evdns_nameserver_add() function by parsing a string as an IP
+ address and adds it as a nameserver.
+
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_nameserver_add()
+ */
+int evdns_nameserver_ip_add(const char *ip_as_string);
+
+
+/**
+ Lookup an A record for a given name.
+
+ @param name a DNS hostname
+ @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+ @param callback a callback function to invoke when the request is completed
+ @param ptr an argument to pass to the callback function
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
+ */
+int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
+
+
+/**
+ Lookup an AAAA record for a given name.
+
+ @param name a DNS hostname
+ @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+ @param callback a callback function to invoke when the request is completed
+ @param ptr an argument to pass to the callback function
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
+ */
+int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr);
+
+struct in_addr;
+struct in6_addr;
+
+/**
+ Lookup a PTR record for a given IP address.
+
+ @param in an IPv4 address
+ @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+ @param callback a callback function to invoke when the request is completed
+ @param ptr an argument to pass to the callback function
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resolve_reverse_ipv6()
+ */
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
+
+
+/**
+ Lookup a PTR record for a given IPv6 address.
+
+ @param in an IPv6 address
+ @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+ @param callback a callback function to invoke when the request is completed
+ @param ptr an argument to pass to the callback function
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resolve_reverse_ipv6()
+ */
+int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr);
+
+
+/**
+ Set the value of a configuration option.
+
+ The currently available configuration options are:
+
+ ndots, timeout, max-timeouts, max-inflight, and attempts
+
+ @param option the name of the configuration option to be modified
+ @param val the value to be set
+ @param flags either 0 | DNS_OPTION_SEARCH | DNS_OPTION_MISC
+ @return 0 if successful, or -1 if an error occurred
+ */
+int evdns_set_option(const char *option, const char *val, int flags);
+
+
+/**
+ Parse a resolv.conf file.
+
+ The 'flags' parameter determines what information is parsed from the
+ resolv.conf file. See the man page for resolv.conf for the format of this
+ file.
+
+ The following directives are not parsed from the file: sortlist, rotate,
+ no-check-names, inet6, debug.
+
+ If this function encounters an error, the possible return values are: 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 listed in the file
+
+ @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC|
+ DNS_OPTIONS_ALL
+ @param filename the path to the resolv.conf file
+ @return 0 if successful, or various positive error codes if an error
+ occurred (see above)
+ @see resolv.conf(3), evdns_config_windows_nameservers()
+ */
+int evdns_resolv_conf_parse(int flags, const char *const filename);
+
+
+/**
+ Obtain nameserver information using the Windows API.
+
+ 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.
+
+ @return 0 if successful, or -1 if an error occurred
+ @see evdns_resolv_conf_parse()
+ */
+#ifdef MS_WINDOWS
+int evdns_config_windows_nameservers(void);
+#endif
+
+
+/**
+ Clear the list of search domains.
+ */
+void evdns_search_clear(void);
+
+
+/**
+ Add a domain to the list of search domains
+
+ @param domain the domain to be added to the search list
+ */
+void evdns_search_add(const char *domain);
+
+
+/**
+ Set the 'ndots' parameter for searches.
+
+ Sets the number of dots which, when found in a name, causes
+ the first query to be without any search domain.
+
+ @param ndots the new ndots parameter
+ */
+void evdns_search_ndots_set(const int ndots);
+
+/**
+ A callback that is invoked when a log message is generated
+
+ @param is_warning indicates if the log message is a 'warning'
+ @param msg the content of the log message
+ */
+typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
+
+
+/**
+ Set the callback function to handle log messages.
+
+ @param fn the callback to be invoked when a log message is generated
+ */
+void evdns_set_log_fn(evdns_debug_log_fn_type fn);
+
+/**
+ Set a callback that will be invoked to generate transaction IDs. By
+ default, we pick transaction IDs based on the current clock time.
+
+ @param fn the new callback, or NULL to use the default.
+ */
+void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
+
+#define DNS_NO_SEARCH 1
+
+/*
+ * 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;
+#ifdef __cplusplus
+ int dns_question_class;
+#else
+ /* You should refer to this field as "dns_question_class". The
+ * name "class" works in C for backward compatibility, and will be
+ * removed in a future version. (1.5 or later). */
+ int class;
+#define dns_question_class class
+#endif
+ 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 dns_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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !EVENTDNS_H */
diff --git a/extra/libevent/event-internal.h b/extra/libevent/event-internal.h
new file mode 100644
index 00000000000..59a089ddec4
--- /dev/null
+++ b/extra/libevent/event-internal.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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 "config.h"
+#include "min_heap.h"
+#include "evsignal.h"
+
+struct eventop {
+ const char *name;
+ void *(*init)(struct event_base *);
+ int (*add)(void *, struct event *);
+ int (*del)(void *, struct event *);
+ int (*dispatch)(struct event_base *, void *, struct timeval *);
+ void (*dealloc)(struct event_base *, void *);
+ /* set if we need to reinitialize the event base */
+ int need_reinit;
+};
+
+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 */
+ int event_break; /* Set to terminate loop immediately */
+
+ /* active event management */
+ struct event_list **activequeues;
+ int nactivequeues;
+
+ /* signal handling info */
+ struct evsignal_info sig;
+
+ struct event_list eventqueue;
+ struct timeval event_tv;
+
+ struct min_heap timeheap;
+};
+
+/* Internal use only: Functions that might be missing from <sys/queue.h> */
+/* These following macros are copied from BSD sys/queue.h
+ Copyright (c) 1991, 1993, The Regents of the University of California.
+ All rights reserved.
+*/
+#ifndef TAILQ_EMPTY
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#endif /* TAILQ_EMPTY */
+#ifndef HAVE_TAILQFOREACH
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+#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)
+#endif /* TAILQ_FOREACH */
+
+int _evsignal_set_handler(struct event_base *base, int evsignal,
+ void (*fn)(int));
+int _evsignal_restore_handler(struct event_base *base, int evsignal);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_INTERNAL_H_ */
diff --git a/extra/libevent/event.c b/extra/libevent/event.c
new file mode 100644
index 00000000000..2042c2de626
--- /dev/null
+++ b/extra/libevent/event.c
@@ -0,0 +1,989 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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>
+#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 "evutil.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_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_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 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));
+}
+
+struct event_base *
+event_init(void)
+{
+ struct event_base *base = event_base_new();
+
+ if (base != NULL)
+ current_base = base;
+
+ return (base);
+}
+
+struct event_base *
+event_base_new(void)
+{
+ int i;
+ struct event_base *base;
+
+ if ((base = calloc(1, sizeof(struct event_base))) == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ event_sigcb = NULL;
+ event_gotsig = 0;
+
+ detect_monotonic();
+ gettime(&base->event_tv);
+
+ min_heap_ctor(&base->timeheap);
+ 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);
+
+ return (base);
+}
+
+void
+event_base_free(struct event_base *base)
+{
+ int i, n_deleted=0;
+ struct event *ev;
+
+ if (base == NULL && current_base)
+ base = current_base;
+ if (base == current_base)
+ current_base = NULL;
+
+ /* XXX(niels) - check for internal events first */
+ assert(base);
+ /* Delete all non-internal events. */
+ for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) {
+ struct event *next = TAILQ_NEXT(ev, ev_next);
+ if (!(ev->ev_flags & EVLIST_INTERNAL)) {
+ event_del(ev);
+ ++n_deleted;
+ }
+ ev = next;
+ }
+ while ((ev = min_heap_top(&base->timeheap)) != NULL) {
+ event_del(ev);
+ ++n_deleted;
+ }
+
+ if (n_deleted)
+ event_debug(("%s: %d events were still set in base",
+ __func__, n_deleted));
+
+ 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(min_heap_empty(&base->timeheap));
+ min_heap_dtor(&base->timeheap);
+
+ for (i = 0; i < base->nactivequeues; ++i)
+ free(base->activequeues[i]);
+ free(base->activequeues);
+
+ assert(TAILQ_EMPTY(&base->eventqueue));
+
+ free(base);
+}
+
+/* reinitialized the event base after a fork */
+int
+event_reinit(struct event_base *base)
+{
+ const struct eventop *evsel = base->evsel;
+ void *evbase = base->evbase;
+ int res = 0;
+ struct event *ev;
+
+ /* check if this event mechanism requires reinit */
+ if (!evsel->need_reinit)
+ return (0);
+
+ if (base->evsel->dealloc != NULL)
+ base->evsel->dealloc(base, base->evbase);
+ base->evbase = evsel->init(base);
+ if (base->evbase == NULL)
+ event_errx(1, "%s: could not reinitialize event mechanism",
+ __func__);
+
+ TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
+ if (evsel->add(evbase, ev) == -1)
+ res = -1;
+ }
+
+ return (res);
+}
+
+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;
+
+ 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)) {
+ if (ev->ev_events & EV_PERSIST)
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
+ else
+ event_del(ev);
+
+ /* 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 || base->event_break)
+ 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));
+}
+
+const char *
+event_base_get_method(struct event_base *base)
+{
+ assert(base);
+ return (base->evsel->name);
+}
+
+static void
+event_loopexit_cb(int fd __attribute__((unused)),
+ short what __attribute__((unused)), 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_loopbreak(void)
+{
+ return (event_base_loopbreak(current_base));
+}
+
+int
+event_base_loopbreak(struct event_base *event_base)
+{
+ if (event_base == NULL)
+ return (-1);
+
+ event_base->event_break = 1;
+ return (0);
+}
+
+
+
+/* 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;
+
+ if(!TAILQ_EMPTY(&base->sig.signalqueue))
+ evsignal_base = base;
+ done = 0;
+ while (!done) {
+ /* Terminate the loop if we have been asked to */
+ if (base->event_gotterm) {
+ base->event_gotterm = 0;
+ break;
+ }
+
+ if (base->event_break) {
+ base->event_break = 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.
+ */
+ evutil_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) {
+ evutil_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;
+
+ min_heap_elem_init(ev);
+
+ /* 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);
+ evutil_timersub(&ev->ev_timeout, &now, &res);
+ /* correctly remap to real time */
+ gettimeofday(&now, NULL);
+ evutil_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);
+ else if (min_heap_reserve(&base->timeheap,
+ 1 + min_heap_size(&base->timeheap)) == -1)
+ return (-1); /* ENOMEM == errno */
+
+ /* 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);
+ evutil_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))) {
+ int res = evsel->add(evbase, ev);
+ if (res != -1)
+ event_queue_insert(base, ev, EVLIST_INSERTED);
+
+ return (res);
+ } else if ((ev->ev_events & EV_SIGNAL) &&
+ !(ev->ev_flags & EVLIST_SIGNAL)) {
+ int res = evsel->add(evbase, ev);
+ if (res != -1)
+ event_queue_insert(base, ev, EVLIST_SIGNAL);
+
+ return (res);
+ }
+
+ 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 = min_heap_top(&base->timeheap)) == NULL) {
+ /* if no time-based events are active wait for I/O */
+ *tv_p = NULL;
+ return (0);
+ }
+
+ if (gettime(&now) == -1)
+ return (-1);
+
+ if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
+ evutil_timerclear(tv);
+ return (0);
+ }
+
+ evutil_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 **pev;
+ unsigned int size;
+ struct timeval off;
+
+ if (use_monotonic)
+ return;
+
+ /* Check if time is running backwards */
+ gettime(tv);
+ if (evutil_timercmp(tv, &base->event_tv, >=)) {
+ base->event_tv = *tv;
+ return;
+ }
+
+ event_debug(("%s: time is running backwards, corrected",
+ __func__));
+ evutil_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.
+ */
+ pev = base->timeheap.p;
+ size = base->timeheap.n;
+ for (; size-- > 0; ++pev) {
+ struct timeval *ev_tv = &(**pev).ev_timeout;
+ evutil_timersub(ev_tv, &off, ev_tv);
+ }
+}
+
+void
+timeout_process(struct event_base *base)
+{
+ struct timeval now;
+ struct event *ev;
+
+ if (min_heap_empty(&base->timeheap))
+ return;
+
+ gettime(&now);
+
+ while ((ev = min_heap_top(&base->timeheap))) {
+ if (evutil_timercmp(&ev->ev_timeout, &now, >))
+ break;
+
+ /* 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)
+{
+ 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)
+ base->event_count--;
+
+ ev->ev_flags &= ~queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ 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:
+ min_heap_erase(&base->timeheap, 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)
+{
+ 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)
+ base->event_count++;
+
+ ev->ev_flags |= queue;
+ switch (queue) {
+ case EVLIST_ACTIVE:
+ 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: {
+ min_heap_push(&base->timeheap, ev);
+ 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 --git a/extra/libevent/event.h b/extra/libevent/event.h
new file mode 100644
index 00000000000..078c940770e
--- /dev/null
+++ b/extra/libevent/event.h
@@ -0,0 +1,1129 @@
+/*
+ * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu>
+ * 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_
+
+/** @mainpage
+
+ @section intro Introduction
+
+ libevent is an event notification library for developing scalable network
+ servers. The libevent API provides a mechanism to execute a callback
+ function when a specific event occurs on a file descriptor or after a
+ timeout has been reached. Furthermore, libevent also support callbacks due
+ to signals or regular timeouts.
+
+ libevent is meant to replace the event loop found in event driven network
+ servers. An application just needs to call event_dispatch() and then add or
+ remove events dynamically without having to change the event loop.
+
+ Currently, libevent supports /dev/poll, kqueue(2), select(2), poll(2) and
+ epoll(4). It also has experimental support for real-time signals. The
+ internal event mechanism is completely independent of the exposed event API,
+ and a simple update of libevent can provide new functionality without having
+ to redesign the applications. As a result, Libevent allows for portable
+ application development and provides the most scalable event notification
+ mechanism available on an operating system. Libevent can also be used for
+ multi-threaded aplications; see Steven Grimm's explanation. Libevent should
+ compile on Linux, *BSD, Mac OS X, Solaris and Windows.
+
+ @section usage Standard usage
+
+ Every program that uses libevent must include the <event.h> header, and pass
+ the -levent flag to the linker. Before using any of the functions in the
+ library, you must call event_init() or event_base_new() to perform one-time
+ initialization of the libevent library.
+
+ @section event Event notification
+
+ For each file descriptor that you wish to monitor, you must declare an event
+ structure and call event_set() to initialize the members of the structure.
+ To enable notification, you add the structure to the list of monitored
+ events by calling event_add(). The event structure must remain allocated as
+ long as it is active, so it should be allocated on the heap. Finally, you
+ call event_dispatch() to loop and dispatch events.
+
+ @section bufferevent I/O Buffers
+
+ libevent provides an abstraction on top of the regular event callbacks. This
+ abstraction is called a buffered event. A buffered event provides input and
+ output buffers that get filled and drained automatically. The user of a
+ buffered event no longer deals directly with the I/O, but instead is reading
+ from input and writing to output buffers.
+
+ Once initialized via bufferevent_new(), the bufferevent structure can be
+ used repeatedly with bufferevent_enable() and bufferevent_disable().
+ Instead of reading and writing directly to a socket, you would call
+ bufferevent_read() and bufferevent_write().
+
+ When read enabled the bufferevent will try to read from the file descriptor
+ and call the read callback. The write callback is executed whenever the
+ output buffer is drained below the write low watermark, which is 0 by
+ default.
+
+ @section timers Timers
+
+ libevent can also be used to create timers that invoke a callback after a
+ certain amount of time has expired. The evtimer_set() function prepares an
+ event struct to be used as a timer. To activate the timer, call
+ evtimer_add(). Timers can be deactivated by calling evtimer_del().
+
+ @section timeouts Timeouts
+
+ In addition to simple timers, libevent can assign timeout events to file
+ descriptors that are triggered whenever a certain amount of time has passed
+ with no activity on a file descriptor. The timeout_set() function
+ initializes an event struct for use as a timeout. Once initialized, the
+ event must be activated by using timeout_add(). To cancel the timeout, call
+ timeout_del().
+
+ @section evdns Asynchronous DNS resolution
+
+ libevent provides an asynchronous DNS resolver that should be used instead
+ of the standard DNS resolver functions. These functions can be imported by
+ including the <evdns.h> header in your program. Before using any of the
+ resolver functions, you must call evdns_init() to initialize the library. To
+ convert a hostname to an IP address, you call the evdns_resolve_ipv4()
+ function. To perform a reverse lookup, you would call the
+ evdns_resolve_reverse() function. All of these functions use callbacks to
+ avoid blocking while the lookup is performed.
+
+ @section evhttp Event-driven HTTP servers
+
+ libevent provides a very simple event-driven HTTP server that can be
+ embedded in your program and used to service HTTP requests.
+
+ To use this capability, you need to include the <evhttp.h> header in your
+ program. You create the server by calling evhttp_new(). Add addresses and
+ ports to listen on with evhttp_bind_socket(). You then register one or more
+ callbacks to handle incoming requests. Each URI can be assigned a callback
+ via the evhttp_set_cb() function. A generic callback function can also be
+ registered via evhttp_set_gencb(); this callback will be invoked if no other
+ callbacks have been registered for a given URI.
+
+ @section evrpc A framework for RPC servers and clients
+
+ libevents provides a framework for creating RPC servers and clients. It
+ takes care of marshaling and unmarshaling all data structures.
+
+ @section api API Reference
+
+ To browse the complete documentation of the libevent API, click on any of
+ the following links.
+
+ event.h
+ The primary libevent header
+
+ evdns.h
+ Asynchronous DNS resolution
+
+ evhttp.h
+ An embedded libevent-based HTTP server
+
+ evrpc.h
+ A framework for creating RPC servers and clients
+
+ */
+
+/** @file event.h
+
+ A library for writing event-driven network servers
+
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <my_attribute.h>
+
+#include <event-config.h>
+#ifdef _EVENT_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef _EVENT_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef _EVENT_HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdarg.h>
+
+/* For int types. */
+#include <evutil.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 */
+
+struct event_base;
+struct event {
+ TAILQ_ENTRY (event) ev_next;
+ TAILQ_ENTRY (event) ev_active_next;
+ TAILQ_ENTRY (event) ev_signal_next;
+ unsigned int min_heap_idx; /* for managing timeouts */
+
+ 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 */
+
+/**
+ Initialize the event API.
+
+ Use event_base_new() to initialize a new event base, but does not set
+ the current_base global. If using only event_base_new(), each event
+ added must have an event base set with event_base_set()
+
+ @see event_base_set(), event_base_free(), event_init()
+ */
+struct event_base *event_base_new(void);
+
+/**
+ Initialize the event API.
+
+ The event API needs to be initialized with event_init() before it can be
+ used. Sets the current_base global representing the default base for
+ events that have no base associated with them.
+
+ @see event_base_set(), event_base_new()
+ */
+struct event_base *event_init(void);
+
+/**
+ Reinitialized the event base after a fork
+
+ Some event mechanisms do not survive across fork. The event base needs
+ to be reinitialized with the event_reinit() function.
+
+ @param base the event base that needs to be re-initialized
+ @return 0 if successful, or -1 if some events could not be re-added.
+ @see event_base_new(), event_init()
+*/
+int event_reinit(struct event_base *base);
+
+/**
+ Loop to process events.
+
+ In order to process events, an application needs to call
+ event_dispatch(). This function only returns on error, and should
+ replace the event core of the application program.
+
+ @see event_base_dispatch()
+ */
+int event_dispatch(void);
+
+
+/**
+ Threadsafe event dispatching loop.
+
+ @param eb the event_base structure returned by event_init()
+ @see event_init(), event_dispatch()
+ */
+int event_base_dispatch(struct event_base *);
+
+
+/**
+ Get the kernel event notification mechanism used by libevent.
+
+ @param eb the event_base structure returned by event_base_new()
+ @return a string identifying the kernel event mechanism (kqueue, epoll, etc.)
+ */
+const char *event_base_get_method(struct event_base *);
+
+
+/**
+ Deallocate all memory associated with an event_base, and free the base.
+
+ Note that this function will not close any fds or free any memory passed
+ to event_set as the argument to callback.
+
+ @param eb an event_base to be freed
+ */
+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);
+/**
+ Redirect libevent's log messages.
+
+ @param cb a function taking two arguments: an integer severity between
+ _EVENT_LOG_DEBUG and _EVENT_LOG_ERR, and a string. If cb is NULL,
+ then the default log is used.
+ */
+void event_set_log_callback(event_log_cb cb);
+
+/**
+ Associate a different event base with an event.
+
+ @param eb the event base
+ @param ev the event
+ */
+int event_base_set(struct event_base *, struct event *);
+
+/**
+ event_loop() flags
+ */
+/*@{*/
+#define EVLOOP_ONCE 0x01 /**< Block at most once. */
+#define EVLOOP_NONBLOCK 0x02 /**< Do not block. */
+/*@}*/
+
+/**
+ Handle events.
+
+ This is a more flexible version of event_dispatch().
+
+ @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK
+ @return 0 if successful, -1 if an error occurred, or 1 if no events were
+ registered.
+ @see event_loopexit(), event_base_loop()
+*/
+int event_loop(int);
+
+/**
+ Handle events (threadsafe version).
+
+ This is a more flexible version of event_base_dispatch().
+
+ @param eb the event_base structure returned by event_init()
+ @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK
+ @return 0 if successful, -1 if an error occurred, or 1 if no events were
+ registered.
+ @see event_loopexit(), event_base_loop()
+ */
+int event_base_loop(struct event_base *, int);
+
+/**
+ Exit the event loop after the specified time.
+
+ The next event_loop() iteration after the given timer expires will
+ complete normally (handling all queued events) then exit without
+ blocking for events again.
+
+ Subsequent invocations of event_loop() will proceed normally.
+
+ @param tv the amount of time after which the loop should terminate.
+ @return 0 if successful, or -1 if an error occurred
+ @see event_loop(), event_base_loop(), event_base_loopexit()
+ */
+int event_loopexit(struct timeval *);
+
+
+/**
+ Exit the event loop after the specified time (threadsafe variant).
+
+ The next event_base_loop() iteration after the given timer expires will
+ complete normally (handling all queued events) then exit without
+ blocking for events again.
+
+ Subsequent invocations of event_base_loop() will proceed normally.
+
+ @param eb the event_base structure returned by event_init()
+ @param tv the amount of time after which the loop should terminate.
+ @return 0 if successful, or -1 if an error occurred
+ @see event_loopexit()
+ */
+int event_base_loopexit(struct event_base *, struct timeval *);
+
+/**
+ Abort the active event_loop() immediately.
+
+ event_loop() will abort the loop after the next event is completed;
+ event_loopbreak() is typically invoked from this event's callback.
+ This behavior is analogous to the "break;" statement.
+
+ Subsequent invocations of event_loop() will proceed normally.
+
+ @return 0 if successful, or -1 if an error occurred
+ @see event_base_loopbreak(), event_loopexit()
+ */
+int event_loopbreak(void);
+
+/**
+ Abort the active event_base_loop() immediately.
+
+ event_base_loop() will abort the loop after the next event is completed;
+ event_base_loopbreak() is typically invoked from this event's callback.
+ This behavior is analogous to the "break;" statement.
+
+ Subsequent invocations of event_loop() will proceed normally.
+
+ @param eb the event_base structure returned by event_init()
+ @return 0 if successful, or -1 if an error occurred
+ @see event_base_loopexit
+ */
+int event_base_loopbreak(struct event_base *);
+
+
+/**
+ Add a timer event.
+
+ @param ev the event struct
+ @param tv timeval struct
+ */
+#define evtimer_add(ev, tv) event_add(ev, tv)
+
+
+/**
+ Define a timer event.
+
+ @param ev event struct to be modified
+ @param cb callback function
+ @param arg argument that will be passed to the callback function
+ */
+#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
+
+
+/**
+ * Delete a timer event.
+ *
+ * @param ev the event struct to be disabled
+ */
+#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)
+
+/**
+ * Add a timeout event.
+ *
+ * @param ev the event struct to be disabled
+ * @param tv the timeout value, in seconds
+ */
+#define timeout_add(ev, tv) event_add(ev, tv)
+
+
+/**
+ * Define a timeout event.
+ *
+ * @param ev the event struct to be defined
+ * @param cb the callback to be invoked when the timeout expires
+ * @param arg the argument to be passed to the callback
+ */
+#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
+
+
+/**
+ * Disable a timeout event.
+ *
+ * @param ev the timeout event to be disabled
+ */
+#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)
+
+/**
+ Prepare an event structure to be added.
+
+ The function event_set() prepares the event structure ev to be used in
+ future calls to event_add() and event_del(). The event will be prepared to
+ call the function specified by the fn argument with an int argument
+ indicating the file descriptor, a short argument indicating the type of
+ event, and a void * argument given in the arg argument. The fd indicates
+ the file descriptor that should be monitored for events. The events can be
+ either EV_READ, EV_WRITE, or both. Indicating that an application can read
+ or write from the file descriptor respectively without blocking.
+
+ The function fn will be called with the file descriptor that triggered the
+ event and the type of event which will be either EV_TIMEOUT, EV_SIGNAL,
+ EV_READ, or EV_WRITE. The additional flag EV_PERSIST makes an event_add()
+ persistent until event_del() has been called.
+
+ @param ev an event struct to be modified
+ @param fd the file descriptor to be monitored
+ @param event desired events to monitor; can be EV_READ and/or EV_WRITE
+ @param fn callback function to be invoked when the event occurs
+ @param arg an argument to be passed to the callback function
+
+ @see event_add(), event_del(), event_once()
+
+ */
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+
+/**
+ Schedule a one-time event to occur.
+
+ The function event_once() is similar to event_set(). However, it schedules
+ a callback to be called exactly once and does not require the caller to
+ prepare an event structure.
+
+ @param fd a file descriptor to monitor
+ @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ |
+ EV_WRITE
+ @param callback callback function to be invoked when the event occurs
+ @param arg an argument to be passed to the callback function
+ @param timeout the maximum amount of time to wait for the event, or NULL
+ to wait forever
+ @return 0 if successful, or -1 if an error occurred
+ @see event_set()
+
+ */
+int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
+
+
+/**
+ Schedule a one-time event (threadsafe variant)
+
+ The function event_base_once() is similar to event_set(). However, it
+ schedules a callback to be called exactly once and does not require the
+ caller to prepare an event structure.
+
+ @param base an event_base returned by event_init()
+ @param fd a file descriptor to monitor
+ @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ |
+ EV_WRITE
+ @param callback callback function to be invoked when the event occurs
+ @param arg an argument to be passed to the callback function
+ @param timeout the maximum amount of time to wait for the event, or NULL
+ to wait forever
+ @return 0 if successful, or -1 if an error occurred
+ @see event_once()
+ */
+int event_base_once(struct event_base *, int, short, void (*)(int, short, void *), void *, struct timeval *);
+
+
+/**
+ Add an event to the set of monitored events.
+
+ The function event_add() schedules the execution of the ev event when the
+ event specified in event_set() occurs or in at least the time specified in
+ the tv. If tv is NULL, no timeout occurs and the function will only be
+ called if a matching event occurs on the file descriptor. The event in the
+ ev argument must be already initialized by event_set() and may not be used
+ in calls to event_set() until it has timed out or been removed with
+ event_del(). If the event in the ev argument already has a scheduled
+ timeout, the old timeout will be replaced by the new one.
+
+ @param ev an event struct initialized via event_set()
+ @param timeout the maximum amount of time to wait for the event, or NULL
+ to wait forever
+ @return 0 if successful, or -1 if an error occurred
+ @see event_del(), event_set()
+ */
+int event_add(struct event *, struct timeval *);
+
+
+/**
+ Remove an event from the set of monitored events.
+
+ The function event_del() will cancel the event in the argument ev. If the
+ event has already executed or has never been added the call will have no
+ effect.
+
+ @param ev an event struct to be removed from the working set
+ @return 0 if successful, or -1 if an error occurred
+ @see event_add()
+ */
+int event_del(struct event *);
+
+void event_active(struct event *, int, short);
+
+
+/**
+ Checks if a specific event is pending or scheduled.
+
+ @param ev an event struct previously passed to event_add()
+ @param event the requested event type; any of EV_TIMEOUT|EV_READ|
+ EV_WRITE|EV_SIGNAL
+ @param tv an alternate timeout (FIXME - is this true?)
+
+ @return 1 if the event is pending, or 0 if the event has not occurred
+
+ */
+int event_pending(struct event *, short, struct timeval *);
+
+
+/**
+ Test if an event structure has been initialized.
+
+ The event_initialized() macro can be used to check if an event has been
+ initialized.
+
+ @param ev an event structure to be tested
+ @return 1 if the structure has been initialized, or 0 if it has not been
+ initialized
+ */
+#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
+
+
+/**
+ Get the libevent version number.
+
+ @return a string containing the version number of libevent
+ */
+const char *event_get_version(void);
+
+
+/**
+ Get the kernel event notification mechanism used by libevent.
+
+ @return a string identifying the kernel event mechanism (kqueue, epoll, etc.)
+ */
+const char *event_get_method(void);
+
+
+/**
+ Set the number of different event priorities.
+
+ By default libevent schedules all active events with the same priority.
+ However, some time it is desirable to process some events with a higher
+ priority than others. For that reason, libevent supports strict priority
+ queues. Active events with a lower priority are always processed before
+ events with a higher priority.
+
+ The number of different priorities can be set initially with the
+ event_priority_init() function. This function should be called before the
+ first call to event_dispatch(). The event_priority_set() function can be
+ used to assign a priority to an event. By default, libevent assigns the
+ middle priority to all events unless their priority is explicitly set.
+
+ @param npriorities the maximum number of priorities
+ @return 0 if successful, or -1 if an error occurred
+ @see event_base_priority_init(), event_priority_set()
+
+ */
+int event_priority_init(int);
+
+
+/**
+ Set the number of different event priorities (threadsafe variant).
+
+ See the description of event_priority_init() for more information.
+
+ @param eb the event_base structure returned by event_init()
+ @param npriorities the maximum number of priorities
+ @return 0 if successful, or -1 if an error occurred
+ @see event_priority_init(), event_priority_set()
+ */
+int event_base_priority_init(struct event_base *, int);
+
+
+/**
+ Assign a priority to an event.
+
+ @param ev an event struct
+ @param priority the new priority to be assigned
+ @return 0 if successful, or -1 if an error occurred
+ @see event_priority_init()
+ */
+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 */
+};
+
+
+/**
+ Create a new bufferevent.
+
+ libevent provides an abstraction on top of the regular event callbacks.
+ This abstraction is called a buffered event. A buffered event provides
+ input and output buffers that get filled and drained automatically. The
+ user of a buffered event no longer deals directly with the I/O, but
+ instead is reading from input and writing to output buffers.
+
+ Once initialized, the bufferevent structure can be used repeatedly with
+ bufferevent_enable() and bufferevent_disable().
+
+ When read enabled the bufferevent will try to read from the file descriptor
+ and call the read callback. The write callback is executed whenever the
+ output buffer is drained below the write low watermark, which is 0 by
+ default.
+
+ If multiple bases are in use, bufferevent_base_set() must be called before
+ enabling the bufferevent for the first time.
+
+ @param fd the file descriptor from which data is read and written to.
+ This file descriptor is not allowed to be a pipe(2).
+ @param readcb callback to invoke when there is data to be read, or NULL if
+ no callback is desired
+ @param writecb callback to invoke when the file descriptor is ready for
+ writing, or NULL if no callback is desired
+ @param errorcb callback to invoke when there is an error on the file
+ descriptor
+ @param cbarg an argument that will be supplied to each of the callbacks
+ (readcb, writecb, and errorcb)
+ @return a pointer to a newly allocated bufferevent struct, or NULL if an
+ error occurred
+ @see bufferevent_base_set(), bufferevent_free()
+ */
+struct bufferevent *bufferevent_new(int fd,
+ evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
+
+
+/**
+ Assign a bufferevent to a specific event_base.
+
+ @param base an event_base returned by event_init()
+ @param bufev a bufferevent struct returned by bufferevent_new()
+ @return 0 if successful, or -1 if an error occurred
+ @see bufferevent_new()
+ */
+int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
+
+
+/**
+ Assign a priority to a bufferevent.
+
+ @param bufev a bufferevent struct
+ @param pri the priority to be assigned
+ @return 0 if successful, or -1 if an error occurred
+ */
+int bufferevent_priority_set(struct bufferevent *bufev, int pri);
+
+
+/**
+ Deallocate the storage associated with a bufferevent structure.
+
+ @param bufev the bufferevent structure to be freed.
+ */
+void bufferevent_free(struct bufferevent *bufev);
+
+
+/**
+ Write data to a bufferevent buffer.
+
+ The bufferevent_write() function can be used to write data to the file
+ descriptor. The data is appended to the output buffer and written to the
+ descriptor automatically as it becomes available for writing.
+
+ @param bufev the bufferevent to be written to
+ @param data a pointer to the data to be written
+ @param size the length of the data, in bytes
+ @return 0 if successful, or -1 if an error occurred
+ @see bufferevent_write_buffer()
+ */
+int bufferevent_write(struct bufferevent *bufev,
+ const void *data, size_t size);
+
+
+/**
+ Write data from an evbuffer to a bufferevent buffer. The evbuffer is
+ being drained as a result.
+
+ @param bufev the bufferevent to be written to
+ @param buf the evbuffer to be written
+ @return 0 if successful, or -1 if an error occurred
+ @see bufferevent_write()
+ */
+int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
+
+
+/**
+ Read data from a bufferevent buffer.
+
+ The bufferevent_read() function is used to read data from the input buffer.
+
+ @param bufev the bufferevent to be read from
+ @param data pointer to a buffer that will store the data
+ @param size the size of the data buffer, in bytes
+ @return the amount of data read, in bytes.
+ */
+size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
+
+/**
+ Enable a bufferevent.
+
+ @param bufev the bufferevent to be enabled
+ @param event any combination of EV_READ | EV_WRITE.
+ @return 0 if successful, or -1 if an error occurred
+ @see bufferevent_disable()
+ */
+int bufferevent_enable(struct bufferevent *bufev, short event);
+
+
+/**
+ Disable a bufferevent.
+
+ @param bufev the bufferevent to be disabled
+ @param event any combination of EV_READ | EV_WRITE.
+ @return 0 if successful, or -1 if an error occurred
+ @see bufferevent_enable()
+ */
+int bufferevent_disable(struct bufferevent *bufev, short event);
+
+
+/**
+ Set the read and write timeout for a buffered event.
+
+ @param bufev the bufferevent to be modified
+ @param timeout_read the read timeout
+ @param timeout_write the write timeout
+ */
+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
+
+
+/**
+ Allocate storage for a new evbuffer.
+
+ @return a pointer to a newly allocated evbuffer struct, or NULL if an error
+ occurred
+ */
+struct evbuffer *evbuffer_new(void);
+
+
+/**
+ Deallocate storage for an evbuffer.
+
+ @param pointer to the evbuffer to be freed
+ */
+void evbuffer_free(struct evbuffer *);
+
+
+/**
+ Expands the available space in an event buffer.
+
+ Expands the available space in the event buffer to at least datlen
+
+ @param buf the event buffer to be expanded
+ @param datlen the new minimum length requirement
+ @return 0 if successful, or -1 if an error occurred
+*/
+int evbuffer_expand(struct evbuffer *, size_t);
+
+
+/**
+ Append data to the end of an evbuffer.
+
+ @param buf the event buffer to be appended to
+ @param data pointer to the beginning of the data buffer
+ @param datlen the number of bytes to be copied from the data buffer
+ */
+int evbuffer_add(struct evbuffer *, const void *, size_t);
+
+
+
+/**
+ Read data from an event buffer and drain the bytes read.
+
+ @param buf the event buffer to be read from
+ @param data the destination buffer to store the result
+ @param datlen the maximum size of the destination buffer
+ @return the number of bytes read
+ */
+int evbuffer_remove(struct evbuffer *, void *, size_t);
+
+
+/**
+ * Read a single line from an event buffer.
+ *
+ * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
+ * The returned buffer needs to be freed by the caller.
+ *
+ * @param buffer the evbuffer to read from
+ * @return pointer to a single line, or NULL if an error occurred
+ */
+char *evbuffer_readline(struct evbuffer *);
+
+
+/**
+ Move data from one evbuffer into another evbuffer.
+
+ This is a destructive add. The data from one buffer moves into
+ the other buffer. The destination buffer is expanded as needed.
+
+ @param outbuf the output buffer
+ @param inbuf the input buffer
+ @return 0 if successful, or -1 if an error occurred
+ */
+int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
+
+
+/**
+ Append a formatted string to the end of an evbuffer.
+
+ @param buf the evbuffer that will be appended to
+ @param fmt a format string
+ @param ... arguments that will be passed to printf(3)
+ @return 0 if successful, or -1 if an error occurred
+ */
+int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 2, 3)))
+#endif
+;
+
+
+/**
+ Append a va_list formatted string to the end of an evbuffer.
+
+ @param buf the evbuffer that will be appended to
+ @param fmt a format string
+ @param ap a varargs va_list argument array that will be passed to vprintf(3)
+ @return 0 if successful, or -1 if an error occurred
+ */
+int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
+
+
+/**
+ Remove a specified number of bytes data from the beginning of an evbuffer.
+
+ @param buf the evbuffer to be drained
+ @param len the number of bytes to drain from the beginning of the buffer
+ @return 0 if successful, or -1 if an error occurred
+ */
+void evbuffer_drain(struct evbuffer *, size_t);
+
+
+/**
+ Write the contents of an evbuffer to a file descriptor.
+
+ The evbuffer will be drained after the bytes have been successfully written.
+
+ @param buffer the evbuffer to be written and drained
+ @param fd the file descriptor to be written to
+ @return the number of bytes written, or -1 if an error occurred
+ @see evbuffer_read()
+ */
+int evbuffer_write(struct evbuffer *, int);
+
+
+/**
+ Read from a file descriptor and store the result in an evbuffer.
+
+ @param buf the evbuffer to store the result
+ @param fd the file descriptor to read from
+ @param howmuch the number of bytes to be read
+ @return the number of bytes read, or -1 if an error occurred
+ @see evbuffer_write()
+ */
+int evbuffer_read(struct evbuffer *, int, int);
+
+
+/**
+ Find a string within an evbuffer.
+
+ @param buffer the evbuffer to be searched
+ @param what the string to be searched for
+ @param len the length of the search string
+ @return a pointer to the beginning of the search string, or NULL if the search failed.
+ */
+u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
+
+/**
+ Set a callback to invoke when the evbuffer is modified.
+
+ @param buffer the evbuffer to be monitored
+ @param cb the callback function to invoke when the evbuffer is modified
+ @param cbarg an argument to be provided to the callback function
+ */
+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, ev_uint32_t tag, const void *data,
+ ev_uint32_t len);
+
+/**
+ Encode an integer and store it in an evbuffer.
+
+ 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.
+
+ @param evbuf evbuffer to store the encoded number
+ @param number a 32-bit integer
+ */
+void encode_int(struct evbuffer *evbuf, ev_uint32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag,
+ ev_uint32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag,
+ const char *string);
+
+void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag,
+ struct timeval *tv);
+
+int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag,
+ struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength);
+int evtag_consume(struct evbuffer *evbuf);
+
+int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag,
+ ev_uint32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag,
+ void *data, size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag,
+ char **pstring);
+
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag,
+ struct timeval *ptv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVENT_H_ */
diff --git a/extra/libevent/event_tagging.c b/extra/libevent/event_tagging.c
new file mode 100644
index 00000000000..c67ea8a57af
--- /dev/null
+++ b/extra/libevent/event_tagging.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
+ * 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_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#else
+#include <sys/ioctl.h>
+#endif
+
+#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
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "event.h"
+#include "evutil.h"
+#include "log.h"
+
+int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf);
+int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag);
+int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf);
+
+static struct evbuffer *_buf; /* not thread safe */
+
+void
+evtag_init(void)
+{
+ 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, ev_uint32_t number)
+{
+ int off = 1, nibbles = 0;
+ ev_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);
+}
+
+/*
+ * Support variable length encoding of tags; we use the high bit in each
+ * octet as a continuation signal.
+ */
+
+int
+evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag)
+{
+ int bytes = 0;
+ ev_uint8_t data[5];
+
+ memset(data, 0, sizeof(data));
+ do {
+ ev_uint8_t lower = tag & 0x7f;
+ tag >>= 7;
+
+ if (tag)
+ lower |= 0x80;
+
+ data[bytes++] = lower;
+ } while (tag);
+
+ if (evbuf != NULL)
+ evbuffer_add(evbuf, data, bytes);
+
+ return (bytes);
+}
+
+static int
+decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain)
+{
+ ev_uint32_t number = 0;
+ ev_uint8_t *data = EVBUFFER_DATA(evbuf);
+ int len = EVBUFFER_LENGTH(evbuf);
+ int count = 0, shift = 0, done = 0;
+
+ while (count++ < len) {
+ ev_uint8_t lower = *data++;
+ number |= (lower & 0x7f) << shift;
+ shift += 7;
+
+ if (!(lower & 0x80)) {
+ done = 1;
+ break;
+ }
+ }
+
+ if (!done)
+ return (-1);
+
+ if (dodrain)
+ evbuffer_drain(evbuf, count);
+
+ if (ptag != NULL)
+ *ptag = number;
+
+ return (count);
+}
+
+int
+evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf)
+{
+ return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */));
+}
+
+/*
+ * 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, ev_uint32_t tag,
+ const void *data, ev_uint32_t len)
+{
+ evtag_encode_tag(evbuf, tag);
+ encode_int(evbuf, len);
+ evbuffer_add(evbuf, (void *)data, len);
+}
+
+/* Marshaling for integers */
+void
+evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ encode_int(_buf, integer);
+
+ evtag_encode_tag(evbuf, tag);
+ encode_int(evbuf, EVBUFFER_LENGTH(_buf));
+ evbuffer_add_buffer(evbuf, _buf);
+}
+
+void
+evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string)
+{
+ evtag_marshal(buf, tag, string, strlen(string));
+}
+
+void
+evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_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(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+{
+ ev_uint32_t number = 0;
+ ev_uint8_t *data = EVBUFFER_DATA(evbuf);
+ int len = EVBUFFER_LENGTH(evbuf);
+ int nibbles = 0;
+
+ if (!len)
+ return (-1);
+
+ nibbles = ((data[0] & 0xf0) >> 4) + 1;
+ if (nibbles > 8 || (nibbles >> 1) + 1 > len)
+ return (-1);
+ len = (nibbles >> 1) + 1;
+
+ while (nibbles > 0) {
+ number <<= 4;
+ if (nibbles & 0x1)
+ number |= data[nibbles >> 1] & 0x0f;
+ else
+ number |= (data[nibbles >> 1] & 0xf0) >> 4;
+ nibbles--;
+ }
+
+ if (dodrain)
+ evbuffer_drain(evbuf, len);
+
+ *pnumber = number;
+
+ return (len);
+}
+
+int
+evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf)
+{
+ return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
+}
+
+int
+evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag)
+{
+ return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */));
+}
+
+int
+evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength)
+{
+ struct evbuffer tmp;
+ int res, len;
+
+ len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
+ if (len == -1)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += len;
+ tmp.off -= len;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ *plength += res + len;
+
+ return (0);
+}
+
+int
+evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength)
+{
+ struct evbuffer tmp;
+ int res, len;
+
+ len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
+ if (len == -1)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += len;
+ tmp.off -= len;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+evtag_consume(struct evbuffer *evbuf)
+{
+ ev_uint32_t len;
+ if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1)
+ return (-1);
+ if (evtag_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, ev_uint32_t *ptag, struct evbuffer *dst)
+{
+ ev_uint32_t len;
+ ev_uint32_t integer;
+
+ if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1)
+ return (-1);
+ if (evtag_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);
+
+ return (len);
+}
+
+/* Marshaling for integers */
+
+int
+evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag,
+ ev_uint32_t *pinteger)
+{
+ ev_uint32_t tag;
+ ev_uint32_t len;
+ ev_uint32_t integer;
+
+ if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1)
+ return (-1);
+ if (need_tag != tag)
+ return (-1);
+ if (evtag_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 (evtag_decode_int(pinteger, _buf));
+}
+
+/* Unmarshal a fixed length tag */
+
+int
+evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data,
+ size_t len)
+{
+ ev_uint32_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, ev_uint32_t need_tag,
+ char **pstring)
+{
+ ev_uint32_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, ev_uint32_t need_tag,
+ struct timeval *ptv)
+{
+ ev_uint32_t tag;
+ ev_uint32_t integer;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (evtag_decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_sec = integer;
+ if (evtag_decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_usec = integer;
+
+ return (0);
+}
diff --git a/extra/libevent/evhttp.h b/extra/libevent/evhttp.h
new file mode 100644
index 00000000000..c20b1a7165e
--- /dev/null
+++ b/extra/libevent/evhttp.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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
+
+/** @file evhttp.h
+ *
+ * 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;
+
+/** Create a new HTTP server
+ *
+ * @param base (optional) the event base to receive the HTTP events
+ * @return a pointer to a newly initialized evhttp server structure
+ */
+struct evhttp *evhttp_new(struct event_base *base);
+
+/**
+ * Binds an HTTP server on the specified address and port.
+ *
+ * Can be called multiple times to bind the same http server
+ * to multiple different ports.
+ *
+ * @param http a pointer to an evhttp object
+ * @param address a string containing the IP address to listen(2) on
+ * @param port the port number to listen on
+ * @return a newly allocated evhttp struct
+ * @see evhttp_free()
+ */
+int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port);
+
+/**
+ * Free the previously created HTTP server.
+ *
+ * Works only if no requests are currently being served.
+ *
+ * @param http the evhttp server object to be freed
+ * @see evhttp_start()
+ */
+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 *);
+
+/**
+ * Set the timeout for an HTTP request.
+ *
+ * @param http an evhttp object
+ * @param timeout_in_secs the timeout, in seconds
+ */
+void evhttp_set_timeout(struct evhttp *, int timeout_in_secs);
+
+/* Request/Response functionality */
+
+/**
+ * Send an HTML error message to the client.
+ *
+ * @param req a request object
+ * @param error the HTTP error code
+ * @param reason a brief explanation of the error
+ */
+void evhttp_send_error(struct evhttp_request *req, int error,
+ const char *reason);
+
+/**
+ * Send an HTML reply to the client.
+ *
+ * @param req a request object
+ * @param code the HTTP response code to send
+ * @param reason a brief message to send with the response code
+ * @param databuf the body of the response
+ */
+void evhttp_send_reply(struct evhttp_request *req, int code,
+ const char *reason, struct evbuffer *databuf);
+
+/* 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 *);
+
+/**
+ * Start an HTTP server on the specified address and port
+ *
+ * DEPRECATED: it does not allow an event base to be specified
+ *
+ * @param address the address to which the HTTP server should be bound
+ * @param port the port number on which the HTTP server should listen
+ * @return an struct evhttp object
+ */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * 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 {
+#if defined(TAILQ_ENTRY)
+ TAILQ_ENTRY(evhttp_request) next;
+#else
+struct {
+ struct evhttp_request *tqe_next;
+ struct evhttp_request **tqe_prev;
+} next;
+#endif
+
+ /* 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 */
+ ev_int64_t 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 ip address from which http connections are made */
+void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
+ const char *address);
+
+/** 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 *);
+
+/**
+ * Associates an event base with the connection - can only be called
+ * on a freshly created connection object that has not been used yet.
+ */
+void evhttp_connection_set_base(struct evhttp_connection *evcon,
+ struct event_base *base);
+
+/** 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 */
+
+
+/**
+ Helper function to encode a URI.
+
+ The returned string must be freed by the caller.
+
+ @param uri an unencoded URI
+ @return a newly allocated URI-encoded string
+ */
+char *evhttp_encode_uri(const char *uri);
+
+
+/**
+ Helper function to decode a URI.
+
+ The returned string must be freed by the caller.
+
+ @param uri an encoded URI
+ @return a newly allocated unencoded URI
+ */
+char *evhttp_decode_uri(const char *uri);
+
+
+/**
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ * URI should already be decoded.
+ */
+void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+
+
+/**
+ * Escape HTML character entities in a string.
+ *
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ *
+ * @param html an unescaped HTML string
+ * @return an escaped HTML string
+ */
+char *evhttp_htmlescape(const char *html);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVHTTP_H_ */
diff --git a/extra/libevent/evport.c b/extra/libevent/evport.c
new file mode 100644
index 00000000000..c3250531d45
--- /dev/null
+++ b/extra/libevent/evport.c
@@ -0,0 +1,517 @@
+/*
+ * Submitted by David Pacheco (dp.spambait@gmail.com)
+ *
+ * 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 SUN MICROSYSTEMS, INC. ``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 SUN MICROSYSTEMS, INC. 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.
+ */
+
+/*
+ * Copyright (c) 2007 Sun Microsystems. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
+ * This implementation is loosely modeled after the one used for select(2) (in
+ * select.c).
+ *
+ * The outstanding events are tracked in a data structure called evport_data.
+ * Each entry in the ed_fds array corresponds to a file descriptor, and contains
+ * pointers to the read and write events that correspond to that fd. (That is,
+ * when the file is readable, the "read" event should handle it, etc.)
+ *
+ * evport_add and evport_del update this data structure. evport_dispatch uses it
+ * to determine where to callback when an event occurs (which it gets from
+ * port_getn).
+ *
+ * Helper functions are used: grow() grows the file descriptor array as
+ * necessary when large fd's come in. reassociate() takes care of maintaining
+ * the proper file-descriptor/event-port associations.
+ *
+ * As in the select(2) implementation, signals are handled by evsignal.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_EVENT_PORTS
+
+#include <sys/time.h>
+#include <assert.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <poll.h>
+#include <port.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+#include "evsignal.h"
+
+
+/*
+ * Default value for ed_nevents, which is the maximum file descriptor number we
+ * can handle. If an event comes in for a file descriptor F > nevents, we will
+ * grow the array of file descriptors, doubling its size.
+ */
+#define DEFAULT_NFDS 16
+
+
+/*
+ * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
+ * any particular call. You can speed things up by increasing this, but it will
+ * (obviously) require more memory.
+ */
+#define EVENTS_PER_GETN 8
+
+/*
+ * Per-file-descriptor information about what events we're subscribed to. These
+ * fields are NULL if no event is subscribed to either of them.
+ */
+
+struct fd_info {
+ struct event* fdi_revt; /* the event responsible for the "read" */
+ struct event* fdi_wevt; /* the event responsible for the "write" */
+};
+
+#define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL)
+#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL)
+#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
+#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
+ (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
+
+struct evport_data {
+ int ed_port; /* event port for system events */
+ int ed_nevents; /* number of allocated fdi's */
+ struct fd_info *ed_fds; /* allocated fdi table */
+ /* fdi's that we need to reassoc */
+ int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
+};
+
+static void* evport_init (struct event_base *);
+static int evport_add (void *, struct event *);
+static int evport_del (void *, struct event *);
+static int evport_dispatch (struct event_base *, void *, struct timeval *);
+static void evport_dealloc (struct event_base *, void *);
+
+const struct eventop evportops = {
+ "event ports",
+ evport_init,
+ evport_add,
+ evport_del,
+ evport_dispatch,
+ evport_dealloc,
+ 1 /* need reinit */
+};
+
+/*
+ * Initialize the event port implementation.
+ */
+
+static void*
+evport_init(struct event_base *base)
+{
+ struct evport_data *evpd;
+ int i;
+ /*
+ * Disable event ports when this environment variable is set
+ */
+ if (getenv("EVENT_NOEVPORT"))
+ return (NULL);
+
+ if (!(evpd = calloc(1, sizeof(struct evport_data))))
+ return (NULL);
+
+ if ((evpd->ed_port = port_create()) == -1) {
+ free(evpd);
+ return (NULL);
+ }
+
+ /*
+ * Initialize file descriptor structure
+ */
+ evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info));
+ if (evpd->ed_fds == NULL) {
+ close(evpd->ed_port);
+ free(evpd);
+ return (NULL);
+ }
+ evpd->ed_nevents = DEFAULT_NFDS;
+ for (i = 0; i < EVENTS_PER_GETN; i++)
+ evpd->ed_pending[i] = -1;
+
+ evsignal_init(base);
+
+ return (evpd);
+}
+
+#ifdef CHECK_INVARIANTS
+/*
+ * Checks some basic properties about the evport_data structure. Because it
+ * checks all file descriptors, this function can be expensive when the maximum
+ * file descriptor ever used is rather large.
+ */
+
+static void
+check_evportop(struct evport_data *evpd)
+{
+ assert(evpd);
+ assert(evpd->ed_nevents > 0);
+ assert(evpd->ed_port > 0);
+ assert(evpd->ed_fds > 0);
+
+ /*
+ * Verify the integrity of the fd_info struct as well as the events to
+ * which it points (at least, that they're valid references and correct
+ * for their position in the structure).
+ */
+ int i;
+ for (i = 0; i < evpd->ed_nevents; ++i) {
+ struct event *ev;
+ struct fd_info *fdi;
+
+ fdi = &evpd->ed_fds[i];
+ if ((ev = fdi->fdi_revt) != NULL) {
+ assert(ev->ev_fd == i);
+ }
+ if ((ev = fdi->fdi_wevt) != NULL) {
+ assert(ev->ev_fd == i);
+ }
+ }
+}
+
+/*
+ * Verifies very basic integrity of a given port_event.
+ */
+static void
+check_event(port_event_t* pevt)
+{
+ /*
+ * We've only registered for PORT_SOURCE_FD events. The only
+ * other thing we can legitimately receive is PORT_SOURCE_ALERT,
+ * but since we're not using port_alert either, we can assume
+ * PORT_SOURCE_FD.
+ */
+ assert(pevt->portev_source == PORT_SOURCE_FD);
+ assert(pevt->portev_user == NULL);
+}
+
+#else
+#define check_evportop(epop)
+#define check_event(pevt)
+#endif /* CHECK_INVARIANTS */
+
+/*
+ * Doubles the size of the allocated file descriptor array.
+ */
+static int
+grow(struct evport_data *epdp, int factor)
+{
+ struct fd_info *tmp;
+ int oldsize = epdp->ed_nevents;
+ int newsize = factor * oldsize;
+ assert(factor > 1);
+
+ check_evportop(epdp);
+
+ tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
+ if (NULL == tmp)
+ return -1;
+ epdp->ed_fds = tmp;
+ memset((char*) (epdp->ed_fds + oldsize), 0,
+ (newsize - oldsize)*sizeof(struct fd_info));
+ epdp->ed_nevents = newsize;
+
+ check_evportop(epdp);
+
+ return 0;
+}
+
+
+/*
+ * (Re)associates the given file descriptor with the event port. The OS events
+ * are specified (implicitly) from the fd_info struct.
+ */
+static int
+reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
+{
+ int sysevents = FDI_TO_SYSEVENTS(fdip);
+
+ if (sysevents != 0) {
+ if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
+ fd, sysevents, NULL) == -1) {
+ event_warn("port_associate");
+ return (-1);
+ }
+ }
+
+ check_evportop(epdp);
+
+ return (0);
+}
+
+/*
+ * Main event loop - polls port_getn for some number of events, and processes
+ * them.
+ */
+
+static int
+evport_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int i, res;
+ struct evport_data *epdp = arg;
+ port_event_t pevtlist[EVENTS_PER_GETN];
+
+ /*
+ * port_getn will block until it has at least nevents events. It will
+ * also return how many it's given us (which may be more than we asked
+ * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
+ * nevents.
+ */
+ int nevents = 1;
+
+ /*
+ * We have to convert a struct timeval to a struct timespec
+ * (only difference is nanoseconds vs. microseconds). If no time-based
+ * events are active, we should wait for I/O (and tv == NULL).
+ */
+ struct timespec ts;
+ struct timespec *ts_p = NULL;
+ if (tv != NULL) {
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+ ts_p = &ts;
+ }
+
+ /*
+ * Before doing anything else, we need to reassociate the events we hit
+ * last time which need reassociation. See comment at the end of the
+ * loop below.
+ */
+ for (i = 0; i < EVENTS_PER_GETN; ++i) {
+ struct fd_info *fdi = NULL;
+ if (epdp->ed_pending[i] != -1) {
+ fdi = &(epdp->ed_fds[epdp->ed_pending[i]]);
+ }
+
+ if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
+ int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd :
+ fdi->fdi_wevt->ev_fd;
+ reassociate(epdp, fdi, fd);
+ epdp->ed_pending[i] = -1;
+ }
+ }
+
+ if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
+ (unsigned int *) &nevents, ts_p)) == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ evsignal_process(base);
+ return (0);
+ } else if (errno == ETIME) {
+ if (nevents == 0)
+ return (0);
+ } else {
+ event_warn("port_getn");
+ return (-1);
+ }
+ } else if (base->sig.evsignal_caught) {
+ evsignal_process(base);
+ }
+
+ event_debug(("%s: port_getn reports %d events", __func__, nevents));
+
+ for (i = 0; i < nevents; ++i) {
+ struct event *ev;
+ struct fd_info *fdi;
+ port_event_t *pevt = &pevtlist[i];
+ int fd = (int) pevt->portev_object;
+
+ check_evportop(epdp);
+ check_event(pevt);
+ epdp->ed_pending[i] = fd;
+
+ /*
+ * Figure out what kind of event it was
+ * (because we have to pass this to the callback)
+ */
+ res = 0;
+ if (pevt->portev_events & POLLIN)
+ res |= EV_READ;
+ if (pevt->portev_events & POLLOUT)
+ res |= EV_WRITE;
+
+ assert(epdp->ed_nevents > fd);
+ fdi = &(epdp->ed_fds[fd]);
+
+ /*
+ * We now check for each of the possible events (READ
+ * or WRITE). Then, we activate the event (which will
+ * cause its callback to be executed).
+ */
+
+ if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
+ event_active(ev, res, 1);
+ }
+
+ if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
+ event_active(ev, res, 1);
+ }
+ } /* end of all events gotten */
+
+ check_evportop(epdp);
+
+ return (0);
+}
+
+
+/*
+ * Adds the given event (so that you will be notified when it happens via
+ * the callback function).
+ */
+
+static int
+evport_add(void *arg, struct event *ev)
+{
+ struct evport_data *evpd = arg;
+ struct fd_info *fdi;
+ int factor;
+
+ check_evportop(evpd);
+
+ /*
+ * Delegate, if it's not ours to handle.
+ */
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(ev));
+
+ /*
+ * If necessary, grow the file descriptor info table
+ */
+
+ factor = 1;
+ while (ev->ev_fd >= factor * evpd->ed_nevents)
+ factor *= 2;
+
+ if (factor > 1) {
+ if (-1 == grow(evpd, factor)) {
+ return (-1);
+ }
+ }
+
+ fdi = &evpd->ed_fds[ev->ev_fd];
+ if (ev->ev_events & EV_READ)
+ fdi->fdi_revt = ev;
+ if (ev->ev_events & EV_WRITE)
+ fdi->fdi_wevt = ev;
+
+ return reassociate(evpd, fdi, ev->ev_fd);
+}
+
+/*
+ * Removes the given event from the list of events to wait for.
+ */
+
+static int
+evport_del(void *arg, struct event *ev)
+{
+ struct evport_data *evpd = arg;
+ struct fd_info *fdi;
+ int i;
+ int associated = 1;
+
+ check_evportop(evpd);
+
+ /*
+ * Delegate, if it's not ours to handle
+ */
+ if (ev->ev_events & EV_SIGNAL) {
+ return (evsignal_del(ev));
+ }
+
+ if (evpd->ed_nevents < ev->ev_fd) {
+ return (-1);
+ }
+
+ for (i = 0; i < EVENTS_PER_GETN; ++i) {
+ if (evpd->ed_pending[i] == ev->ev_fd) {
+ associated = 0;
+ break;
+ }
+ }
+
+ fdi = &evpd->ed_fds[ev->ev_fd];
+ if (ev->ev_events & EV_READ)
+ fdi->fdi_revt = NULL;
+ if (ev->ev_events & EV_WRITE)
+ fdi->fdi_wevt = NULL;
+
+ if (associated) {
+ if (!FDI_HAS_EVENTS(fdi) &&
+ port_dissociate(evpd->ed_port, PORT_SOURCE_FD,
+ ev->ev_fd) == -1) {
+ /*
+ * Ignre EBADFD error the fd could have been closed
+ * before event_del() was called.
+ */
+ if (errno != EBADFD) {
+ event_warn("port_dissociate");
+ return (-1);
+ }
+ } else {
+ if (FDI_HAS_EVENTS(fdi)) {
+ return (reassociate(evpd, fdi, ev->ev_fd));
+ }
+ }
+ } else {
+ if (fdi->fdi_revt == NULL && fdi->fdi_wevt == NULL) {
+ evpd->ed_pending[i] = -1;
+ }
+ }
+ return 0;
+}
+
+
+static void
+evport_dealloc(struct event_base *base, void *arg)
+{
+ struct evport_data *evpd = arg;
+
+ evsignal_dealloc(base);
+
+ close(evpd->ed_port);
+
+ if (evpd->ed_fds)
+ free(evpd->ed_fds);
+ free(evpd);
+}
+
+#endif /* HAVE_EVENT_PORTS */
diff --git a/extra/libevent/evrpc-internal.h b/extra/libevent/evrpc-internal.h
new file mode 100644
index 00000000000..c900f959f97
--- /dev/null
+++ b/extra/libevent/evrpc-internal.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
+ * 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 _EVRPC_INTERNAL_H_
+#define _EVRPC_INTERNAL_H_
+
+#include "http-internal.h"
+
+struct evrpc;
+
+#define EVRPC_URI_PREFIX "/.rpc."
+
+struct evrpc_hook {
+ TAILQ_ENTRY(evrpc_hook) (next);
+
+ /* returns -1; if the rpc should be aborted, is allowed to rewrite */
+ int (*process)(struct evhttp_request *, struct evbuffer *, void *);
+ void *process_arg;
+};
+
+TAILQ_HEAD(evrpc_hook_list, evrpc_hook);
+
+/*
+ * this is shared between the base and the pool, so that we can reuse
+ * the hook adding functions; we alias both evrpc_pool and evrpc_base
+ * to this common structure.
+ */
+struct _evrpc_hooks {
+ /* hooks for processing outbound and inbound rpcs */
+ struct evrpc_hook_list in_hooks;
+ struct evrpc_hook_list out_hooks;
+};
+
+#define input_hooks common.in_hooks
+#define output_hooks common.out_hooks
+
+struct evrpc_base {
+ struct _evrpc_hooks common;
+
+ /* the HTTP server under which we register our RPC calls */
+ struct evhttp* http_server;
+
+ /* a list of all RPCs registered with us */
+ TAILQ_HEAD(evrpc_list, evrpc) registered_rpcs;
+};
+
+struct evrpc_req_generic;
+void evrpc_reqstate_free(struct evrpc_req_generic* rpc_state);
+
+/* A pool for holding evhttp_connection objects */
+struct evrpc_pool {
+ struct _evrpc_hooks common;
+
+ struct event_base *base;
+
+ struct evconq connections;
+
+ int timeout;
+
+ TAILQ_HEAD(evrpc_requestq, evrpc_request_wrapper) requests;
+};
+
+
+#endif /* _EVRPC_INTERNAL_H_ */
diff --git a/extra/libevent/evrpc.c b/extra/libevent/evrpc.c
new file mode 100644
index 00000000000..bfe3087ef8a
--- /dev/null
+++ b/extra/libevent/evrpc.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#include "misc.h"
+#endif
+
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#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 "event.h"
+#include "evrpc.h"
+#include "evrpc-internal.h"
+#include "evhttp.h"
+#include "evutil.h"
+#include "log.h"
+
+struct evrpc_base *
+evrpc_init(struct evhttp *http_server)
+{
+ struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base));
+ if (base == NULL)
+ return (NULL);
+
+ /* we rely on the tagging sub system */
+ evtag_init();
+
+ TAILQ_INIT(&base->registered_rpcs);
+ TAILQ_INIT(&base->input_hooks);
+ TAILQ_INIT(&base->output_hooks);
+ base->http_server = http_server;
+
+ return (base);
+}
+
+void
+evrpc_free(struct evrpc_base *base)
+{
+ struct evrpc *rpc;
+ struct evrpc_hook *hook;
+
+ while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) {
+ assert(evrpc_unregister_rpc(base, rpc->uri));
+ }
+ while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) {
+ assert(evrpc_remove_hook(base, INPUT, hook));
+ }
+ while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) {
+ assert(evrpc_remove_hook(base, OUTPUT, hook));
+ }
+ free(base);
+}
+
+void *
+evrpc_add_hook(void *vbase,
+ enum EVRPC_HOOK_TYPE hook_type,
+ int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
+ void *cb_arg)
+{
+ struct _evrpc_hooks *base = vbase;
+ struct evrpc_hook_list *head = NULL;
+ struct evrpc_hook *hook = NULL;
+ switch (hook_type) {
+ case INPUT:
+ head = &base->in_hooks;
+ break;
+ case OUTPUT:
+ head = &base->out_hooks;
+ break;
+ default:
+ assert(hook_type == INPUT || hook_type == OUTPUT);
+ }
+
+ hook = calloc(1, sizeof(struct evrpc_hook));
+ assert(hook != NULL);
+
+ hook->process = cb;
+ hook->process_arg = cb_arg;
+ TAILQ_INSERT_TAIL(head, hook, next);
+
+ return (hook);
+}
+
+static int
+evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle)
+{
+ struct evrpc_hook *hook = NULL;
+ TAILQ_FOREACH(hook, head, next) {
+ if (hook == handle) {
+ TAILQ_REMOVE(head, hook, next);
+ free(hook);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * remove the hook specified by the handle
+ */
+
+int
+evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle)
+{
+ struct _evrpc_hooks *base = vbase;
+ struct evrpc_hook_list *head = NULL;
+ switch (hook_type) {
+ case INPUT:
+ head = &base->in_hooks;
+ break;
+ case OUTPUT:
+ head = &base->out_hooks;
+ break;
+ default:
+ assert(hook_type == INPUT || hook_type == OUTPUT);
+ }
+
+ return (evrpc_remove_hook_internal(head, handle));
+}
+
+static int
+evrpc_process_hooks(struct evrpc_hook_list *head,
+ struct evhttp_request *req, struct evbuffer *evbuf)
+{
+ struct evrpc_hook *hook;
+ TAILQ_FOREACH(hook, head, next) {
+ if (hook->process(req, evbuf, hook->process_arg) == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void evrpc_pool_schedule(struct evrpc_pool *pool);
+static void evrpc_request_cb(struct evhttp_request *, void *);
+void evrpc_request_done(struct evrpc_req_generic*);
+
+/*
+ * Registers a new RPC with the HTTP server. The evrpc object is expected
+ * to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn
+ * calls this function.
+ */
+
+static char *
+evrpc_construct_uri(const char *uri)
+{
+ char *constructed_uri;
+ int constructed_uri_len;
+
+ constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1;
+ if ((constructed_uri = malloc(constructed_uri_len)) == NULL)
+ event_err(1, "%s: failed to register rpc at %s",
+ __func__, uri);
+ memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
+ memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri));
+ constructed_uri[constructed_uri_len - 1] = '\0';
+
+ return (constructed_uri);
+}
+
+int
+evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
+ void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
+{
+ char *constructed_uri = evrpc_construct_uri(rpc->uri);
+
+ rpc->base = base;
+ rpc->cb = cb;
+ rpc->cb_arg = cb_arg;
+
+ TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
+
+ evhttp_set_cb(base->http_server,
+ constructed_uri,
+ evrpc_request_cb,
+ rpc);
+
+ free(constructed_uri);
+
+ return (0);
+}
+
+int
+evrpc_unregister_rpc(struct evrpc_base *base, const char *name)
+{
+ char *registered_uri = NULL;
+ struct evrpc *rpc;
+
+ /* find the right rpc; linear search might be slow */
+ TAILQ_FOREACH(rpc, &base->registered_rpcs, next) {
+ if (strcmp(rpc->uri, name) == 0)
+ break;
+ }
+ if (rpc == NULL) {
+ /* We did not find an RPC with this name */
+ return (-1);
+ }
+ TAILQ_REMOVE(&base->registered_rpcs, rpc, next);
+
+ free((char *)rpc->uri);
+ free(rpc);
+
+ registered_uri = evrpc_construct_uri(name);
+
+ /* remove the http server callback */
+ assert(evhttp_del_cb(base->http_server, registered_uri) == 0);
+
+ free(registered_uri);
+ return (0);
+}
+
+static void
+evrpc_request_cb(struct evhttp_request *req, void *arg)
+{
+ struct evrpc *rpc = arg;
+ struct evrpc_req_generic *rpc_state = NULL;
+
+ /* let's verify the outside parameters */
+ if (req->type != EVHTTP_REQ_POST ||
+ EVBUFFER_LENGTH(req->input_buffer) <= 0)
+ goto error;
+
+ /*
+ * we might want to allow hooks to suspend the processing,
+ * but at the moment, we assume that they just act as simple
+ * filters.
+ */
+ if (evrpc_process_hooks(&rpc->base->input_hooks,
+ req, req->input_buffer) == -1)
+ goto error;
+
+ rpc_state = calloc(1, sizeof(struct evrpc_req_generic));
+ if (rpc_state == NULL)
+ goto error;
+
+ /* let's check that we can parse the request */
+ rpc_state->request = rpc->request_new();
+ if (rpc_state->request == NULL)
+ goto error;
+
+ rpc_state->rpc = rpc;
+
+ if (rpc->request_unmarshal(
+ rpc_state->request, req->input_buffer) == -1) {
+ /* we failed to parse the request; that's a bummer */
+ goto error;
+ }
+
+ /* at this point, we have a well formed request, prepare the reply */
+
+ rpc_state->reply = rpc->reply_new();
+ if (rpc_state->reply == NULL)
+ goto error;
+
+ rpc_state->http_req = req;
+ rpc_state->done = evrpc_request_done;
+
+ /* give the rpc to the user; they can deal with it */
+ rpc->cb(rpc_state, rpc->cb_arg);
+
+ return;
+
+error:
+ evrpc_reqstate_free(rpc_state);
+ evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
+ return;
+}
+
+void
+evrpc_reqstate_free(struct evrpc_req_generic* rpc_state)
+{
+ /* clean up all memory */
+ if (rpc_state != NULL) {
+ struct evrpc *rpc = rpc_state->rpc;
+
+ if (rpc_state->request != NULL)
+ rpc->request_free(rpc_state->request);
+ if (rpc_state->reply != NULL)
+ rpc->reply_free(rpc_state->reply);
+ free(rpc_state);
+ }
+}
+
+void
+evrpc_request_done(struct evrpc_req_generic* rpc_state)
+{
+ struct evhttp_request *req = rpc_state->http_req;
+ struct evrpc *rpc = rpc_state->rpc;
+ struct evbuffer* data = NULL;
+
+ if (rpc->reply_complete(rpc_state->reply) == -1) {
+ /* the reply was not completely filled in. error out */
+ goto error;
+ }
+
+ if ((data = evbuffer_new()) == NULL) {
+ /* out of memory */
+ goto error;
+ }
+
+ /* serialize the reply */
+ rpc->reply_marshal(data, rpc_state->reply);
+
+ /* do hook based tweaks to the request */
+ if (evrpc_process_hooks(&rpc->base->output_hooks,
+ req, data) == -1)
+ goto error;
+
+ /* on success, we are going to transmit marshaled binary data */
+ if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
+ evhttp_add_header(req->output_headers,
+ "Content-Type", "application/octet-stream");
+ }
+
+ evhttp_send_reply(req, HTTP_OK, "OK", data);
+
+ evbuffer_free(data);
+
+ evrpc_reqstate_free(rpc_state);
+
+ return;
+
+error:
+ if (data != NULL)
+ evbuffer_free(data);
+ evrpc_reqstate_free(rpc_state);
+ evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
+ return;
+}
+
+/* Client implementation of RPC site */
+
+static int evrpc_schedule_request(struct evhttp_connection *connection,
+ struct evrpc_request_wrapper *ctx);
+
+struct evrpc_pool *
+evrpc_pool_new(struct event_base *base)
+{
+ struct evrpc_pool *pool = calloc(1, sizeof(struct evrpc_pool));
+ if (pool == NULL)
+ return (NULL);
+
+ TAILQ_INIT(&pool->connections);
+ TAILQ_INIT(&pool->requests);
+
+ TAILQ_INIT(&pool->input_hooks);
+ TAILQ_INIT(&pool->output_hooks);
+
+ pool->base = base;
+ pool->timeout = -1;
+
+ return (pool);
+}
+
+static void
+evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
+{
+ free(request->name);
+ free(request);
+}
+
+void
+evrpc_pool_free(struct evrpc_pool *pool)
+{
+ struct evhttp_connection *connection;
+ struct evrpc_request_wrapper *request;
+ struct evrpc_hook *hook;
+
+ while ((request = TAILQ_FIRST(&pool->requests)) != NULL) {
+ TAILQ_REMOVE(&pool->requests, request, next);
+ /* if this gets more complicated we need our own function */
+ evrpc_request_wrapper_free(request);
+ }
+
+ while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) {
+ TAILQ_REMOVE(&pool->connections, connection, next);
+ evhttp_connection_free(connection);
+ }
+
+ while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) {
+ assert(evrpc_remove_hook(pool, INPUT, hook));
+ }
+
+ while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) {
+ assert(evrpc_remove_hook(pool, OUTPUT, hook));
+ }
+
+ free(pool);
+}
+
+/*
+ * Add a connection to the RPC pool. A request scheduled on the pool
+ * may use any available connection.
+ */
+
+void
+evrpc_pool_add_connection(struct evrpc_pool *pool,
+ struct evhttp_connection *connection) {
+ assert(connection->http_server == NULL);
+ TAILQ_INSERT_TAIL(&pool->connections, connection, next);
+
+ /*
+ * associate an event base with this connection
+ */
+ if (pool->base != NULL)
+ evhttp_connection_set_base(connection, pool->base);
+
+ /*
+ * unless a timeout was specifically set for a connection,
+ * the connection inherits the timeout from the pool.
+ */
+ if (connection->timeout == -1)
+ connection->timeout = pool->timeout;
+
+ /*
+ * if we have any requests pending, schedule them with the new
+ * connections.
+ */
+
+ if (TAILQ_FIRST(&pool->requests) != NULL) {
+ struct evrpc_request_wrapper *request =
+ TAILQ_FIRST(&pool->requests);
+ TAILQ_REMOVE(&pool->requests, request, next);
+ evrpc_schedule_request(connection, request);
+ }
+}
+
+void
+evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
+{
+ struct evhttp_connection *evcon;
+ TAILQ_FOREACH(evcon, &pool->connections, next) {
+ evcon->timeout = timeout_in_secs;
+ }
+ pool->timeout = timeout_in_secs;
+}
+
+
+static void evrpc_reply_done(struct evhttp_request *, void *);
+static void evrpc_request_timeout(int, short, void *);
+
+/*
+ * Finds a connection object associated with the pool that is currently
+ * idle and can be used to make a request.
+ */
+static struct evhttp_connection *
+evrpc_pool_find_connection(struct evrpc_pool *pool)
+{
+ struct evhttp_connection *connection;
+ TAILQ_FOREACH(connection, &pool->connections, next) {
+ if (TAILQ_FIRST(&connection->requests) == NULL)
+ return (connection);
+ }
+
+ return (NULL);
+}
+
+/*
+ * We assume that the ctx is no longer queued on the pool.
+ */
+static int
+evrpc_schedule_request(struct evhttp_connection *connection,
+ struct evrpc_request_wrapper *ctx)
+{
+ struct evhttp_request *req = NULL;
+ struct evrpc_pool *pool = ctx->pool;
+ struct evrpc_status status;
+ char *uri = NULL;
+ int res = 0;
+
+ if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL)
+ goto error;
+
+ /* serialize the request data into the output buffer */
+ ctx->request_marshal(req->output_buffer, ctx->request);
+
+ uri = evrpc_construct_uri(ctx->name);
+ if (uri == NULL)
+ goto error;
+
+ /* we need to know the connection that we might have to abort */
+ ctx->evcon = connection;
+
+ /* apply hooks to the outgoing request */
+ if (evrpc_process_hooks(&pool->output_hooks,
+ req, req->output_buffer) == -1)
+ goto error;
+
+ if (pool->timeout > 0) {
+ /*
+ * a timeout after which the whole rpc is going to be aborted.
+ */
+ struct timeval tv;
+ evutil_timerclear(&tv);
+ tv.tv_sec = pool->timeout;
+ evtimer_add(&ctx->ev_timeout, &tv);
+ }
+
+ /* start the request over the connection */
+ res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri);
+ free(uri);
+
+ if (res == -1)
+ goto error;
+
+ return (0);
+
+error:
+ memset(&status, 0, sizeof(status));
+ status.error = EVRPC_STATUS_ERR_UNSTARTED;
+ (*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
+ evrpc_request_wrapper_free(ctx);
+ return (-1);
+}
+
+int
+evrpc_make_request(struct evrpc_request_wrapper *ctx)
+{
+ struct evrpc_pool *pool = ctx->pool;
+
+ /* initialize the event structure for this rpc */
+ evtimer_set(&ctx->ev_timeout, evrpc_request_timeout, ctx);
+ if (pool->base != NULL)
+ event_base_set(pool->base, &ctx->ev_timeout);
+
+ /* we better have some available connections on the pool */
+ assert(TAILQ_FIRST(&pool->connections) != NULL);
+
+ /*
+ * if no connection is available, we queue the request on the pool,
+ * the next time a connection is empty, the rpc will be send on that.
+ */
+ TAILQ_INSERT_TAIL(&pool->requests, ctx, next);
+
+ evrpc_pool_schedule(pool);
+
+ return (0);
+}
+
+static void
+evrpc_reply_done(struct evhttp_request *req, void *arg)
+{
+ struct evrpc_request_wrapper *ctx = arg;
+ struct evrpc_pool *pool = ctx->pool;
+ struct evrpc_status status;
+ int res = -1;
+
+ /* cancel any timeout we might have scheduled */
+ event_del(&ctx->ev_timeout);
+
+ memset(&status, 0, sizeof(status));
+ status.http_req = req;
+
+ /* we need to get the reply now */
+ if (req != NULL) {
+ /* apply hooks to the incoming request */
+ if (evrpc_process_hooks(&pool->input_hooks,
+ req, req->input_buffer) == -1) {
+ status.error = EVRPC_STATUS_ERR_HOOKABORTED;
+ res = -1;
+ } else {
+ res = ctx->reply_unmarshal(ctx->reply,
+ req->input_buffer);
+ if (res == -1) {
+ status.error = EVRPC_STATUS_ERR_BADPAYLOAD;
+ }
+ }
+ } else {
+ status.error = EVRPC_STATUS_ERR_TIMEOUT;
+ }
+
+ if (res == -1) {
+ /* clear everything that we might have written previously */
+ ctx->reply_clear(ctx->reply);
+ }
+
+ (*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
+
+ evrpc_request_wrapper_free(ctx);
+
+ /* the http layer owns the request structure */
+
+ /* see if we can schedule another request */
+ evrpc_pool_schedule(pool);
+}
+
+static void
+evrpc_pool_schedule(struct evrpc_pool *pool)
+{
+ struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests);
+ struct evhttp_connection *evcon;
+
+ /* if no requests are pending, we have no work */
+ if (ctx == NULL)
+ return;
+
+ if ((evcon = evrpc_pool_find_connection(pool)) != NULL) {
+ TAILQ_REMOVE(&pool->requests, ctx, next);
+ evrpc_schedule_request(evcon, ctx);
+ }
+}
+
+static void
+evrpc_request_timeout(int fd, short what, void *arg)
+{
+ struct evrpc_request_wrapper *ctx = arg;
+ struct evhttp_connection *evcon = ctx->evcon;
+ assert(evcon != NULL);
+
+ evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+}
diff --git a/extra/libevent/evrpc.h b/extra/libevent/evrpc.h
new file mode 100644
index 00000000000..45f684ac5ac
--- /dev/null
+++ b/extra/libevent/evrpc.h
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
+ * 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 _EVRPC_H_
+#define _EVRPC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file evrpc.h
+ *
+ * This header files provides basic support for an RPC server and client.
+ *
+ * To support RPCs in a server, every supported RPC command needs to be
+ * defined and registered.
+ *
+ * EVRPC_HEADER(SendCommand, Request, Reply);
+ *
+ * SendCommand is the name of the RPC command.
+ * Request is the name of a structure generated by event_rpcgen.py.
+ * It contains all parameters relating to the SendCommand RPC. The
+ * server needs to fill in the Reply structure.
+ * Reply is the name of a structure generated by event_rpcgen.py. It
+ * contains the answer to the RPC.
+ *
+ * To register an RPC with an HTTP server, you need to first create an RPC
+ * base with:
+ *
+ * struct evrpc_base *base = evrpc_init(http);
+ *
+ * A specific RPC can then be registered with
+ *
+ * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg);
+ *
+ * when the server receives an appropriately formatted RPC, the user callback
+ * is invokved. The callback needs to fill in the reply structure.
+ *
+ * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg);
+ *
+ * To send the reply, call EVRPC_REQUEST_DONE(rpc);
+ *
+ * See the regression test for an example.
+ */
+
+struct evbuffer;
+struct event_base;
+struct evrpc_req_generic;
+
+/* Encapsulates a request */
+struct evrpc {
+ TAILQ_ENTRY(evrpc) next;
+
+ /* the URI at which the request handler lives */
+ const char* uri;
+
+ /* creates a new request structure */
+ void *(*request_new)(void);
+
+ /* frees the request structure */
+ void (*request_free)(void *);
+
+ /* unmarshals the buffer into the proper request structure */
+ int (*request_unmarshal)(void *, struct evbuffer *);
+
+ /* creates a new reply structure */
+ void *(*reply_new)(void);
+
+ /* creates a new reply structure */
+ void (*reply_free)(void *);
+
+ /* verifies that the reply is valid */
+ int (*reply_complete)(void *);
+
+ /* marshals the reply into a buffer */
+ void (*reply_marshal)(struct evbuffer*, void *);
+
+ /* the callback invoked for each received rpc */
+ void (*cb)(struct evrpc_req_generic *, void *);
+ void *cb_arg;
+
+ /* reference for further configuration */
+ struct evrpc_base *base;
+};
+
+/** The type of a specific RPC Message
+ *
+ * @param rpcname the name of the RPC message
+ */
+#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname
+
+struct evhttp_request;
+struct evrpc_status;
+
+/* We alias the RPC specific structs to this voided one */
+struct evrpc_req_generic {
+ /* the unmarshaled request object */
+ void *request;
+
+ /* the empty reply object that needs to be filled in */
+ void *reply;
+
+ /*
+ * the static structure for this rpc; that can be used to
+ * automatically unmarshal and marshal the http buffers.
+ */
+ struct evrpc *rpc;
+
+ /*
+ * the http request structure on which we need to answer.
+ */
+ struct evhttp_request* http_req;
+
+ /*
+ * callback to reply and finish answering this rpc
+ */
+ void (*done)(struct evrpc_req_generic* rpc);
+};
+
+/** Creates the definitions and prototypes for an RPC
+ *
+ * You need to use EVRPC_HEADER to create structures and function prototypes
+ * needed by the server and client implementation. The structures have to be
+ * defined in an .rpc file and converted to source code via event_rpcgen.py
+ *
+ * @param rpcname the name of the RPC
+ * @param reqstruct the name of the RPC request structure
+ * @param replystruct the name of the RPC reply structure
+ * @see EVRPC_GENERATE()
+ */
+#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
+EVRPC_STRUCT(rpcname) { \
+ struct reqstruct* request; \
+ struct rplystruct* reply; \
+ struct evrpc* rpc; \
+ struct evhttp_request* http_req; \
+ void (*done)(struct evrpc_status *, \
+ struct evrpc* rpc, void *request, void *reply); \
+}; \
+int evrpc_send_request_##rpcname(struct evrpc_pool *, \
+ struct reqstruct *, struct rplystruct *, \
+ void (*)(struct evrpc_status *, \
+ struct reqstruct *, struct rplystruct *, void *cbarg), \
+ void *);
+
+/** Generates the code for receiving and sending an RPC message
+ *
+ * EVRPC_GENERATE is used to create the code corresponding to sending
+ * and receiving a particular RPC message
+ *
+ * @param rpcname the name of the RPC
+ * @param reqstruct the name of the RPC request structure
+ * @param replystruct the name of the RPC reply structure
+ * @see EVRPC_HEADER()
+ */
+#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \
+int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \
+ struct reqstruct *request, struct rplystruct *reply, \
+ void (*cb)(struct evrpc_status *, \
+ struct reqstruct *, struct rplystruct *, void *cbarg), \
+ void *cbarg) { \
+ struct evrpc_status status; \
+ struct evrpc_request_wrapper *ctx; \
+ ctx = (struct evrpc_request_wrapper *) \
+ malloc(sizeof(struct evrpc_request_wrapper)); \
+ if (ctx == NULL) \
+ goto error; \
+ ctx->pool = pool; \
+ ctx->evcon = NULL; \
+ ctx->name = strdup(#rpcname); \
+ if (ctx->name == NULL) { \
+ free(ctx); \
+ goto error; \
+ } \
+ ctx->cb = (void (*)(struct evrpc_status *, \
+ void *, void *, void *))cb; \
+ ctx->cb_arg = cbarg; \
+ ctx->request = (void *)request; \
+ ctx->reply = (void *)reply; \
+ ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \
+ ctx->reply_clear = (void (*)(void *))rplystruct##_clear; \
+ ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \
+ return (evrpc_make_request(ctx)); \
+error: \
+ memset(&status, 0, sizeof(status)); \
+ status.error = EVRPC_STATUS_ERR_UNSTARTED; \
+ (*(cb))(&status, request, reply, cbarg); \
+ return (-1); \
+}
+
+/** Provides access to the HTTP request object underlying an RPC
+ *
+ * Access to the underlying http object; can be used to look at headers or
+ * for getting the remote ip address
+ *
+ * @param rpc_req the rpc request structure provided to the server callback
+ * @return an struct evhttp_request object that can be inspected for
+ * HTTP headers or sender information.
+ */
+#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req
+
+/** Creates the reply to an RPC request
+ *
+ * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
+ * to have been filled in. The request and reply pointers become invalid
+ * after this call has finished.
+ *
+ * @param rpc_req the rpc request structure provided to the server callback
+ */
+#define EVRPC_REQUEST_DONE(rpc_req) do { \
+ struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \
+ _req->done(_req); \
+} while (0)
+
+
+/* Takes a request object and fills it in with the right magic */
+#define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \
+ do { \
+ (rpc)->uri = strdup(#name); \
+ if ((rpc)->uri == NULL) { \
+ fprintf(stderr, "failed to register object\n"); \
+ exit(1); \
+ } \
+ (rpc)->request_new = (void *(*)(void))request##_new; \
+ (rpc)->request_free = (void (*)(void *))request##_free; \
+ (rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \
+ (rpc)->reply_new = (void *(*)(void))reply##_new; \
+ (rpc)->reply_free = (void (*)(void *))reply##_free; \
+ (rpc)->reply_complete = (int (*)(void *))reply##_complete; \
+ (rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \
+ } while (0)
+
+struct evrpc_base;
+struct evhttp;
+
+/* functions to start up the rpc system */
+
+/** Creates a new rpc base from which RPC requests can be received
+ *
+ * @param server a pointer to an existing HTTP server
+ * @return a newly allocated evrpc_base struct
+ * @see evrpc_free()
+ */
+struct evrpc_base *evrpc_init(struct evhttp *server);
+
+/**
+ * Frees the evrpc base
+ *
+ * For now, you are responsible for making sure that no rpcs are ongoing.
+ *
+ * @param base the evrpc_base object to be freed
+ * @see evrpc_init
+ */
+void evrpc_free(struct evrpc_base *base);
+
+/** register RPCs with the HTTP Server
+ *
+ * registers a new RPC with the HTTP server, each RPC needs to have
+ * a unique name under which it can be identified.
+ *
+ * @param base the evrpc_base structure in which the RPC should be
+ * registered.
+ * @param name the name of the RPC
+ * @param request the name of the RPC request structure
+ * @param reply the name of the RPC reply structure
+ * @param callback the callback that should be invoked when the RPC
+ * is received. The callback has the following prototype
+ * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg)
+ * @param cbarg an additional parameter that can be passed to the callback.
+ * The parameter can be used to carry around state.
+ */
+#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \
+ do { \
+ struct evrpc* rpc = (struct evrpc *)calloc(1, sizeof(struct evrpc)); \
+ EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \
+ evrpc_register_rpc(base, rpc, \
+ (void (*)(struct evrpc_req_generic*, void *))callback, cbarg); \
+ } while (0)
+
+int evrpc_register_rpc(struct evrpc_base *, struct evrpc *,
+ void (*)(struct evrpc_req_generic*, void *), void *);
+
+/**
+ * Unregisters an already registered RPC
+ *
+ * @param base the evrpc_base object from which to unregister an RPC
+ * @param name the name of the rpc to unregister
+ * @return -1 on error or 0 when successful.
+ * @see EVRPC_REGISTER()
+ */
+#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc(base, #name)
+
+int evrpc_unregister_rpc(struct evrpc_base *base, const char *name);
+
+/*
+ * Client-side RPC support
+ */
+
+struct evrpc_pool;
+struct evhttp_connection;
+
+/**
+ * provides information about the completed RPC request.
+ */
+struct evrpc_status {
+#define EVRPC_STATUS_ERR_NONE 0
+#define EVRPC_STATUS_ERR_TIMEOUT 1
+#define EVRPC_STATUS_ERR_BADPAYLOAD 2
+#define EVRPC_STATUS_ERR_UNSTARTED 3
+#define EVRPC_STATUS_ERR_HOOKABORTED 4
+ int error;
+
+ /* for looking at headers or other information */
+ struct evhttp_request *http_req;
+};
+
+struct evrpc_request_wrapper {
+ TAILQ_ENTRY(evrpc_request_wrapper) next;
+
+ /* pool on which this rpc request is being made */
+ struct evrpc_pool *pool;
+
+ /* connection on which the request is being sent */
+ struct evhttp_connection *evcon;
+
+ /* event for implementing request timeouts */
+ struct event ev_timeout;
+
+ /* the name of the rpc */
+ char *name;
+
+ /* callback */
+ void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg);
+ void *cb_arg;
+
+ void *request;
+ void *reply;
+
+ /* unmarshals the buffer into the proper request structure */
+ void (*request_marshal)(struct evbuffer *, void *);
+
+ /* removes all stored state in the reply */
+ void (*reply_clear)(void *);
+
+ /* marshals the reply into a buffer */
+ int (*reply_unmarshal)(void *, struct evbuffer*);
+};
+
+/** launches an RPC and sends it to the server
+ *
+ * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server.
+ *
+ * @param name the name of the RPC
+ * @param pool the evrpc_pool that contains the connection objects over which
+ * the request should be sent.
+ * @param request a pointer to the RPC request structure - it contains the
+ * data to be sent to the server.
+ * @param reply a pointer to the RPC reply structure. It is going to be filled
+ * if the request was answered successfully
+ * @param cb the callback to invoke when the RPC request has been answered
+ * @param cbarg an additional argument to be passed to the client
+ * @return 0 on success, -1 on failure
+ */
+#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \
+ evrpc_send_request_##name(pool, request, reply, cb, cbarg)
+
+int evrpc_make_request(struct evrpc_request_wrapper *);
+
+/** creates an rpc connection pool
+ *
+ * a pool has a number of connections associated with it.
+ * rpc requests are always made via a pool.
+ *
+ * @param base a pointer to an struct event_based object; can be left NULL
+ * in singled-threaded applications
+ * @return a newly allocated struct evrpc_pool object
+ * @see evrpc_pool_free()
+ */
+struct evrpc_pool *evrpc_pool_new(struct event_base *base);
+/** frees an rpc connection pool
+ *
+ * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new()
+ * @see evrpc_pool_new()
+ */
+void evrpc_pool_free(struct evrpc_pool *pool);
+/*
+ * adds a connection over which rpc can be dispatched. the connection
+ * object must have been newly created.
+ */
+void evrpc_pool_add_connection(struct evrpc_pool *,
+ struct evhttp_connection *);
+
+/**
+ * Sets the timeout in secs after which a request has to complete. The
+ * RPC is completely aborted if it does not complete by then. Setting
+ * the timeout to 0 means that it never timeouts and can be used to
+ * implement callback type RPCs.
+ *
+ * Any connection already in the pool will be updated with the new
+ * timeout. Connections added to the pool after set_timeout has be
+ * called receive the pool timeout only if no timeout has been set
+ * for the connection itself.
+ *
+ * @param pool a pointer to a struct evrpc_pool object
+ * @param timeout_in_secs the number of seconds after which a request should
+ * timeout and a failure be returned to the callback.
+ */
+void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs);
+
+/**
+ * Hooks for changing the input and output of RPCs; this can be used to
+ * implement compression, authentication, encryption, ...
+ */
+
+enum EVRPC_HOOK_TYPE {
+ INPUT, /**< apply the function to an input hook */
+ OUTPUT /**< apply the function to an output hook */
+};
+
+/** adds a processing hook to either an rpc base or rpc pool
+ *
+ * If a hook returns -1, the processing is aborted.
+ *
+ * The add functions return handles that can be used for removing hooks.
+ *
+ * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
+ * @param hook_type either INPUT or OUTPUT
+ * @param cb the callback to call when the hook is activated
+ * @param cb_arg an additional argument for the callback
+ * @return a handle to the hook so it can be removed later
+ * @see evrpc_remove_hook()
+ */
+void *evrpc_add_hook(void *vbase,
+ enum EVRPC_HOOK_TYPE hook_type,
+ int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
+ void *cb_arg);
+
+/** removes a previously added hook
+ *
+ * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
+ * @param hook_type either INPUT or OUTPUT
+ * @param handle a handle returned by evrpc_add_hook()
+ * @return 1 on success or 0 on failure
+ * @see evrpc_add_hook()
+ */
+int evrpc_remove_hook(void *vbase,
+ enum EVRPC_HOOK_TYPE hook_type,
+ void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVRPC_H_ */
diff --git a/extra/libevent/evsignal.h b/extra/libevent/evsignal.h
new file mode 100644
index 00000000000..0d1e83140bd
--- /dev/null
+++ b/extra/libevent/evsignal.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * 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 _EVSIGNAL_H_
+#define _EVSIGNAL_H_
+
+typedef void (*ev_sighandler_t)(int);
+
+struct evsignal_info {
+ struct event_list signalqueue;
+ struct event ev_signal;
+ int ev_signal_pair[2];
+ int ev_signal_added;
+ volatile sig_atomic_t evsignal_caught;
+ sig_atomic_t evsigcaught[NSIG];
+#ifdef HAVE_SIGACTION
+ struct sigaction **sh_old;
+#else
+ ev_sighandler_t **sh_old;
+#endif
+ int sh_old_max;
+};
+void evsignal_init(struct event_base *);
+void evsignal_process(struct event_base *);
+int evsignal_add(struct event *);
+int evsignal_del(struct event *);
+void evsignal_dealloc(struct event_base *);
+
+#endif /* _EVSIGNAL_H_ */
diff --git a/extra/libevent/evutil.c b/extra/libevent/evutil.c
new file mode 100644
index 00000000000..1202d354fe4
--- /dev/null
+++ b/extra/libevent/evutil.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2007 Niels Provos <provos@citi.umich.edu>
+ * 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 <winsock2.h>
+#include "misc.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <errno.h>
+
+#include "evutil.h"
+#include "log.h"
+
+int
+evutil_socketpair(int family, int type, int protocol, int fd[2])
+{
+#ifndef WIN32
+ return socketpair(family, type, protocol, fd);
+#else
+ /* This code is originally from Tor. Used with permission. */
+
+ /* This socketpair does not work when localhost is down. So
+ * it's really not the same thing at all. But it's close enough
+ * for now, and really, when localhost is down sometimes, we
+ * have other problems too.
+ */
+ int listener = -1;
+ int connector = -1;
+ int acceptor = -1;
+ struct sockaddr_in listen_addr;
+ struct sockaddr_in connect_addr;
+ int size;
+ int saved_errno = -1;
+
+ if (protocol
+#ifdef AF_UNIX
+ || family != AF_UNIX
+#endif
+ ) {
+ EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT);
+ return -1;
+ }
+ if (!fd) {
+ EVUTIL_SET_SOCKET_ERROR(WSAEINVAL);
+ return -1;
+ }
+
+ listener = (int)socket(AF_INET, type, 0);
+ if (listener < 0)
+ return -1;
+ memset(&listen_addr, 0, sizeof(listen_addr));
+ listen_addr.sin_family = AF_INET;
+ listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ listen_addr.sin_port = 0; /* kernel chooses port. */
+ if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
+ == -1)
+ goto tidy_up_and_fail;
+ if (listen(listener, 1) == -1)
+ goto tidy_up_and_fail;
+
+ connector = (int)socket(AF_INET, type, 0);
+ if (connector < 0)
+ goto tidy_up_and_fail;
+ /* We want to find out the port number to connect to. */
+ size = sizeof(connect_addr);
+ if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+ goto tidy_up_and_fail;
+ if (size != sizeof (connect_addr))
+ goto abort_tidy_up_and_fail;
+ if (connect(connector, (struct sockaddr *) &connect_addr,
+ sizeof(connect_addr)) == -1)
+ goto tidy_up_and_fail;
+
+ size = sizeof(listen_addr);
+ acceptor = (int)accept(listener, (struct sockaddr *) &listen_addr, &size);
+ if (acceptor < 0)
+ goto tidy_up_and_fail;
+ if (size != sizeof(listen_addr))
+ goto abort_tidy_up_and_fail;
+ EVUTIL_CLOSESOCKET(listener);
+ /* Now check we are talking to ourself by matching port and host on the
+ two sockets. */
+ if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+ goto tidy_up_and_fail;
+ if (size != sizeof (connect_addr)
+ || listen_addr.sin_family != connect_addr.sin_family
+ || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
+ || listen_addr.sin_port != connect_addr.sin_port)
+ goto abort_tidy_up_and_fail;
+ fd[0] = connector;
+ fd[1] = acceptor;
+
+ return 0;
+
+ abort_tidy_up_and_fail:
+ saved_errno = WSAECONNABORTED;
+ tidy_up_and_fail:
+ if (saved_errno < 0)
+ saved_errno = WSAGetLastError();
+ if (listener != -1)
+ EVUTIL_CLOSESOCKET(listener);
+ if (connector != -1)
+ EVUTIL_CLOSESOCKET(connector);
+ if (acceptor != -1)
+ EVUTIL_CLOSESOCKET(acceptor);
+
+ EVUTIL_SET_SOCKET_ERROR(saved_errno);
+ return -1;
+#endif
+}
+
+int
+evutil_make_socket_nonblocking(int fd)
+{
+#ifdef WIN32
+ {
+ unsigned long nonblocking = 1;
+ ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking);
+ }
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ event_warn("fcntl(O_NONBLOCK)");
+ return -1;
+}
+#endif
+ return 0;
+}
+
+ev_int64_t
+evutil_strtoll(const char *s, char **endptr, int base)
+{
+#ifdef HAVE_STRTOLL
+ return (ev_int64_t)strtoll(s, endptr, base);
+#elif SIZEOF_LONG == 8
+ return (ev_int64_t)strtol(s, endptr, base);
+#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300
+ /* XXXX on old versions of MS APIs, we only support base
+ * 10. */
+ ev_int64_t r;
+ if (base != 10)
+ return 0;
+ r = (ev_int64_t) _atoi64(s);
+ while (isspace(*s))
+ ++s;
+ while (isdigit(*s))
+ ++s;
+ if (endptr)
+ *endptr = (char*) s;
+ return r;
+#elif defined(WIN32)
+ return (ev_int64_t) _strtoi64(s, endptr, base);
+#else
+#error "I don't know how to parse 64-bit integers."
+#endif
+}
diff --git a/extra/libevent/evutil.h b/extra/libevent/evutil.h
new file mode 100644
index 00000000000..2cfcacb2e0e
--- /dev/null
+++ b/extra/libevent/evutil.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2007 Niels Provos <provos@citi.umich.edu>
+ * 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 _EVUTIL_H_
+#define _EVUTIL_H_
+
+/** @file evutil.h
+
+ Common convenience functions for cross-platform portability and
+ related socket manipulations.
+
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <event-config.h>
+#ifdef _EVENT_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef _EVENT_HAVE_STDINT_H
+#include <stdint.h>
+#elif defined(_EVENT_HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+#ifdef _EVENT_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef _EVENT_HAVE_UINT64_T
+#define ev_uint64_t uint64_t
+#define ev_int64_t int64_t
+#elif defined(WIN32)
+#define ev_uint64_t unsigned __int64
+#define ev_int64_t __int64
+#elif _EVENT_SIZEOF_LONG_LONG == 8
+#define ev_uint64_t unsigned long long
+#define ev_int64_t long long
+#elif _EVENT_SIZEOF_LONG == 8
+#define ev_uint64_t unsigned long
+#define ev_int64_t long
+#else
+#error "No way to define ev_uint64_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT32_T
+#define ev_uint32_t uint32_t
+#elif defined(WIN32)
+#define ev_uint32_t unsigned int
+#elif _EVENT_SIZEOF_LONG == 4
+#define ev_uint32_t unsigned long
+#elif _EVENT_SIZEOF_INT == 4
+#define ev_uint32_t unsigned int
+#else
+#error "No way to define ev_uint32_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT16_T
+#define ev_uint16_t uint16_t
+#elif defined(WIN32)
+#define ev_uint16_t unsigned short
+#elif _EVENT_SIZEOF_INT == 2
+#define ev_uint16_t unsigned int
+#elif _EVENT_SIZEOF_SHORT == 2
+#define ev_uint16_t unsigned short
+#else
+#error "No way to define ev_uint16_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT8_T
+#define ev_uint8_t uint8_t
+#else
+#define ev_uint8_t unsigned char
+#endif
+
+int evutil_socketpair(int d, int type, int protocol, int sv[2]);
+int evutil_make_socket_nonblocking(int sock);
+#ifdef WIN32
+#define EVUTIL_CLOSESOCKET(s) closesocket(s)
+#else
+#define EVUTIL_CLOSESOCKET(s) close(s)
+#endif
+
+#ifdef WIN32
+#define EVUTIL_SOCKET_ERROR() WSAGetLastError()
+#define EVUTIL_SET_SOCKET_ERROR(errcode) \
+ do { WSASetLastError(errcode); } while (0)
+#else
+#define EVUTIL_SOCKET_ERROR() (errno)
+#define EVUTIL_SET_SOCKET_ERROR(errcode) \
+ do { errno = (errcode); } while (0)
+#endif
+
+/*
+ * Manipulation functions for struct timeval
+ */
+#ifdef _EVENT_HAVE_TIMERADD
+#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp))
+#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp))
+#else
+#define evutil_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 evutil_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)
+#endif /* !_EVENT_HAVE_HAVE_TIMERADD */
+
+#ifdef _EVENT_HAVE_TIMERCLEAR
+#define evutil_timerclear(tvp) timerclear(tvp)
+#else
+#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#endif
+
+#ifdef _EVENT_HAVE_TIMERCMP
+#define evutil_timercmp(tvp, uvp, cmp) timercmp((tvp), (uvp), cmp)
+#else
+#define evutil_timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif
+
+#ifdef _EVENT_HAVE_TIMERISSET
+#define evutil_timerisset(tvp) timerisset(tvp)
+#else
+#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+
+/* big-int related functions */
+ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVUTIL_H_ */
diff --git a/extra/libevent/http-internal.h b/extra/libevent/http-internal.h
new file mode 100644
index 00000000000..e9a45f124d8
--- /dev/null
+++ b/extra/libevent/http-internal.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2001 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * This header file contains definitions for dealing with HTTP requests
+ * that are internal to libevent. As user of the library, you should not
+ * need to know about these.
+ */
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#define HTTP_CONNECT_TIMEOUT 45
+#define HTTP_WRITE_TIMEOUT 50
+#define HTTP_READ_TIMEOUT 50
+
+#define HTTP_PREFIX "http://"
+#define HTTP_DEFAULTPORT 80
+
+enum evhttp_connection_error {
+ EVCON_HTTP_TIMEOUT,
+ EVCON_HTTP_EOF,
+ EVCON_HTTP_INVALID_HEADER
+};
+
+struct evbuffer;
+struct addrinfo;
+struct evhttp_request;
+
+/* A stupid connection object - maybe make this a bufferevent later */
+
+enum evhttp_connection_state {
+ EVCON_DISCONNECTED, /* not currently connected not trying either */
+ EVCON_CONNECTING, /* tries to currently connect */
+ EVCON_CONNECTED /* connection is established */
+};
+
+struct event_base;
+
+struct evhttp_connection {
+ /* we use tailq only if they were created for an http server */
+ TAILQ_ENTRY(evhttp_connection) (next);
+
+ int fd;
+ struct event ev;
+ struct event close_ev;
+ struct evbuffer *input_buffer;
+ struct evbuffer *output_buffer;
+
+ char *bind_address; /* address to use for binding the src */
+
+ char *address; /* address to connect to */
+ u_short port;
+
+ int flags;
+#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
+#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
+#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
+
+ int timeout; /* timeout in seconds for events */
+ int retry_cnt; /* retry count */
+ int retry_max; /* maximum number of retries */
+
+ enum evhttp_connection_state state;
+
+ /* for server connections, the http server they are connected with */
+ struct evhttp *http_server;
+
+ TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
+
+ void (*cb)(struct evhttp_connection *, void *);
+ void *cb_arg;
+
+ void (*closecb)(struct evhttp_connection *, void *);
+ void *closecb_arg;
+
+ struct event_base *base;
+};
+
+struct evhttp_cb {
+ TAILQ_ENTRY(evhttp_cb) next;
+
+ char *what;
+
+ void (*cb)(struct evhttp_request *req, void *);
+ void *cbarg;
+};
+
+/* both the http server as well as the rpc system need to queue connections */
+TAILQ_HEAD(evconq, evhttp_connection);
+
+struct evhttp {
+ struct event bind_ev;
+
+ TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
+ struct evconq connections;
+
+ int timeout;
+
+ void (*gencb)(struct evhttp_request *req, void *);
+ void *gencbarg;
+
+ struct event_base *base;
+};
+
+/* resets the connection; can be reused for more requests */
+void evhttp_connection_reset(struct evhttp_connection *);
+
+/* connects if necessary */
+int evhttp_connection_connect(struct evhttp_connection *);
+
+/* notifies the current request that it failed; resets connection */
+void evhttp_connection_fail(struct evhttp_connection *,
+ enum evhttp_connection_error error);
+
+void evhttp_get_request(struct evhttp *, int, struct sockaddr *, socklen_t);
+
+int evhttp_hostportfile(char *, char **, u_short *, char **);
+
+int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
+
+void evhttp_start_read(struct evhttp_connection *);
+void evhttp_read_header(int, short, void *);
+void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
+
+void evhttp_write_buffer(struct evhttp_connection *,
+ void (*)(struct evhttp_connection *, void *), void *);
+
+/* response sending HTML the data in the buffer */
+void evhttp_response_code(struct evhttp_request *, int, const char *);
+void evhttp_send_page(struct evhttp_request *, struct evbuffer *);
+
+#endif /* _HTTP_H */
diff --git a/extra/libevent/http.c b/extra/libevent/http.c
new file mode 100644
index 00000000000..d56a34ca122
--- /dev/null
+++ b/extra/libevent/http.c
@@ -0,0 +1,2512 @@
+/*
+ * Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
+ * 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_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+
+#ifndef WIN32
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#endif
+
+#include <sys/queue.h>
+
+#ifndef WIN32
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#endif
+#include <signal.h>
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#undef timeout_pending
+#undef timeout_initialized
+
+#include "strlcpy-internal.h"
+#include "event.h"
+#include "evhttp.h"
+#include "evutil.h"
+#include "log.h"
+#include "http-internal.h"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define strdup _strdup
+#endif
+
+#ifndef HAVE_GETADDRINFO
+struct addrinfo {
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ struct addrinfo *ai_next;
+};
+static int
+fake_getaddrinfo(const char *hostname, struct addrinfo *ai)
+{
+ struct hostent *he = NULL;
+ struct sockaddr_in *sa;
+ if (hostname) {
+ he = gethostbyname(hostname);
+ if (!he)
+ return (-1);
+ }
+ ai->ai_family = he ? he->h_addrtype : AF_INET;
+ ai->ai_socktype = SOCK_STREAM;
+ ai->ai_protocol = 0;
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen)))
+ return (-1);
+ sa = (struct sockaddr_in*)ai->ai_addr;
+ memset(sa, 0, ai->ai_addrlen);
+ if (he) {
+ sa->sin_family = he->h_addrtype;
+ memcpy(&sa->sin_addr, he->h_addr_list[0], he->h_length);
+ } else {
+ sa->sin_family = AF_INET;
+ sa->sin_addr.s_addr = INADDR_ANY;
+ }
+ ai->ai_next = NULL;
+ return (0);
+}
+static void
+fake_freeaddrinfo(struct addrinfo *ai)
+{
+ free(ai->ai_addr);
+}
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/* wrapper for setting the base from the http server */
+#define EVHTTP_BASE_SET(x, y) do { \
+ if ((x)->base != NULL) event_base_set((x)->base, y); \
+} while (0)
+
+extern int debug;
+
+static int socket_connect(int fd, const char *address, unsigned short port);
+static int bind_socket_ai(struct addrinfo *);
+static int bind_socket(const char *, u_short);
+static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
+static int evhttp_associate_new_request_with_connection(
+ struct evhttp_connection *evcon);
+static void evhttp_connection_start_detectclose(
+ struct evhttp_connection *evcon);
+static void evhttp_connection_stop_detectclose(
+ struct evhttp_connection *evcon);
+static void evhttp_request_dispatch(struct evhttp_connection* evcon);
+
+void evhttp_read(int, short, void *);
+void evhttp_write(int, short, void *);
+
+#ifndef HAVE_STRSEP
+/* strsep replacement for platforms that lack it. Only works if
+ * del is one character long. */
+static char *
+strsep(char **s, const char *del)
+{
+ char *d, *tok;
+ assert(strlen(del) == 1);
+ if (!s || !*s)
+ return NULL;
+ tok = *s;
+ d = strstr(tok, del);
+ if (d) {
+ *d = '\0';
+ *s = d + 1;
+ } else
+ *s = NULL;
+ return tok;
+}
+#endif
+
+static const char *
+html_replace(char ch, char *buf)
+{
+ switch (ch) {
+ case '<':
+ return "&lt;";
+ case '>':
+ return "&gt;";
+ case '"':
+ return "&quot;";
+ case '\'':
+ return "&#039;";
+ case '&':
+ return "&amp;";
+ default:
+ break;
+ }
+
+ /* Echo the character back */
+ buf[0] = ch;
+ buf[1] = '\0';
+
+ return buf;
+}
+
+/*
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ */
+
+char *
+evhttp_htmlescape(const char *html)
+{
+ int i, new_size = 0, old_size = strlen(html);
+ char *escaped_html, *p;
+ char scratch_space[2];
+
+ for (i = 0; i < old_size; ++i)
+ new_size += strlen(html_replace(html[i], scratch_space));
+
+ p = escaped_html = malloc(new_size + 1);
+ if (escaped_html == NULL)
+ event_err(1, "%s: malloc(%d)", __func__, new_size + 1);
+ for (i = 0; i < old_size; ++i) {
+ const char *replaced = html_replace(html[i], scratch_space);
+ /* this is length checked */
+ strcpy(p, replaced);
+ p += strlen(replaced);
+ }
+
+ *p = '\0';
+
+ return (escaped_html);
+}
+
+static const char *
+evhttp_method(enum evhttp_cmd_type type)
+{
+ const char *method;
+
+ switch (type) {
+ case EVHTTP_REQ_GET:
+ method = "GET";
+ break;
+ case EVHTTP_REQ_POST:
+ method = "POST";
+ break;
+ case EVHTTP_REQ_HEAD:
+ method = "HEAD";
+ break;
+ default:
+ method = NULL;
+ break;
+ }
+
+ return (method);
+}
+
+static void
+evhttp_add_event(struct event *ev, int timeout, int default_timeout)
+{
+ if (timeout != 0) {
+ struct timeval tv;
+
+ evutil_timerclear(&tv);
+ tv.tv_sec = timeout != -1 ? timeout : default_timeout;
+ event_add(ev, &tv);
+ } else {
+ event_add(ev, NULL);
+ }
+}
+
+void
+evhttp_write_buffer(struct evhttp_connection *evcon,
+ void (*cb)(struct evhttp_connection *, void *), void *arg)
+{
+ event_debug(("%s: preparing to write buffer\n", __func__));
+
+ /* Set call back */
+ evcon->cb = cb;
+ evcon->cb_arg = arg;
+
+ /* check if the event is already pending */
+ if (event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL))
+ event_del(&evcon->ev);
+
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->ev);
+ evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT);
+}
+
+/*
+ * Create the headers need for an HTTP request
+ */
+static void
+evhttp_make_header_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ char line[1024];
+ const char *method;
+
+ evhttp_remove_header(req->output_headers, "Accept-Encoding");
+ evhttp_remove_header(req->output_headers, "Proxy-Connection");
+
+ /* Generate request line */
+ method = evhttp_method(req->type);
+ snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
+ method, req->uri, req->major, req->minor);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+
+ /* Add the content length on a post request if missing */
+ if (req->type == EVHTTP_REQ_POST &&
+ evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+ char size[12];
+ snprintf(size, sizeof(size), "%ld",
+ (long)EVBUFFER_LENGTH(req->output_buffer));
+ evhttp_add_header(req->output_headers, "Content-Length", size);
+ }
+}
+
+static int
+evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
+{
+ if (flags & EVHTTP_PROXY_REQUEST) {
+ /* proxy connection */
+ const char *connection = evhttp_find_header(headers, "Proxy-Connection");
+ return (connection == NULL || strcasecmp(connection, "keep-alive") != 0);
+ } else {
+ const char *connection = evhttp_find_header(headers, "Connection");
+ return (connection != NULL && strcasecmp(connection, "close") == 0);
+ }
+}
+
+static int
+evhttp_is_connection_keepalive(struct evkeyvalq* headers)
+{
+ const char *connection = evhttp_find_header(headers, "Connection");
+ return (connection != NULL
+ && strncasecmp(connection, "keep-alive", 10) == 0);
+}
+
+static void
+evhttp_maybe_add_date_header(struct evkeyvalq *headers)
+{
+ if (evhttp_find_header(headers, "Date") == NULL) {
+ char date[50];
+#ifndef WIN32
+ struct tm cur;
+#endif
+ struct tm *cur_p;
+ time_t t = time(NULL);
+#ifdef WIN32
+ cur_p = gmtime(&t);
+#else
+ gmtime_r(&t, &cur);
+ cur_p = &cur;
+#endif
+ if (strftime(date, sizeof(date),
+ "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
+ evhttp_add_header(headers, "Date", date);
+ }
+ }
+}
+
+static void
+evhttp_maybe_add_content_length_header(struct evkeyvalq *headers,
+ long content_length)
+{
+ if (evhttp_find_header(headers, "Transfer-Encoding") == NULL &&
+ evhttp_find_header(headers, "Content-Length") == NULL) {
+ char len[12];
+ snprintf(len, sizeof(len), "%ld", content_length);
+ evhttp_add_header(headers, "Content-Length", len);
+ }
+}
+
+/*
+ * Create the headers needed for an HTTP reply
+ */
+
+static void
+evhttp_make_header_response(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ char line[1024];
+ snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
+ req->major, req->minor, req->response_code,
+ req->response_code_line);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+
+ if (req->major == 1 && req->minor == 1)
+ evhttp_maybe_add_date_header(req->output_headers);
+
+ if (req->major == 1 &&
+ (req->minor == 1 ||
+ evhttp_is_connection_keepalive(req->input_headers))) {
+ /*
+ * we need to add the content length if the user did
+ * not give it, this is required for persistent
+ * connections to work.
+ */
+ evhttp_maybe_add_content_length_header(req->output_headers,
+ (long)EVBUFFER_LENGTH(req->output_buffer));
+ }
+
+ /* Potentially add headers for unidentified content. */
+ if (EVBUFFER_LENGTH(req->output_buffer)) {
+ if (evhttp_find_header(req->output_headers,
+ "Content-Type") == NULL) {
+ evhttp_add_header(req->output_headers,
+ "Content-Type", "text/html; charset=ISO-8859-1");
+ }
+ }
+
+ /* if the request asked for a close, we send a close, too */
+ if (evhttp_is_connection_close(req->flags, req->input_headers)) {
+ evhttp_remove_header(req->output_headers, "Connection");
+ if (!(req->flags & EVHTTP_PROXY_REQUEST))
+ evhttp_add_header(req->output_headers, "Connection", "close");
+ evhttp_remove_header(req->output_headers, "Proxy-Connection");
+ }
+}
+
+void
+evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ char line[1024];
+ struct evkeyval *header;
+
+ /*
+ * Depending if this is a HTTP request or response, we might need to
+ * add some new headers or remove existing headers.
+ */
+ if (req->kind == EVHTTP_REQUEST) {
+ evhttp_make_header_request(evcon, req);
+ } else {
+ evhttp_make_header_response(evcon, req);
+ }
+
+ TAILQ_FOREACH(header, req->output_headers, next) {
+ snprintf(line, sizeof(line), "%s: %s\r\n",
+ header->key, header->value);
+ evbuffer_add(evcon->output_buffer, line, strlen(line));
+ }
+ evbuffer_add(evcon->output_buffer, "\r\n", 2);
+
+ if (EVBUFFER_LENGTH(req->output_buffer) > 0) {
+ /*
+ * For a request, we add the POST data, for a reply, this
+ * is the regular data.
+ */
+ evbuffer_add_buffer(evcon->output_buffer, req->output_buffer);
+ }
+}
+
+/* Separated host, port and file from URI */
+
+int
+evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile)
+{
+ /* XXX not threadsafe. */
+ static char host[1024];
+ static char file[1024];
+ char *p;
+ const char *p2;
+ int len;
+ u_short port;
+
+ len = strlen(HTTP_PREFIX);
+ if (strncasecmp(url, HTTP_PREFIX, len))
+ return (-1);
+
+ url += len;
+
+ /* We might overrun */
+ if (strlcpy(host, url, sizeof (host)) >= sizeof(host))
+ return (-1);
+
+ p = strchr(host, '/');
+ if (p != NULL) {
+ *p = '\0';
+ p2 = p + 1;
+ } else
+ p2 = NULL;
+
+ if (pfile != NULL) {
+ /* Generate request file */
+ if (p2 == NULL)
+ p2 = "";
+ snprintf(file, sizeof(file), "/%s", p2);
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL) {
+ *p = '\0';
+ port = atoi(p + 1);
+
+ if (port == 0)
+ return (-1);
+ } else
+ port = HTTP_DEFAULTPORT;
+
+ if (phost != NULL)
+ *phost = host;
+ if (pport != NULL)
+ *pport = port;
+ if (pfile != NULL)
+ *pfile = file;
+
+ return (0);
+}
+
+static int
+evhttp_connection_incoming_fail(struct evhttp_request *req,
+ enum evhttp_connection_error error)
+{
+ switch (error) {
+ case EVCON_HTTP_TIMEOUT:
+ case EVCON_HTTP_EOF:
+ /*
+ * these are cases in which we probably should just
+ * close the connection and not send a reply. this
+ * case may happen when a browser keeps a persistent
+ * connection open and we timeout on the read.
+ */
+ return (-1);
+ case EVCON_HTTP_INVALID_HEADER:
+ default: /* xxx: probably should just error on default */
+ /* the callback looks at the uri to determine errors */
+ if (req->uri) {
+ free(req->uri);
+ req->uri = NULL;
+ }
+
+ /*
+ * the callback needs to send a reply, once the reply has
+ * been send, the connection should get freed.
+ */
+ (*req->cb)(req, req->cb_arg);
+ }
+
+ return (0);
+}
+
+void
+evhttp_connection_fail(struct evhttp_connection *evcon,
+ enum evhttp_connection_error error)
+{
+ struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
+ void (*cb)(struct evhttp_request *, void *);
+ void *cb_arg;
+ assert(req != NULL);
+
+ if (evcon->flags & EVHTTP_CON_INCOMING) {
+ /*
+ * for incoming requests, there are two different
+ * failure cases. it's either a network level error
+ * or an http layer error. for problems on the network
+ * layer like timeouts we just drop the connections.
+ * For HTTP problems, we might have to send back a
+ * reply before the connection can be freed.
+ */
+ if (evhttp_connection_incoming_fail(req, error) == -1)
+ evhttp_connection_free(evcon);
+ return;
+ }
+
+ /* save the callback for later; the cb might free our object */
+ cb = req->cb;
+ cb_arg = req->cb_arg;
+
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ evhttp_request_free(req);
+
+ /* xxx: maybe we should fail all requests??? */
+
+ /* reset the connection */
+ evhttp_connection_reset(evcon);
+
+ /* We are trying the next request that was queued on us */
+ if (TAILQ_FIRST(&evcon->requests) != NULL)
+ evhttp_connection_connect(evcon);
+
+ /* inform the user */
+ if (cb != NULL)
+ (*cb)(NULL, cb_arg);
+}
+
+void
+evhttp_write(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ int n;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+ return;
+ }
+
+ n = evbuffer_write(evcon->output_buffer, fd);
+ if (n == -1) {
+ event_debug(("%s: evbuffer_write", __func__));
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+
+ if (n == 0) {
+ event_debug(("%s: write nothing", __func__));
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+
+ if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) {
+ evhttp_add_event(&evcon->ev,
+ evcon->timeout, HTTP_WRITE_TIMEOUT);
+ return;
+ }
+
+ /* Activate our call back */
+ if (evcon->cb != NULL)
+ (*evcon->cb)(evcon, evcon->cb_arg);
+}
+
+static void
+evhttp_connection_done(struct evhttp_connection *evcon)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
+
+ /*
+ * if this is an incoming connection, we need to leave the request
+ * on the connection, so that we can reply to it.
+ */
+ if (con_outgoing) {
+ int need_close;
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ req->evcon = NULL;
+
+ need_close =
+ evhttp_is_connection_close(req->flags, req->input_headers) ||
+ evhttp_is_connection_close(req->flags, req->output_headers);
+
+ /* check if we got asked to close the connection */
+ if (need_close)
+ evhttp_connection_reset(evcon);
+
+ if (TAILQ_FIRST(&evcon->requests) != NULL) {
+ /*
+ * We have more requests; reset the connection
+ * and deal with the next request. xxx: no
+ * persistent connection right now
+ */
+ if (evcon->state != EVCON_CONNECTED)
+ evhttp_connection_connect(evcon);
+ else
+ evhttp_request_dispatch(evcon);
+ } else if (!need_close) {
+ /*
+ * The connection is going to be persistent, but we
+ * need to detect if the other side closes it.
+ */
+ evhttp_connection_start_detectclose(evcon);
+ }
+ }
+
+ /* notify the user of the request */
+ (*req->cb)(req, req->cb_arg);
+
+ /* if this was an outgoing request, we own and it's done. so free it */
+ if (con_outgoing) {
+ evhttp_request_free(req);
+ }
+}
+
+/*
+ * Handles reading from a chunked request.
+ * return 1: all data has been read
+ * return 0: more data is expected
+ * return -1: data is corrupted
+ */
+
+static int
+evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
+{
+ int len;
+
+ while ((len = EVBUFFER_LENGTH(buf)) > 0) {
+ if (req->ntoread < 0) {
+ /* Read chunk size */
+ char *p = evbuffer_readline(buf);
+ char *endp;
+ int error;
+ if (p == NULL)
+ break;
+ /* the last chunk is on a new line? */
+ if (strlen(p) == 0) {
+ free(p);
+ continue;
+ }
+ req->ntoread = evutil_strtoll(p, &endp, 16);
+ error = *p == '\0' || (*endp != '\0' && *endp != ' ');
+ free(p);
+ if (error) {
+ /* could not get chunk size */
+ return (-1);
+ }
+ if (req->ntoread == 0) {
+ /* Last chunk */
+ return (1);
+ }
+ continue;
+ }
+
+ /* don't have enough to complete a chunk; wait for more */
+ if (len < req->ntoread)
+ return (0);
+
+ /* Completed chunk */
+ evbuffer_add(req->input_buffer,
+ EVBUFFER_DATA(buf), req->ntoread);
+ evbuffer_drain(buf, req->ntoread);
+ req->ntoread = -1;
+ if (req->chunk_cb != NULL) {
+ (*req->chunk_cb)(req, req->cb_arg);
+ evbuffer_drain(req->input_buffer,
+ EVBUFFER_LENGTH(req->input_buffer));
+ }
+ }
+
+ return (0);
+}
+
+static void
+evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ struct evbuffer *buf = evcon->input_buffer;
+
+ if (req->chunked) {
+ int res = evhttp_handle_chunked_read(req, buf);
+ if (res == 1) {
+ /* finished last chunk */
+ evhttp_connection_done(evcon);
+ return;
+ } else if (res == -1) {
+ /* corrupted data */
+ evhttp_connection_fail(evcon,
+ EVCON_HTTP_INVALID_HEADER);
+ return;
+ }
+ } else if (req->ntoread < 0) {
+ /* Read until connection close. */
+ evbuffer_add_buffer(req->input_buffer, buf);
+ } else if (EVBUFFER_LENGTH(buf) >= req->ntoread) {
+ /* Completed content length */
+ evbuffer_add(req->input_buffer, EVBUFFER_DATA(buf),
+ req->ntoread);
+ evbuffer_drain(buf, req->ntoread);
+ req->ntoread = 0;
+ evhttp_connection_done(evcon);
+ return;
+ }
+ /* Read more! */
+ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->ev);
+ evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
+}
+
+/*
+ * Reads data into a buffer structure until no more data
+ * can be read on the file descriptor or we have read all
+ * the data that we wanted to read.
+ * Execute callback when done.
+ */
+
+void
+evhttp_read(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ struct evbuffer *buf = evcon->input_buffer;
+ int n, len;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+ return;
+ }
+ n = evbuffer_read(buf, fd, -1);
+ len = EVBUFFER_LENGTH(buf);
+ event_debug(("%s: got %d on %d\n", __func__, n, fd));
+
+ if (n == -1) {
+ event_debug(("%s: evbuffer_read", __func__));
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ } else if (n == 0) {
+ /* Connection closed */
+ evhttp_connection_done(evcon);
+ return;
+ }
+ evhttp_read_body(evcon, req);
+}
+
+static void
+evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
+{
+ /* This is after writing the request to the server */
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ assert(req != NULL);
+
+ /* We are done writing our header and are now expecting the response */
+ req->kind = EVHTTP_RESPONSE;
+
+ evhttp_start_read(evcon);
+}
+
+/*
+ * Clean up a connection object
+ */
+
+void
+evhttp_connection_free(struct evhttp_connection *evcon)
+{
+ struct evhttp_request *req;
+
+ /* notify interested parties that this connection is going down */
+ if (evcon->fd != -1) {
+ if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL)
+ (*evcon->closecb)(evcon, evcon->closecb_arg);
+ }
+
+ /* remove all requests that might be queued on this connection */
+ while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ evhttp_request_free(req);
+ }
+
+ if (evcon->http_server != NULL) {
+ struct evhttp *http = evcon->http_server;
+ TAILQ_REMOVE(&http->connections, evcon, next);
+ }
+
+ if (event_initialized(&evcon->close_ev))
+ event_del(&evcon->close_ev);
+
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+
+ if (evcon->fd != -1)
+ EVUTIL_CLOSESOCKET(evcon->fd);
+
+ if (evcon->bind_address != NULL)
+ free(evcon->bind_address);
+
+ if (evcon->address != NULL)
+ free(evcon->address);
+
+ if (evcon->input_buffer != NULL)
+ evbuffer_free(evcon->input_buffer);
+
+ if (evcon->output_buffer != NULL)
+ evbuffer_free(evcon->output_buffer);
+
+ free(evcon);
+}
+
+void
+evhttp_connection_set_local_address(struct evhttp_connection *evcon,
+ const char *address)
+{
+ assert(evcon->state == EVCON_DISCONNECTED);
+ if (evcon->bind_address)
+ free(evcon->bind_address);
+ if ((evcon->bind_address = strdup(address)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+}
+
+
+static void
+evhttp_request_dispatch(struct evhttp_connection* evcon)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+ /* this should not usually happy but it's possible */
+ if (req == NULL)
+ return;
+
+ /* delete possible close detection events */
+ evhttp_connection_stop_detectclose(evcon);
+
+ /* we assume that the connection is connected already */
+ assert(evcon->state == EVCON_CONNECTED);
+
+ /* Create the header from the store arguments */
+ evhttp_make_header(evcon, req);
+
+ evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
+}
+
+/* Reset our connection state */
+void
+evhttp_connection_reset(struct evhttp_connection *evcon)
+{
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+
+ if (evcon->fd != -1) {
+ /* inform interested parties about connection close */
+ if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL)
+ (*evcon->closecb)(evcon, evcon->closecb_arg);
+
+ EVUTIL_CLOSESOCKET(evcon->fd);
+ evcon->fd = -1;
+ }
+ evcon->state = EVCON_DISCONNECTED;
+
+ /* remove unneeded flags */
+ evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+}
+
+static void
+evhttp_detect_close_cb(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ evhttp_connection_reset(evcon);
+}
+
+static void
+evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
+{
+ evcon->flags |= EVHTTP_CON_CLOSEDETECT;
+
+ if (event_initialized(&evcon->close_ev))
+ event_del(&evcon->close_ev);
+ event_set(&evcon->close_ev, evcon->fd, EV_READ,
+ evhttp_detect_close_cb, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->close_ev);
+ event_add(&evcon->close_ev, NULL);
+}
+
+static void
+evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
+{
+ evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+ event_del(&evcon->close_ev);
+}
+
+static void
+evhttp_connection_retry(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+
+ evcon->state = EVCON_DISCONNECTED;
+ evhttp_connection_connect(evcon);
+}
+
+/*
+ * Call back for asynchronous connection attempt.
+ */
+
+static void
+evhttp_connectioncb(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ int error;
+ socklen_t errsz = sizeof(error);
+
+ if (what == EV_TIMEOUT) {
+ event_debug(("%s: connection timeout for \"%s:%d\" on %d",
+ __func__, evcon->address, evcon->port, evcon->fd));
+ goto cleanup;
+ }
+
+ /* Check if the connection completed */
+ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
+ &errsz) == -1) {
+ event_debug(("%s: getsockopt for \"%s:%d\" on %d",
+ __func__, evcon->address, evcon->port, evcon->fd));
+ goto cleanup;
+ }
+
+ if (error) {
+ event_debug(("%s: connect failed for \"%s:%d\" on %d: %s",
+ __func__, evcon->address, evcon->port, evcon->fd,
+ strerror(error)));
+ goto cleanup;
+ }
+
+ /* We are connected to the server now */
+ event_debug(("%s: connected to \"%s:%d\" on %d\n",
+ __func__, evcon->address, evcon->port, evcon->fd));
+
+ /* Reset the retry count as we were successful in connecting */
+ evcon->retry_cnt = 0;
+ evcon->state = EVCON_CONNECTED;
+
+ /* try to start requests that have queued up on this connection */
+ evhttp_request_dispatch(evcon);
+ return;
+
+ cleanup:
+ if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
+ evtimer_set(&evcon->ev, evhttp_connection_retry, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->ev);
+ evhttp_add_event(&evcon->ev, MIN(3600, 2 << evcon->retry_cnt),
+ HTTP_CONNECT_TIMEOUT);
+ evcon->retry_cnt++;
+ return;
+ }
+ evhttp_connection_reset(evcon);
+
+ /* for now, we just signal all requests by executing their callbacks */
+ while (TAILQ_FIRST(&evcon->requests) != NULL) {
+ struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
+ TAILQ_REMOVE(&evcon->requests, request, next);
+ request->evcon = NULL;
+
+ /* we might want to set an error here */
+ request->cb(request, request->cb_arg);
+ evhttp_request_free(request);
+ }
+}
+
+/*
+ * Check if we got a valid response code.
+ */
+
+static int
+evhttp_valid_response_code(int code)
+{
+ if (code == 0)
+ return (0);
+
+ return (1);
+}
+
+/* Parses the status line of a web server */
+
+static int
+evhttp_parse_response_line(struct evhttp_request *req, char *line)
+{
+ char *protocol;
+ char *number;
+ char *readable;
+
+ protocol = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ number = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ readable = line;
+
+ if (strcmp(protocol, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(protocol, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_debug(("%s: bad protocol \"%s\"",
+ __func__, protocol));
+ return (-1);
+ }
+
+ req->response_code = atoi(number);
+ if (!evhttp_valid_response_code(req->response_code)) {
+ event_debug(("%s: bad response code \"%s\"",
+ __func__, number));
+ return (-1);
+ }
+
+ if ((req->response_code_line = strdup(readable)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+ return (0);
+}
+
+/* Parse the first line of a HTTP request */
+
+static int
+evhttp_parse_request_line(struct evhttp_request *req, char *line)
+{
+ char *method;
+ char *uri;
+ char *version;
+
+ /* Parse the request line */
+ method = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ uri = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ version = strsep(&line, " ");
+ if (line != NULL)
+ return (-1);
+
+ /* First line */
+ if (strcmp(method, "GET") == 0) {
+ req->type = EVHTTP_REQ_GET;
+ } else if (strcmp(method, "POST") == 0) {
+ req->type = EVHTTP_REQ_POST;
+ } else if (strcmp(method, "HEAD") == 0) {
+ req->type = EVHTTP_REQ_HEAD;
+ } else {
+ event_debug(("%s: bad method %s on request %p from %s",
+ __func__, method, req, req->remote_host));
+ return (-1);
+ }
+
+ if (strcmp(version, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(version, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_debug(("%s: bad version %s on request %p from %s",
+ __func__, version, req, req->remote_host));
+ return (-1);
+ }
+
+ if ((req->uri = strdup(uri)) == NULL) {
+ event_debug(("%s: evhttp_decode_uri", __func__));
+ return (-1);
+ }
+
+ /* determine if it's a proxy request */
+ if (strlen(req->uri) > 0 && req->uri[0] != '/')
+ req->flags |= EVHTTP_PROXY_REQUEST;
+
+ return (0);
+}
+
+const char *
+evhttp_find_header(const struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ return (header->value);
+ }
+
+ return (NULL);
+}
+
+void
+evhttp_clear_headers(struct evkeyvalq *headers)
+{
+ struct evkeyval *header;
+
+ for (header = TAILQ_FIRST(headers);
+ header != NULL;
+ header = TAILQ_FIRST(headers)) {
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+ }
+}
+
+/*
+ * Returns 0, if the header was successfully removed.
+ * Returns -1, if the header could not be found.
+ */
+
+int
+evhttp_remove_header(struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ break;
+ }
+
+ if (header == NULL)
+ return (-1);
+
+ /* Free and remove the header that we found */
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+
+ return (0);
+}
+
+int
+evhttp_add_header(struct evkeyvalq *headers,
+ const char *key, const char *value)
+{
+ struct evkeyval *header = NULL;
+
+ event_debug(("%s: key: %s val: %s\n", __func__, key, value));
+
+ if (strchr(value, '\r') != NULL || strchr(value, '\n') != NULL ||
+ strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
+ /* drop illegal headers */
+ event_debug(("%s: dropping illegal header\n", __func__));
+ return (-1);
+ }
+
+ header = calloc(1, sizeof(struct evkeyval));
+ if (header == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (-1);
+ }
+ if ((header->key = strdup(key)) == NULL) {
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+ if ((header->value = strdup(value)) == NULL) {
+ free(header->key);
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+
+ TAILQ_INSERT_TAIL(headers, header, next);
+
+ return (0);
+}
+
+/*
+ * Parses header lines from a request or a response into the specified
+ * request object given an event buffer.
+ *
+ * Returns
+ * -1 on error
+ * 0 when we need to read more headers
+ * 1 when all headers have been read.
+ */
+
+int
+evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
+{
+ char *line;
+ int done = 0;
+
+ struct evkeyvalq* headers = req->input_headers;
+ while ((line = evbuffer_readline(buffer)) != NULL) {
+ char *skey, *svalue;
+
+ if (*line == '\0') { /* Last header - Done */
+ done = 1;
+ free (line);
+ break;
+ }
+
+ /* Processing of header lines */
+ if (req->got_firstline == 0) {
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ if (evhttp_parse_request_line(req, line) == -1)
+ goto error;
+ break;
+ case EVHTTP_RESPONSE:
+ if (evhttp_parse_response_line(req, line) == -1)
+ goto error;
+ break;
+ default:
+ goto error;
+ }
+ req->got_firstline = 1;
+ } else {
+ /* Regular header */
+ svalue = line;
+ skey = strsep(&svalue, ":");
+ if (svalue == NULL)
+ goto error;
+
+ svalue += strspn(svalue, " ");
+
+ if (evhttp_add_header(headers, skey, svalue) == -1)
+ goto error;
+ }
+
+ free (line);
+ }
+
+ return (done);
+
+ error:
+ free (line);
+ return (-1);
+}
+
+static int
+evhttp_get_body_length(struct evhttp_request *req)
+{
+ struct evkeyvalq *headers = req->input_headers;
+ const char *content_length;
+ const char *connection;
+
+ content_length = evhttp_find_header(headers, "Content-Length");
+ connection = evhttp_find_header(headers, "Connection");
+
+ if (content_length == NULL && connection == NULL)
+ req->ntoread = -1;
+ else if (content_length == NULL &&
+ strcasecmp(connection, "Close") != 0) {
+ /* Bad combination, we don't know when it will end */
+ event_warnx("%s: we got no content length, but the "
+ "server wants to keep the connection open: %s.",
+ __func__, connection);
+ return (-1);
+ } else if (content_length == NULL) {
+ req->ntoread = -1;
+ } else {
+ char *endp;
+ req->ntoread = evutil_strtoll(content_length, &endp, 10);
+ if (*content_length == '\0' || *endp != '\0') {
+ event_warnx("%s: illegal content length: %s",
+ __func__, content_length);
+ return (-1);
+ }
+ }
+
+ event_debug(("%s: bytes to read: %d (in buffer %d)\n",
+ __func__, req->ntoread,
+ EVBUFFER_LENGTH(req->evcon->input_buffer)));
+
+ return (0);
+}
+
+static void
+evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ const char *xfer_enc;
+
+ /* If this is a request without a body, then we are done */
+ if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) {
+ evhttp_connection_done(evcon);
+ return;
+ }
+ xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding");
+ if (xfer_enc != NULL && strcasecmp(xfer_enc, "chunked") == 0) {
+ req->chunked = 1;
+ req->ntoread = -1;
+ } else {
+ if (evhttp_get_body_length(req) == -1) {
+ evhttp_connection_fail(evcon,
+ EVCON_HTTP_INVALID_HEADER);
+ return;
+ }
+ }
+ evhttp_read_body(evcon, req);
+}
+
+void
+evhttp_read_header(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ int n, res;
+
+ if (what == EV_TIMEOUT) {
+ event_debug(("%s: timeout on %d\n", __func__, fd));
+ evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+ return;
+ }
+
+ n = evbuffer_read(evcon->input_buffer, fd, -1);
+ if (n == 0) {
+ event_debug(("%s: no more data on %d", __func__, fd));
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+ if (n == -1) {
+ event_debug(("%s: bad read on %d", __func__, fd));
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+
+ res = evhttp_parse_lines(req, evcon->input_buffer);
+ if (res == -1) {
+ /* Error while reading, terminate */
+ event_debug(("%s: bad header lines on %d\n", __func__, fd));
+ evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+ return;
+ } else if (res == 0) {
+ /* Need more header lines */
+ evhttp_add_event(&evcon->ev,
+ evcon->timeout, HTTP_READ_TIMEOUT);
+ return;
+ }
+
+ /* Done reading headers, do the real work */
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ event_debug(("%s: checking for post data on %d\n",
+ __func__, fd));
+ evhttp_get_body(evcon, req);
+ break;
+
+ case EVHTTP_RESPONSE:
+ if (req->response_code == HTTP_NOCONTENT ||
+ req->response_code == HTTP_NOTMODIFIED ||
+ (req->response_code >= 100 && req->response_code < 200)) {
+ event_debug(("%s: skipping body for code %d\n",
+ __func__, req->response_code));
+ evhttp_connection_done(evcon);
+ } else {
+ event_debug(("%s: start of read body for %s on %d\n",
+ __func__, req->remote_host, fd));
+ evhttp_get_body(evcon, req);
+ }
+ break;
+
+ default:
+ event_warnx("%s: bad header on %d", __func__, fd);
+ evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+ break;
+ }
+}
+
+/*
+ * Creates a TCP connection to the specified port and executes a callback
+ * when finished. Failure or sucess is indicate by the passed connection
+ * object.
+ *
+ * Although this interface accepts a hostname, it is intended to take
+ * only numeric hostnames so that non-blocking DNS resolution can
+ * happen elsewhere.
+ */
+
+struct evhttp_connection *
+evhttp_connection_new(const char *address, unsigned short port)
+{
+ struct evhttp_connection *evcon = NULL;
+
+ event_debug(("Attempting connection to %s:%d\n", address, port));
+
+ if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) {
+ event_warn("%s: calloc failed", __func__);
+ goto error;
+ }
+
+ evcon->fd = -1;
+ evcon->port = port;
+
+ evcon->timeout = -1;
+ evcon->retry_cnt = evcon->retry_max = 0;
+
+ if ((evcon->address = strdup(address)) == NULL) {
+ event_warn("%s: strdup failed", __func__);
+ goto error;
+ }
+
+ if ((evcon->input_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new failed", __func__);
+ goto error;
+ }
+
+ if ((evcon->output_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new failed", __func__);
+ goto error;
+ }
+
+ evcon->state = EVCON_DISCONNECTED;
+ TAILQ_INIT(&evcon->requests);
+
+ return (evcon);
+
+ error:
+ if (evcon != NULL)
+ evhttp_connection_free(evcon);
+ return (NULL);
+}
+
+void evhttp_connection_set_base(struct evhttp_connection *evcon,
+ struct event_base *base)
+{
+ assert(evcon->base == NULL);
+ assert(evcon->state == EVCON_DISCONNECTED);
+ evcon->base = base;
+}
+
+void
+evhttp_connection_set_timeout(struct evhttp_connection *evcon,
+ int timeout_in_secs)
+{
+ evcon->timeout = timeout_in_secs;
+}
+
+void
+evhttp_connection_set_retries(struct evhttp_connection *evcon,
+ int retry_max)
+{
+ evcon->retry_max = retry_max;
+}
+
+void
+evhttp_connection_set_closecb(struct evhttp_connection *evcon,
+ void (*cb)(struct evhttp_connection *, void *), void *cbarg)
+{
+ evcon->closecb = cb;
+ evcon->closecb_arg = cbarg;
+}
+
+void
+evhttp_connection_get_peer(struct evhttp_connection *evcon,
+ char **address, u_short *port)
+{
+ *address = evcon->address;
+ *port = evcon->port;
+}
+
+int
+evhttp_connection_connect(struct evhttp_connection *evcon)
+{
+ if (evcon->state == EVCON_CONNECTING)
+ return (0);
+
+ evhttp_connection_reset(evcon);
+
+ assert(!(evcon->flags & EVHTTP_CON_INCOMING));
+ evcon->flags |= EVHTTP_CON_OUTGOING;
+
+ evcon->fd = bind_socket(evcon->bind_address, 0);
+ if (evcon->fd == -1) {
+ event_debug(("%s: failed to bind to \"%s\"",
+ __func__, evcon->bind_address));
+ return (-1);
+ }
+
+ if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) {
+ EVUTIL_CLOSESOCKET(evcon->fd); evcon->fd = -1;
+ return (-1);
+ }
+
+ /* Set up a callback for successful connection setup */
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->ev);
+ evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_CONNECT_TIMEOUT);
+
+ evcon->state = EVCON_CONNECTING;
+
+ return (0);
+}
+
+/*
+ * Starts an HTTP request on the provided evhttp_connection object.
+ * If the connection object is not connected to the web server already,
+ * this will start the connection.
+ */
+
+int
+evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri)
+{
+ /* We are making a request */
+ req->kind = EVHTTP_REQUEST;
+ req->type = type;
+ if (req->uri != NULL)
+ free(req->uri);
+ if ((req->uri = strdup(uri)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+ /* Set the protocol version if it is not supplied */
+ if (!req->major && !req->minor) {
+ req->major = 1;
+ req->minor = 1;
+ }
+
+ assert(req->evcon == NULL);
+ req->evcon = evcon;
+ assert(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ /* If the connection object is not connected; make it so */
+ if (evcon->state != EVCON_CONNECTED)
+ return (evhttp_connection_connect(evcon));
+
+ /*
+ * If it's connected already and we are the first in the queue,
+ * then we can dispatch this request immediately. Otherwise, it
+ * will be dispatched once the pending requests are completed.
+ */
+ if (TAILQ_FIRST(&evcon->requests) == req)
+ evhttp_request_dispatch(evcon);
+
+ return (0);
+}
+
+/*
+ * Reads data from file descriptor into request structure
+ * Request structure needs to be set up correctly.
+ */
+
+void
+evhttp_start_read(struct evhttp_connection *evcon)
+{
+ /* Set up an event to read the headers */
+ if (event_initialized(&evcon->ev))
+ event_del(&evcon->ev);
+ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
+ EVHTTP_BASE_SET(evcon, &evcon->ev);
+
+ evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
+}
+
+static void
+evhttp_send_done(struct evhttp_connection *evcon, void *arg)
+{
+ int need_close;
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ TAILQ_REMOVE(&evcon->requests, req, next);
+
+ /* delete possible close detection events */
+ evhttp_connection_stop_detectclose(evcon);
+
+ need_close =
+ (req->minor == 0 &&
+ !evhttp_is_connection_keepalive(req->input_headers))||
+ evhttp_is_connection_close(req->flags, req->input_headers) ||
+ evhttp_is_connection_close(req->flags, req->output_headers);
+
+ assert(req->flags & EVHTTP_REQ_OWN_CONNECTION);
+ evhttp_request_free(req);
+
+ if (need_close) {
+ evhttp_connection_free(evcon);
+ return;
+ }
+
+ /* we have a persistent connection; try to accept another request. */
+ if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ evhttp_connection_free(evcon);
+}
+
+/*
+ * Returns an error page.
+ */
+
+void
+evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
+{
+#define ERR_FORMAT "<HTML><HEAD>\n" \
+ "<TITLE>%d %s</TITLE>\n" \
+ "</HEAD><BODY>\n" \
+ "<H1>Method Not Implemented</H1>\n" \
+ "Invalid method in request<P>\n" \
+ "</BODY></HTML>\n"
+
+ struct evbuffer *buf = evbuffer_new();
+
+ /* close the connection on error */
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
+ evhttp_response_code(req, error, reason);
+
+ evbuffer_add_printf(buf, ERR_FORMAT, error, reason);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+#undef ERR_FORMAT
+}
+
+/* Requires that headers and response code are already set up */
+
+static inline void
+evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ struct evhttp_connection *evcon = req->evcon;
+
+ assert(TAILQ_FIRST(&evcon->requests) == req);
+
+ /* xxx: not sure if we really should expose the data buffer this way */
+ if (databuf != NULL)
+ evbuffer_add_buffer(req->output_buffer, databuf);
+
+ /* Adds headers to the response */
+ evhttp_make_header(evcon, req);
+
+ evhttp_write_buffer(evcon, evhttp_send_done, NULL);
+}
+
+void
+evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
+ struct evbuffer *databuf)
+{
+ /* set up to watch for client close */
+ evhttp_connection_start_detectclose(req->evcon);
+ evhttp_response_code(req, code, reason);
+
+ evhttp_send(req, databuf);
+}
+
+void
+evhttp_send_reply_start(struct evhttp_request *req, int code,
+ const char *reason)
+{
+ /* set up to watch for client close */
+ evhttp_connection_start_detectclose(req->evcon);
+ evhttp_response_code(req, code, reason);
+ if (req->major == 1 && req->minor == 1) {
+ /* use chunked encoding for HTTP/1.1 */
+ evhttp_add_header(req->output_headers, "Transfer-Encoding",
+ "chunked");
+ req->chunked = 1;
+ }
+ evhttp_make_header(req->evcon, req);
+ evhttp_write_buffer(req->evcon, NULL, NULL);
+}
+
+void
+evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ if (req->chunked) {
+ evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n",
+ (unsigned)EVBUFFER_LENGTH(databuf));
+ }
+ evbuffer_add_buffer(req->evcon->output_buffer, databuf);
+ if (req->chunked) {
+ evbuffer_add(req->evcon->output_buffer, "\r\n", 2);
+ }
+ evhttp_write_buffer(req->evcon, NULL, NULL);
+}
+
+void
+evhttp_send_reply_end(struct evhttp_request *req)
+{
+ struct evhttp_connection *evcon = req->evcon;
+
+ if (req->chunked) {
+ evbuffer_add(req->evcon->output_buffer, "0\r\n\r\n", 5);
+ evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
+ req->chunked = 0;
+ } else if (!event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) {
+ /* let the connection know that we are done with the request */
+ evhttp_send_done(evcon, NULL);
+ } else {
+ /* make the callback execute after all data has been written */
+ evcon->cb = evhttp_send_done;
+ evcon->cb_arg = NULL;
+ }
+}
+
+void
+evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
+{
+ req->kind = EVHTTP_RESPONSE;
+ req->response_code = code;
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+ req->response_code_line = strdup(reason);
+}
+
+void
+evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ if (!req->major || !req->minor) {
+ req->major = 1;
+ req->minor = 1;
+ }
+
+ if (req->kind != EVHTTP_RESPONSE)
+ evhttp_response_code(req, 200, "OK");
+
+ evhttp_clear_headers(req->output_headers);
+ evhttp_add_header(req->output_headers, "Content-Type", "text/html");
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
+ evhttp_send(req, databuf);
+}
+
+static const char uri_chars[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+ /* 64 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
+ /* 128 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 192 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/*
+ * Helper functions to encode/decode a URI.
+ * The returned string must be freed by the caller.
+ */
+char *
+evhttp_encode_uri(const char *uri)
+{
+ struct evbuffer *buf = evbuffer_new();
+ char *p;
+
+ for (p = (char *)uri; *p != '\0'; p++) {
+ if (uri_chars[(u_char)(*p)]) {
+ evbuffer_add(buf, p, 1);
+ } else {
+ evbuffer_add_printf(buf, "%%%02X", (u_char)(*p));
+ }
+ }
+ evbuffer_add(buf, "", 1);
+ p = strdup((char *)EVBUFFER_DATA(buf));
+ evbuffer_free(buf);
+
+ return (p);
+}
+
+char *
+evhttp_decode_uri(const char *uri)
+{
+ char c, *ret;
+ int i, j, in_query = 0;
+
+ ret = malloc(strlen(uri) + 1);
+ if (ret == NULL)
+ event_err(1, "%s: malloc(%lu)", __func__,
+ (unsigned long)(strlen(uri) + 1));
+
+ for (i = j = 0; uri[i] != '\0'; i++) {
+ c = uri[i];
+ if (c == '?') {
+ in_query = 1;
+ } else if (c == '+' && in_query) {
+ c = ' ';
+ } else if (c == '%' && isxdigit((unsigned char)uri[i+1]) &&
+ isxdigit((unsigned char)uri[i+2])) {
+ char tmp[] = { uri[i+1], uri[i+2], '\0' };
+ c = (char)strtol(tmp, NULL, 16);
+ i += 2;
+ }
+ ret[j++] = c;
+ }
+ ret[j] = '\0';
+
+ return (ret);
+}
+
+/*
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ * URI should already be decoded.
+ */
+
+void
+evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
+{
+ char *line;
+ char *argument;
+ char *p;
+
+ TAILQ_INIT(headers);
+
+ /* No arguments - we are done */
+ if (strchr(uri, '?') == NULL)
+ return;
+
+ if ((line = strdup(uri)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+
+ argument = line;
+
+ /* We already know that there has to be a ? */
+ strsep(&argument, "?");
+
+ p = argument;
+ while (p != NULL && *p != '\0') {
+ char *key, *value;
+ argument = strsep(&p, "&");
+
+ value = argument;
+ key = strsep(&value, "=");
+ if (value == NULL)
+ goto error;
+
+ value = evhttp_decode_uri(value);
+ event_debug(("Query Param: %s -> %s\n", key, value));
+ evhttp_add_header(headers, key, value);
+ free(value);
+ }
+
+ error:
+ free(line);
+}
+
+static struct evhttp_cb *
+evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req)
+{
+ struct evhttp_cb *cb;
+ size_t offset = 0;
+
+ /* Test for different URLs */
+ char *p = strchr(req->uri, '?');
+ if (p != NULL)
+ offset = (size_t)(p - req->uri);
+
+ TAILQ_FOREACH(cb, callbacks, next) {
+ int res = 0;
+ if (p == NULL) {
+ res = strcmp(cb->what, req->uri) == 0;
+ } else {
+ res = ((strncmp(cb->what, req->uri, offset) == 0) &&
+ (cb->what[offset] == '\0'));
+ }
+
+ if (res)
+ return (cb);
+ }
+
+ return (NULL);
+}
+
+static void
+evhttp_handle_request(struct evhttp_request *req, void *arg)
+{
+ struct evhttp *http = arg;
+ struct evhttp_cb *cb = NULL;
+
+ if (req->uri == NULL) {
+ evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
+ return;
+ }
+
+ if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) {
+ (*cb->cb)(req, cb->cbarg);
+ return;
+ }
+
+ /* Generic call back */
+ if (http->gencb) {
+ (*http->gencb)(req, http->gencbarg);
+ return;
+ } else {
+ /* We need to send a 404 here */
+#define ERR_FORMAT "<html><head>" \
+ "<title>404 Not Found</title>" \
+ "</head><body>" \
+ "<h1>Not Found</h1>" \
+ "<p>The requested URL %s was not found on this server.</p>"\
+ "</body></html>\n"
+
+ char *escaped_html = evhttp_htmlescape(req->uri);
+ struct evbuffer *buf = evbuffer_new();
+
+ evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
+
+ evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
+
+ free(escaped_html);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+#undef ERR_FORMAT
+ }
+}
+
+static void
+accept_socket(int fd, short what, void *arg)
+{
+ struct evhttp *http = arg;
+ struct sockaddr_storage ss;
+ socklen_t addrlen = sizeof(ss);
+ int nfd;
+
+ if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
+ event_warn("%s: bad accept", __func__);
+ return;
+ }
+ if (evutil_make_socket_nonblocking(nfd) < 0)
+ return;
+
+ evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen);
+}
+
+int
+evhttp_bind_socket(struct evhttp *http, const char *address, u_short port)
+{
+ struct event *ev = &http->bind_ev;
+ int fd;
+
+ if ((fd = bind_socket(address, port)) == -1)
+ return (-1);
+
+ if (listen(fd, 10) == -1) {
+ event_warn("%s: listen", __func__);
+ EVUTIL_CLOSESOCKET(fd);
+ return (-1);
+ }
+
+ /* Schedule the socket for accepting */
+ event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
+ EVHTTP_BASE_SET(http, ev);
+ event_add(ev, NULL);
+
+ event_debug(("Bound to port %d - Awaiting connections ... ", port));
+
+ return (0);
+}
+
+static struct evhttp*
+evhttp_new_object(void)
+{
+ struct evhttp *http = NULL;
+
+ if ((http = calloc(1, sizeof(struct evhttp))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (NULL);
+ }
+
+ http->timeout = -1;
+
+ TAILQ_INIT(&http->callbacks);
+ TAILQ_INIT(&http->connections);
+
+ return (http);
+}
+
+struct evhttp *
+evhttp_new(struct event_base *base)
+{
+ struct evhttp *http = evhttp_new_object();
+
+ http->base = base;
+
+ return (http);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, u_short port)
+{
+ struct evhttp *http = evhttp_new_object();
+
+ if (evhttp_bind_socket(http, address, port) == -1) {
+ free(http);
+ return (NULL);
+ }
+
+ return (http);
+}
+
+void
+evhttp_free(struct evhttp* http)
+{
+ struct evhttp_cb *http_cb;
+ struct evhttp_connection *evcon;
+ int fd = http->bind_ev.ev_fd;
+
+ /* Remove the accepting part */
+ event_del(&http->bind_ev);
+ EVUTIL_CLOSESOCKET(fd);
+
+ while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
+ /* evhttp_connection_free removes the connection */
+ evhttp_connection_free(evcon);
+ }
+
+ while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
+ TAILQ_REMOVE(&http->callbacks, http_cb, next);
+ free(http_cb->what);
+ free(http_cb);
+ }
+
+ free(http);
+}
+
+void
+evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
+{
+ http->timeout = timeout_in_secs;
+}
+
+void
+evhttp_set_cb(struct evhttp *http, const char *uri,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ struct evhttp_cb *http_cb;
+
+ if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ http_cb->what = strdup(uri);
+ http_cb->cb = cb;
+ http_cb->cbarg = cbarg;
+
+ TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
+}
+
+int
+evhttp_del_cb(struct evhttp *http, const char *uri)
+{
+ struct evhttp_cb *http_cb;
+
+ TAILQ_FOREACH(http_cb, &http->callbacks, next) {
+ if (strcmp(http_cb->what, uri) == 0)
+ break;
+ }
+ if (http_cb == NULL)
+ return (-1);
+
+ TAILQ_REMOVE(&http->callbacks, http_cb, next);
+ free(http_cb->what);
+ free(http_cb);
+
+ return (0);
+}
+
+void
+evhttp_set_gencb(struct evhttp *http,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ http->gencb = cb;
+ http->gencbarg = cbarg;
+}
+
+/*
+ * Request related functions
+ */
+
+struct evhttp_request *
+evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ /* Allocate request structure */
+ if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+
+ req->kind = EVHTTP_RESPONSE;
+ req->input_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->input_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->input_headers);
+
+ req->output_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->output_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->output_headers);
+
+ if ((req->input_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new", __func__);
+ goto error;
+ }
+
+ if ((req->output_buffer = evbuffer_new()) == NULL) {
+ event_warn("%s: evbuffer_new", __func__);
+ goto error;
+ }
+
+ req->cb = cb;
+ req->cb_arg = arg;
+
+ return (req);
+
+ error:
+ if (req != NULL)
+ evhttp_request_free(req);
+ return (NULL);
+}
+
+void
+evhttp_request_free(struct evhttp_request *req)
+{
+ if (req->remote_host != NULL)
+ free(req->remote_host);
+ if (req->uri != NULL)
+ free(req->uri);
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+
+ evhttp_clear_headers(req->input_headers);
+ free(req->input_headers);
+
+ evhttp_clear_headers(req->output_headers);
+ free(req->output_headers);
+
+ if (req->input_buffer != NULL)
+ evbuffer_free(req->input_buffer);
+
+ if (req->output_buffer != NULL)
+ evbuffer_free(req->output_buffer);
+
+ free(req);
+}
+
+void
+evhttp_request_set_chunked_cb(struct evhttp_request *req,
+ void (*cb)(struct evhttp_request *, void *))
+{
+ req->chunk_cb = cb;
+}
+
+/*
+ * Allows for inspection of the request URI
+ */
+
+const char *
+evhttp_request_uri(struct evhttp_request *req) {
+ if (req->uri == NULL)
+ event_debug(("%s: request %p has no uri\n", req));
+ return (req->uri);
+}
+
+/*
+ * Takes a file descriptor to read a request from.
+ * The callback is executed once the whole request has been read.
+ */
+
+static struct evhttp_connection*
+evhttp_get_request_connection(
+ struct evhttp* http,
+ int fd, struct sockaddr *sa, socklen_t salen)
+{
+ struct evhttp_connection *evcon;
+ char *hostname, *portname;
+
+ name_from_addr(sa, salen, &hostname, &portname);
+ event_debug(("%s: new request from %s:%s on %d\n",
+ __func__, hostname, portname, fd));
+
+ /* we need a connection object to put the http request on */
+ if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
+ return (NULL);
+
+ /* associate the base if we have one*/
+ evhttp_connection_set_base(evcon, http->base);
+
+ evcon->flags |= EVHTTP_CON_INCOMING;
+ evcon->state = EVCON_CONNECTED;
+
+ evcon->fd = fd;
+
+ return (evcon);
+}
+
+static int
+evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
+{
+ struct evhttp *http = evcon->http_server;
+ struct evhttp_request *req;
+ if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
+ return (-1);
+
+ req->evcon = evcon; /* the request ends up owning the connection */
+ req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ req->kind = EVHTTP_REQUEST;
+
+ if ((req->remote_host = strdup(evcon->address)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+ req->remote_port = evcon->port;
+
+ evhttp_start_read(evcon);
+
+ return (0);
+}
+
+void
+evhttp_get_request(struct evhttp *http, int fd,
+ struct sockaddr *sa, socklen_t salen)
+{
+ struct evhttp_connection *evcon;
+
+ evcon = evhttp_get_request_connection(http, fd, sa, salen);
+ if (evcon == NULL)
+ return;
+
+ /* the timeout can be used by the server to close idle connections */
+ if (http->timeout != -1)
+ evhttp_connection_set_timeout(evcon, http->timeout);
+
+ /*
+ * if we want to accept more than one request on a connection,
+ * we need to know which http server it belongs to.
+ */
+ evcon->http_server = http;
+ TAILQ_INSERT_TAIL(&http->connections, evcon, next);
+
+ if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ evhttp_connection_free(evcon);
+}
+
+
+/*
+ * Network helper functions that we do not want to export to the rest of
+ * the world.
+ */
+#if 0 /* Unused */
+static struct addrinfo *
+addr_from_name(char *address)
+{
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo ai, *aitop;
+ int ai_result;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_RAW;
+ ai.ai_flags = 0;
+ if ((ai_result = getaddrinfo(address, NULL, &ai, &aitop)) != 0) {
+ if ( ai_result == EAI_SYSTEM )
+ event_warn("getaddrinfo");
+ else
+ event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
+ }
+
+ return (aitop);
+#else
+ assert(0);
+ return NULL; /* XXXXX Use gethostbyname, if this function is ever used. */
+#endif
+}
+#endif
+
+static void
+name_from_addr(struct sockaddr *sa, socklen_t salen,
+ char **phost, char **pport)
+{
+#ifdef HAVE_GETNAMEINFO
+ /* XXXX not threadsafe. */
+ static char ntop[NI_MAXHOST];
+ static char strport[NI_MAXSERV];
+ int ni_result;
+
+ if ((ni_result = getnameinfo(sa, salen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
+ if (ni_result == EAI_SYSTEM)
+ event_err(1, "getnameinfo failed");
+ else
+ event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
+ }
+
+ *phost = ntop;
+ *pport = strport;
+#else
+ /* XXXX */
+#endif
+}
+
+/* Either connect or bind */
+
+static int
+bind_socket_ai(struct addrinfo *ai)
+{
+ int fd, on = 1, r;
+ int serrno;
+
+ /* Create listen socket */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ event_warn("socket");
+ return (-1);
+ }
+
+ if (evutil_make_socket_nonblocking(fd) < 0)
+ goto out;
+
+#ifndef WIN32
+ if (fcntl(fd, F_SETFD, 1) == -1) {
+ event_warn("fcntl(F_SETFD)");
+ goto out;
+ }
+#endif
+
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
+
+ r = bind(fd, ai->ai_addr, ai->ai_addrlen);
+ if (r == -1)
+ goto out;
+
+ return (fd);
+
+ out:
+ serrno = EVUTIL_SOCKET_ERROR();
+ EVUTIL_CLOSESOCKET(fd);
+ EVUTIL_SET_SOCKET_ERROR(serrno);
+ return (-1);
+}
+
+static struct addrinfo *
+make_addrinfo(const char *address, u_short port)
+{
+ struct addrinfo *aitop = NULL;
+
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo ai;
+ char strport[NI_MAXSERV];
+ int ai_result;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */
+ snprintf(strport, sizeof(strport), "%d", port);
+ if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) {
+ if ( ai_result == EAI_SYSTEM )
+ event_warn("getaddrinfo");
+ else
+ event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
+ return (NULL);
+ }
+#else
+ static int cur;
+ static struct addrinfo ai[2]; /* We will be returning the address of some of this memory so it has to last even after this call. */
+ if (++cur == 2) cur = 0; /* allow calling this function twice */
+
+ if (fake_getaddrinfo(address, &ai[cur]) < 0) {
+ event_warn("fake_getaddrinfo");
+ return (NULL);
+ }
+ aitop = &ai[cur];
+ ((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port);
+#endif
+
+ return (aitop);
+}
+
+static int
+bind_socket(const char *address, u_short port)
+{
+ int fd;
+ struct addrinfo *aitop = make_addrinfo(address, port);
+
+ if (aitop == NULL)
+ return (-1);
+
+ fd = bind_socket_ai(aitop);
+
+#ifdef HAVE_GETADDRINFO
+ freeaddrinfo(aitop);
+#else
+ fake_freeaddrinfo(aitop);
+#endif
+
+ return (fd);
+}
+
+static int
+socket_connect(int fd, const char *address, unsigned short port)
+{
+ struct addrinfo *ai = make_addrinfo(address, port);
+ int res = -1;
+
+ if (ai == NULL) {
+ event_debug(("%s: make_addrinfo: \"%s:%d\"",
+ __func__, address, port));
+ return (-1);
+ }
+
+ if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+#ifdef WIN32
+ int tmp_error = WSAGetLastError();
+ if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL &&
+ tmp_error != WSAEINPROGRESS) {
+ goto out;
+ }
+#else
+ if (errno != EINPROGRESS) {
+ goto out;
+ }
+#endif
+ }
+
+ /* everything is fine */
+ res = 0;
+
+out:
+#ifdef HAVE_GETADDRINFO
+ freeaddrinfo(ai);
+#else
+ fake_freeaddrinfo(ai);
+#endif
+
+ return (res);
+}
diff --git a/extra/libevent/kqueue.c b/extra/libevent/kqueue.c
new file mode 100644
index 00000000000..eec5a6ab6fb
--- /dev/null
+++ b/extra/libevent/kqueue.c
@@ -0,0 +1,425 @@
+/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
+
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * 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_WORKING_KQUEUE
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <sys/event.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* Some platforms apparently define the udata field of struct kevent as
+ * intptr_t, whereas others define it as void*. There doesn't seem to be an
+ * easy way to tell them apart via autoconf, so we need to use OS macros. */
+#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__)
+#define PTR_TO_UDATA(x) ((intptr_t)(x))
+#else
+#define PTR_TO_UDATA(x) (x)
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+#include "event-internal.h"
+
+#define EVLIST_X_KQINKERNEL 0x1000
+
+#define NEVENT 64
+
+struct kqop {
+ struct kevent *changes;
+ int nchanges;
+ struct kevent *events;
+ int nevents;
+ int kq;
+ pid_t pid;
+};
+
+static void *kq_init (struct event_base *);
+static int kq_add (void *, struct event *);
+static int kq_del (void *, struct event *);
+static int kq_dispatch (struct event_base *, void *, struct timeval *);
+static int kq_insert (struct kqop *, struct kevent *);
+static void kq_dealloc (struct event_base *, void *);
+
+const struct eventop kqops = {
+ "kqueue",
+ kq_init,
+ kq_add,
+ kq_del,
+ kq_dispatch,
+ kq_dealloc,
+ 1 /* need reinit */
+};
+
+static void *
+kq_init(struct event_base *base)
+{
+ int kq;
+ struct kqop *kqueueop;
+
+ /* Disable kqueue when this environment variable is set */
+ if (getenv("EVENT_NOKQUEUE"))
+ return (NULL);
+
+ if (!(kqueueop = calloc(1, sizeof(struct kqop))))
+ return (NULL);
+
+ /* Initalize the kernel queue */
+
+ if ((kq = kqueue()) == -1) {
+ event_warn("kqueue");
+ free (kqueueop);
+ return (NULL);
+ }
+
+ kqueueop->kq = kq;
+
+ kqueueop->pid = getpid();
+
+ /* Initalize fields */
+ kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
+ if (kqueueop->changes == NULL) {
+ free (kqueueop);
+ return (NULL);
+ }
+ kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
+ if (kqueueop->events == NULL) {
+ free (kqueueop->changes);
+ free (kqueueop);
+ return (NULL);
+ }
+ kqueueop->nevents = NEVENT;
+
+ /* Check for Mac OS X kqueue bug. */
+ kqueueop->changes[0].ident = -1;
+ kqueueop->changes[0].filter = EVFILT_READ;
+ kqueueop->changes[0].flags = EV_ADD;
+ /*
+ * If kqueue works, then kevent will succeed, and it will
+ * stick an error in events[0]. If kqueue is broken, then
+ * kevent will fail.
+ */
+ if (kevent(kq,
+ kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
+ ((int) kqueueop->events[0].ident) != -1 ||
+ kqueueop->events[0].flags != EV_ERROR) {
+ event_warn("%s: detected broken kqueue; not using.", __func__);
+ free(kqueueop->changes);
+ free(kqueueop->events);
+ free(kqueueop);
+ close(kq);
+ return (NULL);
+ }
+
+ return (kqueueop);
+}
+
+static int
+kq_insert(struct kqop *kqop, struct kevent *kev)
+{
+ int nevents = kqop->nevents;
+
+ if (kqop->nchanges == nevents) {
+ struct kevent *newchange;
+ struct kevent *newresult;
+
+ nevents *= 2;
+
+ newchange = realloc(kqop->changes,
+ nevents * sizeof(struct kevent));
+ if (newchange == NULL) {
+ event_warn("%s: malloc", __func__);
+ return (-1);
+ }
+ kqop->changes = newchange;
+
+ newresult = realloc(kqop->events,
+ nevents * sizeof(struct kevent));
+
+ /*
+ * If we fail, we don't have to worry about freeing,
+ * the next realloc will pick it up.
+ */
+ if (newresult == NULL) {
+ event_warn("%s: malloc", __func__);
+ return (-1);
+ }
+ kqop->events = newresult;
+
+ kqop->nevents = nevents;
+ }
+
+ memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
+
+ event_debug(("%s: fd %d %s%s",
+ __func__, kev->ident,
+ kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
+ kev->flags == EV_DELETE ? " (del)" : ""));
+
+ return (0);
+}
+
+static void
+kq_sighandler(int sig)
+{
+ /* Do nothing here */
+}
+
+static int
+kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ struct kqop *kqop = arg;
+ struct kevent *changes = kqop->changes;
+ struct kevent *events = kqop->events;
+ struct event *ev;
+ struct timespec ts, *ts_p = NULL;
+ int i, res;
+
+ if (tv != NULL) {
+ TIMEVAL_TO_TIMESPEC(tv, &ts);
+ ts_p = &ts;
+ }
+
+ res = kevent(kqop->kq, changes, kqop->nchanges,
+ events, kqop->nevents, ts_p);
+ kqop->nchanges = 0;
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("kevent");
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ event_debug(("%s: kevent reports %d", __func__, res));
+
+ for (i = 0; i < res; i++) {
+ int which = 0;
+
+ if (events[i].flags & EV_ERROR) {
+ /*
+ * Error messages that can happen, when a delete fails.
+ * EBADF happens when the file discriptor has been
+ * closed,
+ * ENOENT when the file discriptor was closed and
+ * then reopened.
+ * EINVAL for some reasons not understood; EINVAL
+ * should not be returned ever; but FreeBSD does :-\
+ * An error is also indicated when a callback deletes
+ * an event we are still processing. In that case
+ * the data field is set to ENOENT.
+ */
+ if (events[i].data == EBADF ||
+ events[i].data == EINVAL ||
+ events[i].data == ENOENT)
+ continue;
+ errno = events[i].data;
+ return (-1);
+ }
+
+ ev = (struct event *)events[i].udata;
+
+ if (events[i].filter == EVFILT_READ) {
+ which |= EV_READ;
+ } else if (events[i].filter == EVFILT_WRITE) {
+ which |= EV_WRITE;
+ } else if (events[i].filter == EVFILT_SIGNAL) {
+ which |= EV_SIGNAL;
+ }
+
+ if (!which)
+ continue;
+
+ if (!(ev->ev_events & EV_PERSIST))
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+
+ event_active(ev, which,
+ ev->ev_events & EV_SIGNAL ? events[i].data : 1);
+ }
+
+ return (0);
+}
+
+
+static int
+kq_add(void *arg, struct event *ev)
+{
+ struct kqop *kqop = arg;
+ struct kevent kev;
+
+ if (ev->ev_events & EV_SIGNAL) {
+ int nsignal = EVENT_SIGNAL(ev);
+ struct timespec timeout = { 0, 0 };
+
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = nsignal;
+ kev.filter = EVFILT_SIGNAL;
+ kev.flags = EV_ADD;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = PTR_TO_UDATA(ev);
+
+ /* Be ready for the signal if it is sent any time between
+ * now and the next call to kq_dispatch. */
+ if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
+ return (-1);
+
+ if (_evsignal_set_handler(ev->ev_base, nsignal,
+ kq_sighandler) == -1)
+ return (-1);
+
+ ev->ev_flags |= EVLIST_X_KQINKERNEL;
+ return (0);
+ }
+
+ if (ev->ev_events & EV_READ) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_READ;
+#ifdef NOTE_EOF
+ /* Make it behave like select() and poll() */
+ kev.fflags = NOTE_EOF;
+#endif
+ kev.flags = EV_ADD;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = PTR_TO_UDATA(ev);
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags |= EVLIST_X_KQINKERNEL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_WRITE;
+ kev.flags = EV_ADD;
+ if (!(ev->ev_events & EV_PERSIST))
+ kev.flags |= EV_ONESHOT;
+ kev.udata = PTR_TO_UDATA(ev);
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags |= EVLIST_X_KQINKERNEL;
+ }
+
+ return (0);
+}
+
+static int
+kq_del(void *arg, struct event *ev)
+{
+ struct kqop *kqop = arg;
+ struct kevent kev;
+
+ if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
+ return (0);
+
+ if (ev->ev_events & EV_SIGNAL) {
+ int nsignal = EVENT_SIGNAL(ev);
+
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = nsignal;
+ kev.filter = EVFILT_SIGNAL;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ if (_evsignal_restore_handler(ev->ev_base, nsignal) == -1)
+ return (-1);
+
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ return (0);
+ }
+
+ if (ev->ev_events & EV_READ) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_READ;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ memset(&kev, 0, sizeof(kev));
+ kev.ident = ev->ev_fd;
+ kev.filter = EVFILT_WRITE;
+ kev.flags = EV_DELETE;
+
+ if (kq_insert(kqop, &kev) == -1)
+ return (-1);
+
+ ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ }
+
+ return (0);
+}
+
+static void
+kq_dealloc(struct event_base *base, void *arg)
+{
+ struct kqop *kqop = arg;
+
+ if (kqop->changes)
+ free(kqop->changes);
+ if (kqop->events)
+ free(kqop->events);
+ if (kqop->kq >= 0 && kqop->pid == getpid())
+ close(kqop->kq);
+ memset(kqop, 0, sizeof(struct kqop));
+ free(kqop);
+}
+
+#endif /* HAVE_WORKING_KQUEUE */
diff --git a/extra/libevent/log.c b/extra/libevent/log.c
new file mode 100644
index 00000000000..aab47182af5
--- /dev/null
+++ b/extra/libevent/log.c
@@ -0,0 +1,218 @@
+/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * log.c
+ *
+ * Based on err.c, which was adapted from OpenBSD libc *err* *warn* code.
+ *
+ * Copyright (c) 2005 Nick Mathewson <nickm@freehaven.net>
+ *
+ * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
+ *
+ * Copyright (c) 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.
+ */
+
+#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>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include "event.h"
+
+#include "log.h"
+
+static void _warn_helper(int severity, int log_errno, const char *fmt,
+ va_list ap);
+static void event_log(int severity, const char *msg);
+
+static int
+event_vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ int r;
+ if (size == 0)
+ return -1;
+#ifdef WIN32
+ r = _vsnprintf(str, size, format, args);
+#else
+ r = vsnprintf(str, size, format, args);
+#endif
+ str[size-1] = '\0';
+ if (r < 0 || ((size_t)r) >= size) {
+ /* different platforms behave differently on overflow;
+ * handle both kinds. */
+ return -1;
+ }
+ return r;
+}
+
+static int
+event_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int r;
+ va_start(ap, format);
+ r = event_vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return r;
+}
+
+void
+event_err(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_ERR, errno, fmt, ap);
+ va_end(ap);
+ exit(eval);
+}
+
+void
+event_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_WARN, errno, fmt, ap);
+ va_end(ap);
+}
+
+void
+event_errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_ERR, -1, fmt, ap);
+ va_end(ap);
+ exit(eval);
+}
+
+void
+event_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_WARN, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+event_msgx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_MSG, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+_event_debugx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap);
+ va_end(ap);
+}
+
+static void
+_warn_helper(int severity, int log_errno, const char *fmt, va_list ap)
+{
+ char buf[1024];
+ size_t len;
+
+ if (fmt != NULL)
+ event_vsnprintf(buf, sizeof(buf), fmt, ap);
+ else
+ buf[0] = '\0';
+
+ if (log_errno >= 0) {
+ len = strlen(buf);
+ if (len < sizeof(buf) - 3) {
+ event_snprintf(buf + len, sizeof(buf) - len, ": %s",
+ strerror(log_errno));
+ }
+ }
+
+ event_log(severity, buf);
+}
+
+static event_log_cb log_fn = NULL;
+
+void
+event_set_log_callback(event_log_cb cb)
+{
+ log_fn = cb;
+}
+
+static void
+event_log(int severity, const char *msg)
+{
+ if (log_fn)
+ log_fn(severity, msg);
+ else {
+ const char *severity_str;
+ switch (severity) {
+ case _EVENT_LOG_DEBUG:
+ severity_str = "debug";
+ break;
+ case _EVENT_LOG_MSG:
+ severity_str = "msg";
+ break;
+ case _EVENT_LOG_WARN:
+ severity_str = "warn";
+ break;
+ case _EVENT_LOG_ERR:
+ severity_str = "err";
+ break;
+ default:
+ severity_str = "???";
+ break;
+ }
+ (void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
+ }
+}
diff --git a/extra/libevent/log.h b/extra/libevent/log.h
new file mode 100644
index 00000000000..7bc6632b8dd
--- /dev/null
+++ b/extra/libevent/log.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * 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 _LOG_H_
+#define _LOG_H_
+
+#ifdef __GNUC__
+#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
+#else
+#define EV_CHECK_FMT(a,b)
+#endif
+
+void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
+void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
+void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2);
+
+#ifdef USE_DEBUG
+#define event_debug(x) _event_debugx x
+#else
+#define event_debug(x) do {;} while (0)
+#endif
+
+#undef EV_CHECK_FMT
+
+#endif
diff --git a/extra/libevent/min_heap.h b/extra/libevent/min_heap.h
new file mode 100644
index 00000000000..389d6dbed4e
--- /dev/null
+++ b/extra/libevent/min_heap.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com>
+ * 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 _MIN_HEAP_H_
+#define _MIN_HEAP_H_
+
+#include "event.h"
+
+typedef struct min_heap
+{
+ struct event** p;
+ unsigned n, a;
+} min_heap_t;
+
+static inline void min_heap_ctor(min_heap_t* s);
+static inline void min_heap_dtor(min_heap_t* s);
+static inline void min_heap_elem_init(struct event* e);
+static inline int min_heap_elem_greater(struct event *a, struct event *b);
+static inline int min_heap_empty(min_heap_t* s);
+static inline unsigned min_heap_size(min_heap_t* s);
+static inline struct event* min_heap_top(min_heap_t* s);
+static inline int min_heap_reserve(min_heap_t* s, unsigned n);
+static inline int min_heap_push(min_heap_t* s, struct event* e);
+static inline struct event* min_heap_pop(min_heap_t* s);
+static inline int min_heap_erase(min_heap_t* s, struct event* e);
+static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e);
+static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e);
+
+int min_heap_elem_greater(struct event *a, struct event *b)
+{
+ return timercmp(&a->ev_timeout, &b->ev_timeout, >);
+}
+
+void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; }
+void min_heap_dtor(min_heap_t* s) { free(s->p); }
+void min_heap_elem_init(struct event* e) { e->min_heap_idx = -1; }
+int min_heap_empty(min_heap_t* s) { return 0u == s->n; }
+unsigned min_heap_size(min_heap_t* s) { return s->n; }
+struct event* min_heap_top(min_heap_t* s) { return s->n ? *s->p : 0; }
+
+int min_heap_push(min_heap_t* s, struct event* e)
+{
+ if(min_heap_reserve(s, s->n + 1))
+ return -1;
+ min_heap_shift_up_(s, s->n++, e);
+ return 0;
+}
+
+struct event* min_heap_pop(min_heap_t* s)
+{
+ if(s->n)
+ {
+ struct event* e = *s->p;
+ e->min_heap_idx = -1;
+ min_heap_shift_down_(s, 0u, s->p[--s->n]);
+ return e;
+ }
+ return 0;
+}
+
+int min_heap_erase(min_heap_t* s, struct event* e)
+{
+ if(((unsigned int)-1) != e->min_heap_idx)
+ {
+ min_heap_shift_down_(s, e->min_heap_idx, s->p[--s->n]);
+ e->min_heap_idx = -1;
+ return 0;
+ }
+ return -1;
+}
+
+int min_heap_reserve(min_heap_t* s, unsigned n)
+{
+ if(s->a < n)
+ {
+ struct event** p;
+ unsigned a = s->a ? s->a * 2 : 8;
+ if(a < n)
+ a = n;
+ if(!(p = (struct event**)realloc(s->p, a * sizeof *p)))
+ return -1;
+ s->p = p;
+ s->a = a;
+ }
+ return 0;
+}
+
+void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)
+{
+ unsigned parent = (hole_index - 1) / 2;
+ while(hole_index && min_heap_elem_greater(s->p[parent], e))
+ {
+ (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index;
+ hole_index = parent;
+ parent = (hole_index - 1) / 2;
+ }
+ (s->p[hole_index] = e)->min_heap_idx = hole_index;
+}
+
+void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e)
+{
+ unsigned min_child = 2 * (hole_index + 1);
+ while(min_child <= s->n)
+ {
+ min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]);
+ if(!(min_heap_elem_greater(e, s->p[min_child])))
+ break;
+ (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index;
+ hole_index = min_child;
+ min_child = 2 * (hole_index + 1);
+ }
+ min_heap_shift_up_(s, hole_index, e);
+}
+
+#endif /* _MIN_HEAP_H_ */
diff --git a/extra/libevent/poll.c b/extra/libevent/poll.c
new file mode 100644
index 00000000000..6c99e2ca79b
--- /dev/null
+++ b/extra/libevent/poll.c
@@ -0,0 +1,378 @@
+/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
+ * 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_POLL
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <sys/queue.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+struct pollop {
+ int event_count; /* Highest number alloc */
+ int nfds; /* Size of event_* */
+ int fd_count; /* Size of idxplus1_by_fd */
+ struct pollfd *event_set;
+ struct event **event_r_back;
+ struct event **event_w_back;
+ int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
+ * that 0 (which is easy to memset) can mean
+ * "no entry." */
+};
+
+static void *poll_init (struct event_base *);
+static int poll_add (void *, struct event *);
+static int poll_del (void *, struct event *);
+static int poll_dispatch (struct event_base *, void *, struct timeval *);
+static void poll_dealloc (struct event_base *, void *);
+
+const struct eventop pollops = {
+ "poll",
+ poll_init,
+ poll_add,
+ poll_del,
+ poll_dispatch,
+ poll_dealloc,
+ 0
+};
+
+static void *
+poll_init(struct event_base *base)
+{
+ struct pollop *pollop;
+
+ /* Disable poll when this environment variable is set */
+ if (getenv("EVENT_NOPOLL"))
+ return (NULL);
+
+ if (!(pollop = calloc(1, sizeof(struct pollop))))
+ return (NULL);
+
+ evsignal_init(base);
+
+ return (pollop);
+}
+
+#ifdef CHECK_INVARIANTS
+static void
+poll_check_ok(struct pollop *pop)
+{
+ int i, idx;
+ struct event *ev;
+
+ for (i = 0; i < pop->fd_count; ++i) {
+ idx = pop->idxplus1_by_fd[i]-1;
+ if (idx < 0)
+ continue;
+ assert(pop->event_set[idx].fd == i);
+ if (pop->event_set[idx].events & POLLIN) {
+ ev = pop->event_r_back[idx];
+ assert(ev);
+ assert(ev->ev_events & EV_READ);
+ assert(ev->ev_fd == i);
+ }
+ if (pop->event_set[idx].events & POLLOUT) {
+ ev = pop->event_w_back[idx];
+ assert(ev);
+ assert(ev->ev_events & EV_WRITE);
+ assert(ev->ev_fd == i);
+ }
+ }
+ for (i = 0; i < pop->nfds; ++i) {
+ struct pollfd *pfd = &pop->event_set[i];
+ assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
+ }
+}
+#else
+#define poll_check_ok(pop)
+#endif
+
+static int
+poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int res, i, msec = -1, nfds;
+ struct pollop *pop = arg;
+
+ poll_check_ok(pop);
+
+ if (tv != NULL)
+ msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
+
+ nfds = pop->nfds;
+ res = poll(pop->event_set, nfds, msec);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("poll");
+ return (-1);
+ }
+
+ evsignal_process(base);
+ return (0);
+ } else if (base->sig.evsignal_caught) {
+ evsignal_process(base);
+ }
+
+ event_debug(("%s: poll reports %d", __func__, res));
+
+ if (res == 0)
+ return (0);
+
+ for (i = 0; i < nfds; i++) {
+ int what = pop->event_set[i].revents;
+ struct event *r_ev = NULL, *w_ev = NULL;
+ if (!what)
+ continue;
+
+ res = 0;
+
+ /* If the file gets closed notify */
+ if (what & (POLLHUP|POLLERR))
+ what |= POLLIN|POLLOUT;
+ if (what & POLLIN) {
+ res |= EV_READ;
+ r_ev = pop->event_r_back[i];
+ }
+ if (what & POLLOUT) {
+ res |= EV_WRITE;
+ w_ev = pop->event_w_back[i];
+ }
+ if (res == 0)
+ continue;
+
+ if (r_ev && (res & r_ev->ev_events)) {
+ event_active(r_ev, res & r_ev->ev_events, 1);
+ }
+ if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
+ event_active(w_ev, res & w_ev->ev_events, 1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+poll_add(void *arg, struct event *ev)
+{
+ struct pollop *pop = arg;
+ struct pollfd *pfd = NULL;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(ev));
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ poll_check_ok(pop);
+ if (pop->nfds + 1 >= pop->event_count) {
+ struct pollfd *tmp_event_set;
+ struct event **tmp_event_r_back;
+ struct event **tmp_event_w_back;
+ int tmp_event_count;
+
+ if (pop->event_count < 32)
+ tmp_event_count = 32;
+ else
+ tmp_event_count = pop->event_count * 2;
+
+ /* We need more file descriptors */
+ tmp_event_set = realloc(pop->event_set,
+ tmp_event_count * sizeof(struct pollfd));
+ if (tmp_event_set == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ pop->event_set = tmp_event_set;
+
+ tmp_event_r_back = realloc(pop->event_r_back,
+ tmp_event_count * sizeof(struct event *));
+ if (tmp_event_r_back == NULL) {
+ /* event_set overallocated; that's okay. */
+ event_warn("realloc");
+ return (-1);
+ }
+ pop->event_r_back = tmp_event_r_back;
+
+ tmp_event_w_back = realloc(pop->event_w_back,
+ tmp_event_count * sizeof(struct event *));
+ if (tmp_event_w_back == NULL) {
+ /* event_set and event_r_back overallocated; that's
+ * okay. */
+ event_warn("realloc");
+ return (-1);
+ }
+ pop->event_w_back = tmp_event_w_back;
+
+ pop->event_count = tmp_event_count;
+ }
+ if (ev->ev_fd >= pop->fd_count) {
+ int *tmp_idxplus1_by_fd;
+ int new_count;
+ if (pop->fd_count < 32)
+ new_count = 32;
+ else
+ new_count = pop->fd_count * 2;
+ while (new_count <= ev->ev_fd)
+ new_count *= 2;
+ tmp_idxplus1_by_fd =
+ realloc(pop->idxplus1_by_fd, new_count * sizeof(int));
+ if (tmp_idxplus1_by_fd == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
+ memset(pop->idxplus1_by_fd + pop->fd_count,
+ 0, sizeof(int)*(new_count - pop->fd_count));
+ pop->fd_count = new_count;
+ }
+
+ i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
+ if (i >= 0) {
+ pfd = &pop->event_set[i];
+ } else {
+ i = pop->nfds++;
+ pfd = &pop->event_set[i];
+ pfd->events = 0;
+ pfd->fd = ev->ev_fd;
+ pop->event_w_back[i] = pop->event_r_back[i] = NULL;
+ pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
+ }
+
+ pfd->revents = 0;
+ if (ev->ev_events & EV_WRITE) {
+ pfd->events |= POLLOUT;
+ pop->event_w_back[i] = ev;
+ }
+ if (ev->ev_events & EV_READ) {
+ pfd->events |= POLLIN;
+ pop->event_r_back[i] = ev;
+ }
+ poll_check_ok(pop);
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+static int
+poll_del(void *arg, struct event *ev)
+{
+ struct pollop *pop = arg;
+ struct pollfd *pfd = NULL;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(ev));
+
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);
+
+ poll_check_ok(pop);
+ i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
+ if (i < 0)
+ return (-1);
+
+ /* Do we still want to read or write? */
+ pfd = &pop->event_set[i];
+ if (ev->ev_events & EV_READ) {
+ pfd->events &= ~POLLIN;
+ pop->event_r_back[i] = NULL;
+ }
+ if (ev->ev_events & EV_WRITE) {
+ pfd->events &= ~POLLOUT;
+ pop->event_w_back[i] = NULL;
+ }
+ poll_check_ok(pop);
+ if (pfd->events)
+ /* Another event cares about that fd. */
+ return (0);
+
+ /* Okay, so we aren't interested in that fd anymore. */
+ pop->idxplus1_by_fd[ev->ev_fd] = 0;
+
+ --pop->nfds;
+ if (i != pop->nfds) {
+ /*
+ * Shift the last pollfd down into the now-unoccupied
+ * position.
+ */
+ memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
+ sizeof(struct pollfd));
+ pop->event_r_back[i] = pop->event_r_back[pop->nfds];
+ pop->event_w_back[i] = pop->event_w_back[pop->nfds];
+ pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
+ }
+
+ poll_check_ok(pop);
+ return (0);
+}
+
+static void
+poll_dealloc(struct event_base *base, void *arg)
+{
+ struct pollop *pop = arg;
+
+ evsignal_dealloc(base);
+ if (pop->event_set)
+ free(pop->event_set);
+ if (pop->event_r_back)
+ free(pop->event_r_back);
+ if (pop->event_w_back)
+ free(pop->event_w_back);
+ if (pop->idxplus1_by_fd)
+ free(pop->idxplus1_by_fd);
+
+ memset(pop, 0, sizeof(struct pollop));
+ free(pop);
+}
+
+#endif /* HAVE_POLL */
diff --git a/extra/libevent/select.c b/extra/libevent/select.c
new file mode 100644
index 00000000000..b8bd1a1c361
--- /dev/null
+++ b/extra/libevent/select.c
@@ -0,0 +1,356 @@
+/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * 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_SELECT
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "log.h"
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+
+struct selectop {
+ int event_fds; /* Highest fd in fd set */
+ int event_fdsz;
+ fd_set *event_readset_in;
+ fd_set *event_writeset_in;
+ fd_set *event_readset_out;
+ fd_set *event_writeset_out;
+ struct event **event_r_by_fd;
+ struct event **event_w_by_fd;
+};
+
+static void *select_init (struct event_base *);
+static int select_add (void *, struct event *);
+static int select_del (void *, struct event *);
+static int select_dispatch (struct event_base *, void *, struct timeval *);
+static void select_dealloc (struct event_base *, void *);
+
+const struct eventop selectops = {
+ "select",
+ select_init,
+ select_add,
+ select_del,
+ select_dispatch,
+ select_dealloc,
+ 0
+};
+
+static int select_resize(struct selectop *sop, int fdsz);
+
+static void *
+select_init(struct event_base *base)
+{
+ struct selectop *sop;
+
+ /* Disable select when this environment variable is set */
+ if (getenv("EVENT_NOSELECT"))
+ return (NULL);
+
+ if (!(sop = calloc(1, sizeof(struct selectop))))
+ return (NULL);
+
+ select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
+
+ evsignal_init(base);
+
+ return (sop);
+}
+
+#ifdef CHECK_INVARIANTS
+static void
+check_selectop(struct selectop *sop)
+{
+ int i;
+ for (i = 0; i <= sop->event_fds; ++i) {
+ if (FD_ISSET(i, sop->event_readset_in)) {
+ assert(sop->event_r_by_fd[i]);
+ assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
+ assert(sop->event_r_by_fd[i]->ev_fd == i);
+ } else {
+ assert(! sop->event_r_by_fd[i]);
+ }
+ if (FD_ISSET(i, sop->event_writeset_in)) {
+ assert(sop->event_w_by_fd[i]);
+ assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
+ assert(sop->event_w_by_fd[i]->ev_fd == i);
+ } else {
+ assert(! sop->event_w_by_fd[i]);
+ }
+ }
+
+}
+#else
+#define check_selectop(sop) do { (void) sop; } while (0)
+#endif
+
+static int
+select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ int res, i;
+ struct selectop *sop = arg;
+
+ check_selectop(sop);
+
+ memcpy(sop->event_readset_out, sop->event_readset_in,
+ sop->event_fdsz);
+ memcpy(sop->event_writeset_out, sop->event_writeset_in,
+ sop->event_fdsz);
+
+ res = select(sop->event_fds + 1, sop->event_readset_out,
+ sop->event_writeset_out, NULL, tv);
+
+ check_selectop(sop);
+
+ if (res == -1) {
+ if (errno != EINTR) {
+ event_warn("select");
+ return (-1);
+ }
+
+ evsignal_process(base);
+ return (0);
+ } else if (base->sig.evsignal_caught) {
+ evsignal_process(base);
+ }
+
+ event_debug(("%s: select reports %d", __func__, res));
+
+ check_selectop(sop);
+ for (i = 0; i <= sop->event_fds; ++i) {
+ struct event *r_ev = NULL, *w_ev = NULL;
+ res = 0;
+ if (FD_ISSET(i, sop->event_readset_out)) {
+ r_ev = sop->event_r_by_fd[i];
+ res |= EV_READ;
+ }
+ if (FD_ISSET(i, sop->event_writeset_out)) {
+ w_ev = sop->event_w_by_fd[i];
+ res |= EV_WRITE;
+ }
+ if (r_ev && (res & r_ev->ev_events)) {
+ event_active(r_ev, res & r_ev->ev_events, 1);
+ }
+ if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
+ event_active(w_ev, res & w_ev->ev_events, 1);
+ }
+ }
+ check_selectop(sop);
+
+ return (0);
+}
+
+
+static int
+select_resize(struct selectop *sop, int fdsz)
+{
+ int n_events, n_events_old;
+
+ fd_set *readset_in = NULL;
+ fd_set *writeset_in = NULL;
+ fd_set *readset_out = NULL;
+ fd_set *writeset_out = NULL;
+ struct event **r_by_fd = NULL;
+ struct event **w_by_fd = NULL;
+
+ n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
+ n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
+
+ if (sop->event_readset_in)
+ check_selectop(sop);
+
+ if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
+ goto error;
+ sop->event_readset_in = readset_in;
+ if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
+ goto error;
+ sop->event_readset_out = readset_out;
+ if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
+ goto error;
+ sop->event_writeset_in = writeset_in;
+ if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
+ goto error;
+ sop->event_writeset_out = writeset_out;
+ if ((r_by_fd = realloc(sop->event_r_by_fd,
+ n_events*sizeof(struct event*))) == NULL)
+ goto error;
+ sop->event_r_by_fd = r_by_fd;
+ if ((w_by_fd = realloc(sop->event_w_by_fd,
+ n_events * sizeof(struct event*))) == NULL)
+ goto error;
+ sop->event_w_by_fd = w_by_fd;
+
+ memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+ memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
+ fdsz - sop->event_fdsz);
+ memset(sop->event_r_by_fd + n_events_old, 0,
+ (n_events-n_events_old) * sizeof(struct event*));
+ memset(sop->event_w_by_fd + n_events_old, 0,
+ (n_events-n_events_old) * sizeof(struct event*));
+
+ sop->event_fdsz = fdsz;
+ check_selectop(sop);
+
+ return (0);
+
+ error:
+ event_warn("malloc");
+ return (-1);
+}
+
+
+static int
+select_add(void *arg, struct event *ev)
+{
+ struct selectop *sop = arg;
+
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_add(ev));
+
+ check_selectop(sop);
+ /*
+ * Keep track of the highest fd, so that we can calculate the size
+ * of the fd_sets for select(2)
+ */
+ if (sop->event_fds < ev->ev_fd) {
+ unsigned int fdsz = sop->event_fdsz;
+
+ if (fdsz < sizeof(fd_mask))
+ fdsz = sizeof(fd_mask);
+
+ while (fdsz <
+ (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
+ fdsz *= 2;
+
+ if (fdsz != (unsigned int) sop->event_fdsz) {
+ if (select_resize(sop, fdsz)) {
+ check_selectop(sop);
+ return (-1);
+ }
+ }
+
+ sop->event_fds = ev->ev_fd;
+ }
+
+ if (ev->ev_events & EV_READ) {
+ FD_SET(ev->ev_fd, sop->event_readset_in);
+ sop->event_r_by_fd[ev->ev_fd] = ev;
+ }
+ if (ev->ev_events & EV_WRITE) {
+ FD_SET(ev->ev_fd, sop->event_writeset_in);
+ sop->event_w_by_fd[ev->ev_fd] = ev;
+ }
+ check_selectop(sop);
+
+ return (0);
+}
+
+/*
+ * Nothing to be done here.
+ */
+
+static int
+select_del(void *arg, struct event *ev)
+{
+ struct selectop *sop = arg;
+
+ check_selectop(sop);
+ if (ev->ev_events & EV_SIGNAL)
+ return (evsignal_del(ev));
+
+ if (sop->event_fds < ev->ev_fd) {
+ check_selectop(sop);
+ return (0);
+ }
+
+ if (ev->ev_events & EV_READ) {
+ FD_CLR(ev->ev_fd, sop->event_readset_in);
+ sop->event_r_by_fd[ev->ev_fd] = NULL;
+ }
+
+ if (ev->ev_events & EV_WRITE) {
+ FD_CLR(ev->ev_fd, sop->event_writeset_in);
+ sop->event_w_by_fd[ev->ev_fd] = NULL;
+ }
+
+ check_selectop(sop);
+ return (0);
+}
+
+static void
+select_dealloc(struct event_base *base, void *arg)
+{
+ struct selectop *sop = arg;
+
+ evsignal_dealloc(base);
+ if (sop->event_readset_in)
+ free(sop->event_readset_in);
+ if (sop->event_writeset_in)
+ free(sop->event_writeset_in);
+ if (sop->event_readset_out)
+ free(sop->event_readset_out);
+ if (sop->event_writeset_out)
+ free(sop->event_writeset_out);
+ if (sop->event_r_by_fd)
+ free(sop->event_r_by_fd);
+ if (sop->event_w_by_fd)
+ free(sop->event_w_by_fd);
+
+ memset(sop, 0, sizeof(struct selectop));
+ free(sop);
+}
+
+#endif /* HAVE_SELECT */
diff --git a/extra/libevent/signal.c b/extra/libevent/signal.c
new file mode 100644
index 00000000000..5eb9d3c606e
--- /dev/null
+++ b/extra/libevent/signal.c
@@ -0,0 +1,304 @@
+/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
+
+/*
+ * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
+ * 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>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <assert.h>
+
+#include "event.h"
+#include "event-internal.h"
+#include "evsignal.h"
+#include "evutil.h"
+#include "log.h"
+
+struct event_base *evsignal_base = NULL;
+
+static void evsignal_handler(int sig);
+
+/* Callback for when the signal handler write a byte to our signaling socket */
+static void
+evsignal_cb(int fd, short what __attribute__((unused)), void *arg __attribute__((unused)))
+{
+ static char signals[100];
+#ifdef WIN32
+ SSIZE_T n;
+#else
+ ssize_t n;
+#endif
+
+ n = recv(fd, signals, sizeof(signals), 0);
+ if (n == -1)
+ event_err(1, "%s: read", __func__);
+}
+
+#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
+
+void
+evsignal_init(struct event_base *base)
+{
+ /*
+ * Our signal handler is going to write to one end of the socket
+ * pair to wake up our event loop. The event loop then scans for
+ * signals that got delivered.
+ */
+ if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
+ event_err(1, "%s: socketpair", __func__);
+
+ FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
+ FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
+ base->sig.sh_old = NULL;
+ base->sig.sh_old_max = 0;
+ base->sig.evsignal_caught = 0;
+ memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
+
+ evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
+
+ event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
+ EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
+ base->sig.ev_signal.ev_base = base;
+ base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
+}
+
+/* Helper: set the signal handler for evsignal to handler in base, so that
+ * we can restore the original handler when we clear the current one. */
+int
+_evsignal_set_handler(struct event_base *base,
+ int evsignal, void (*handler)(int))
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#else
+ ev_sighandler_t sh;
+#endif
+ struct evsignal_info *sig = &base->sig;
+ void *p;
+
+ /*
+ * resize saved signal handler array up to the highest signal number.
+ * a dynamic array is used to keep footprint on the low side.
+ */
+ if (evsignal >= sig->sh_old_max) {
+ event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
+ __func__, evsignal, sig->sh_old_max));
+ sig->sh_old_max = evsignal + 1;
+ p = realloc(sig->sh_old, sig->sh_old_max * sizeof *sig->sh_old);
+ if (p == NULL) {
+ event_warn("realloc");
+ return (-1);
+ }
+ sig->sh_old = p;
+ }
+
+ /* allocate space for previous handler out of dynamic array */
+ sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
+ if (sig->sh_old[evsignal] == NULL) {
+ event_warn("malloc");
+ return (-1);
+ }
+
+ /* save previous handler and setup new handler */
+#ifdef HAVE_SIGACTION
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handler;
+ sa.sa_flags |= SA_RESTART;
+ sigfillset(&sa.sa_mask);
+
+ if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
+ event_warn("sigaction");
+ free(sig->sh_old[evsignal]);
+ return (-1);
+ }
+#else
+ if ((sh = signal(evsignal, handler)) == SIG_ERR) {
+ event_warn("signal");
+ free(sig->sh_old[evsignal]);
+ return (-1);
+ }
+ *sig->sh_old[evsignal] = sh;
+#endif
+
+ return (0);
+}
+
+int
+evsignal_add(struct event *ev)
+{
+ int evsignal;
+ struct event_base *base = ev->ev_base;
+ struct evsignal_info *sig = &ev->ev_base->sig;
+
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
+ evsignal = EVENT_SIGNAL(ev);
+
+ event_debug(("%s: %p: changing signal handler", __func__, ev));
+ if (_evsignal_set_handler(base, evsignal, evsignal_handler) == -1)
+ return (-1);
+
+ /* catch signals if they happen quickly */
+ evsignal_base = base;
+
+ if (!sig->ev_signal_added) {
+ sig->ev_signal_added = 1;
+ event_add(&sig->ev_signal, NULL);
+ }
+
+ return (0);
+}
+
+int
+_evsignal_restore_handler(struct event_base *base, int evsignal)
+{
+ int ret = 0;
+ struct evsignal_info *sig = &base->sig;
+#ifdef HAVE_SIGACTION
+ struct sigaction *sh;
+#else
+ ev_sighandler_t *sh;
+#endif
+
+ /* restore previous handler */
+ sh = sig->sh_old[evsignal];
+ sig->sh_old[evsignal] = NULL;
+#ifdef HAVE_SIGACTION
+ if (sigaction(evsignal, sh, NULL) == -1) {
+ event_warn("sigaction");
+ ret = -1;
+ }
+#else
+ if (signal(evsignal, *sh) == SIG_ERR) {
+ event_warn("signal");
+ ret = -1;
+ }
+#endif
+ free(sh);
+
+ return ret;
+}
+
+int
+evsignal_del(struct event *ev)
+{
+ event_debug(("%s: %p: restoring signal handler", __func__, ev));
+ return _evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev));
+}
+
+static void
+evsignal_handler(int sig)
+{
+ int save_errno = errno;
+
+ if(evsignal_base == NULL) {
+ event_warn(
+ "%s: received signal %d, but have no base configured",
+ __func__, sig);
+ return;
+ }
+
+ evsignal_base->sig.evsigcaught[sig]++;
+ evsignal_base->sig.evsignal_caught = 1;
+
+#ifndef HAVE_SIGACTION
+ signal(sig, evsignal_handler);
+#endif
+
+ /* Wake up our notification mechanism */
+ send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
+ errno = save_errno;
+}
+
+void
+evsignal_process(struct event_base *base)
+{
+ struct event *ev;
+ sig_atomic_t ncalls;
+
+ base->sig.evsignal_caught = 0;
+ TAILQ_FOREACH(ev, &base->sig.signalqueue, ev_signal_next) {
+ ncalls = base->sig.evsigcaught[EVENT_SIGNAL(ev)];
+ if (ncalls) {
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, EV_SIGNAL, ncalls);
+ base->sig.evsigcaught[EVENT_SIGNAL(ev)] = 0;
+ }
+ }
+}
+
+void
+evsignal_dealloc(struct event_base *base)
+{
+ if(base->sig.ev_signal_added) {
+ event_del(&base->sig.ev_signal);
+ base->sig.ev_signal_added = 0;
+ }
+ assert(TAILQ_EMPTY(&base->sig.signalqueue));
+
+ EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]);
+ base->sig.ev_signal_pair[0] = -1;
+ EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]);
+ base->sig.ev_signal_pair[1] = -1;
+ base->sig.sh_old_max = 0;
+
+ /* per index frees are handled in evsignal_del() */
+ free(base->sig.sh_old);
+}
diff --git a/extra/libevent/strlcpy-internal.h b/extra/libevent/strlcpy-internal.h
new file mode 100644
index 00000000000..22b5f61d45e
--- /dev/null
+++ b/extra/libevent/strlcpy-internal.h
@@ -0,0 +1,23 @@
+#ifndef _STRLCPY_INTERNAL_H_
+#define _STRLCPY_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_STRLCPY
+#include <string.h>
+size_t _event_strlcpy(char *dst, const char *src, size_t siz);
+#define strlcpy _event_strlcpy
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/extra/libevent/strlcpy.c b/extra/libevent/strlcpy.c
new file mode 100644
index 00000000000..5d194527c8c
--- /dev/null
+++ b/extra/libevent/strlcpy.c
@@ -0,0 +1,76 @@
+/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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 ``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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_STRLCPY
+#include "strlcpy-internal.h"
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+_event_strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
diff --git a/extra/my_print_defaults.c b/extra/my_print_defaults.c
index 89fd4104c6f..fb4e60cdfb4 100644
--- a/extra/my_print_defaults.c
+++ b/extra/my_print_defaults.c
@@ -49,19 +49,19 @@ static struct my_option my_long_options[] =
{"config-file", 'c', "Deprecated, please use --defaults-file instead. "
"Name of config file to read; if no extension is given, default "
"extension (e.g., .ini or .cnf) will be added",
- &config_file, &config_file, 0, GET_STR, REQUIRED_ARG,
+ (char**) &config_file, (char**) &config_file, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
#ifdef DBUG_OFF
{"debug", '#', "This is a non-debug version. Catch this and exit",
0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
#else
- {"debug", '#', "Output debug log", &default_dbug_option,
- &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug", '#', "Output debug log", (char**) &default_dbug_option,
+ (char**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"defaults-file", 'c', "Like --config-file, except: if first option, "
"then read this file only, do not read global or per-user config "
"files; should be the first option",
- &config_file, &config_file, 0, GET_STR, REQUIRED_ARG,
+ (char**) &config_file, (char*) &config_file, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"defaults-extra-file", 'e',
"Read this file after the global config file and before the config "
@@ -70,7 +70,7 @@ static struct my_option my_long_options[] =
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"defaults-group-suffix", 'g',
"In addition to the given groups, read also groups with this suffix",
- &my_defaults_group_suffix, &my_defaults_group_suffix,
+ (char**) &my_defaults_group_suffix, (char**) &my_defaults_group_suffix,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"extra-file", 'e',
"Deprecated. Synonym for --defaults-extra-file.",
diff --git a/extra/replace.c b/extra/replace.c
index 2ce374726eb..1f091555b66 100644
--- a/extra/replace.c
+++ b/extra/replace.c
@@ -135,6 +135,7 @@ int main(int argc, char *argv[])
}
}
free_buffer();
+ my_free(replace, MYF(0));
my_end(verbose ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
exit(error ? 2 : 0);
return 0; /* No compiler warning */
diff --git a/extra/yassl/src/crypto_wrapper.cpp b/extra/yassl/src/crypto_wrapper.cpp
index b968ec1e6c3..09afc835fce 100644
--- a/extra/yassl/src/crypto_wrapper.cpp
+++ b/extra/yassl/src/crypto_wrapper.cpp
@@ -954,8 +954,11 @@ x509* PemToDer(FILE* file, CertType type, EncryptedInfo* info)
}
}
// get blank line
- if (fgets(line, sizeof(line), file))
- begin = ftell(file);
+ if (fgets(line,sizeof(line), file) == 0)
+ {
+ /* Impossible case */
+ }
+ begin = ftell(file);
}
}
diff --git a/extra/yassl/src/yassl_error.cpp b/extra/yassl/src/yassl_error.cpp
index dd30348cd93..cea2ddb553e 100644
--- a/extra/yassl/src/yassl_error.cpp
+++ b/extra/yassl/src/yassl_error.cpp
@@ -60,7 +60,7 @@ void SetErrorString(unsigned long error, char* buffer)
using namespace TaoCrypt;
const int max = MAX_ERROR_SZ; // shorthand
- switch (error) {
+ switch ((int) error) {
// yaSSL proper errors
case range_error :
diff --git a/extra/yassl/src/yassl_imp.cpp b/extra/yassl/src/yassl_imp.cpp
index 86799f961ae..5128c00418e 100644
--- a/extra/yassl/src/yassl_imp.cpp
+++ b/extra/yassl/src/yassl_imp.cpp
@@ -1298,8 +1298,7 @@ void ServerHello::Process(input_buffer&, SSL& ssl)
else
ssl.useSecurity().use_connection().sessionID_Set_ = false;
- if (ssl.getSecurity().get_resuming())
- {
+ if (ssl.getSecurity().get_resuming()) {
if (memcmp(session_id_, ssl.getSecurity().get_resume().GetID(),
ID_LEN) == 0) {
ssl.set_masterSecret(ssl.getSecurity().get_resume().GetSecret());
diff --git a/extra/yassl/taocrypt/include/block.hpp b/extra/yassl/taocrypt/include/block.hpp
index bb34db5e07f..d5bc8756fdd 100644
--- a/extra/yassl/taocrypt/include/block.hpp
+++ b/extra/yassl/taocrypt/include/block.hpp
@@ -167,7 +167,8 @@ public:
void CleanNew(word32 newSize)
{
New(newSize);
- memset(buffer_, 0, sz_ * sizeof(T));
+ if (sz_ > 0)
+ memset(buffer_, 0, sz_ * sizeof(T));
}
void New(word32 newSize)
diff --git a/extra/yassl/taocrypt/src/aes.cpp b/extra/yassl/taocrypt/src/aes.cpp
index 63eff1d91fc..42cb0622e56 100644
--- a/extra/yassl/taocrypt/src/aes.cpp
+++ b/extra/yassl/taocrypt/src/aes.cpp
@@ -51,7 +51,8 @@ void AES::Process(byte* out, const byte* in, word32 sz)
out += BLOCK_SIZE;
in += BLOCK_SIZE;
}
- else if (mode_ == CBC) {
+ else if (mode_ == CBC)
+ {
if (dir_ == ENCRYPTION)
while (blocks--) {
r_[0] ^= *(word32*)in;
diff --git a/extra/yassl/taocrypt/src/algebra.cpp b/extra/yassl/taocrypt/src/algebra.cpp
index 47a660d5c96..437563b9093 100644
--- a/extra/yassl/taocrypt/src/algebra.cpp
+++ b/extra/yassl/taocrypt/src/algebra.cpp
@@ -18,7 +18,6 @@
/* based on Wei Dai's algebra.cpp from CryptoPP */
#undef NDEBUG
-#define DEBUG // GCC 4.0 bug if NDEBUG and Optimize > 1
#include "runtime.hpp"
#include "algebra.hpp"
diff --git a/extra/yassl/taocrypt/src/blowfish.cpp b/extra/yassl/taocrypt/src/blowfish.cpp
index 2097b045278..2a5ae7ffadf 100644
--- a/extra/yassl/taocrypt/src/blowfish.cpp
+++ b/extra/yassl/taocrypt/src/blowfish.cpp
@@ -53,7 +53,8 @@ void Blowfish::Process(byte* out, const byte* in, word32 sz)
out += BLOCK_SIZE;
in += BLOCK_SIZE;
}
- else if (mode_ == CBC) {
+ else if (mode_ == CBC)
+ {
if (dir_ == ENCRYPTION)
while (blocks--) {
r_[0] ^= *(word32*)in;
diff --git a/extra/yassl/taocrypt/src/misc.cpp b/extra/yassl/taocrypt/src/misc.cpp
index 11dd4dc6d66..978665b2f7c 100644
--- a/extra/yassl/taocrypt/src/misc.cpp
+++ b/extra/yassl/taocrypt/src/misc.cpp
@@ -177,6 +177,15 @@ word Crop(word value, unsigned int size)
#ifdef TAOCRYPT_X86ASM_AVAILABLE
+#ifdef NOT_USED
+#ifndef _MSC_VER
+ static jmp_buf s_env;
+ static void SigIllHandler(int)
+ {
+ longjmp(s_env, 1);
+ }
+#endif
+#endif
bool HaveCpuId()
{
diff --git a/extra/yassl/taocrypt/src/twofish.cpp b/extra/yassl/taocrypt/src/twofish.cpp
index 71601c08162..85feaa23280 100644
--- a/extra/yassl/taocrypt/src/twofish.cpp
+++ b/extra/yassl/taocrypt/src/twofish.cpp
@@ -54,7 +54,8 @@ void Twofish::Process(byte* out, const byte* in, word32 sz)
out += BLOCK_SIZE;
in += BLOCK_SIZE;
}
- else if (mode_ == CBC) {
+ else if (mode_ == CBC)
+ {
if (dir_ == ENCRYPTION)
while (blocks--) {
r_[0] ^= *(word32*)in;