summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authormillert <millert@openbsd.org>2007-03-19 15:12:49 +0000
committermillert <millert@openbsd.org>2007-03-19 15:12:49 +0000
commit7567a3869306a673a370f822187e9f0d2c2bc2fa (patch)
tree7fdf7c957c73da95d17f3cabfea79206f6f7aac1 /lib
parentFix keyword expansion. (diff)
downloadwireguard-openbsd-7567a3869306a673a370f822187e9f0d2c2bc2fa.tar.xz
wireguard-openbsd-7567a3869306a673a370f822187e9f0d2c2bc2fa.zip
Update to libevent-1.3b while retaining our local changes. beck@ OK
Diffstat (limited to 'lib')
-rw-r--r--lib/libevent/Makefile30
-rw-r--r--lib/libevent/buffer.c19
-rw-r--r--lib/libevent/evbuffer.c10
-rw-r--r--lib/libevent/evdns.3302
-rw-r--r--lib/libevent/evdns.c3080
-rw-r--r--lib/libevent/evdns.h367
-rw-r--r--lib/libevent/event-internal.h2
-rw-r--r--lib/libevent/event.336
-rw-r--r--lib/libevent/event.c28
-rw-r--r--lib/libevent/event.h70
-rw-r--r--lib/libevent/event_tagging.c374
-rw-r--r--lib/libevent/evhttp.h216
-rw-r--r--lib/libevent/http-internal.h124
-rw-r--r--lib/libevent/http.c2296
-rw-r--r--lib/libevent/kqueue.c16
-rw-r--r--lib/libevent/log.c14
-rw-r--r--lib/libevent/log.h4
-rw-r--r--lib/libevent/select.c4
-rw-r--r--lib/libevent/shlib_version2
-rw-r--r--lib/libevent/signal.c8
20 files changed, 6944 insertions, 58 deletions
diff --git a/lib/libevent/Makefile b/lib/libevent/Makefile
index 5a76c369aa7..9186d29fd6e 100644
--- a/lib/libevent/Makefile
+++ b/lib/libevent/Makefile
@@ -1,10 +1,11 @@
-# $OpenBSD: Makefile,v 1.20 2006/03/28 15:32:13 brad Exp $
+# $OpenBSD: Makefile,v 1.21 2007/03/19 15:12:49 millert Exp $
LIB= event
WANTLINT=
-SRCS= buffer.c evbuffer.c event.c kqueue.c log.c poll.c select.c signal.c
-HDRS= event.h
-MAN= event.3
+SRCS= buffer.c evbuffer.c evdns.c event.c event_tagging.c http.c kqueue.c \
+ log.c poll.c select.c signal.c
+HDRS= event.h evdns.h
+MAN= evdns.3 event.3
MLINKS= event.3 event_init.3 event.3 event_dispatch.3 event.3 event_loop.3 \
event.3 event_loopexit.3 event.3 event_set.3 event.3 event_base_dispatch.3 \
event.3 event_base_loop.3 event.3 event_base_loopexit.3 event.3 \
@@ -21,18 +22,35 @@ MLINKS= event.3 event_init.3 event.3 event_dispatch.3 event.3 event_loop.3 \
event.3 evbuffer_add.3 event.3 evbuffer_add_buffer.3 event.3 \
evbuffer_add_printf.3 event.3 evbuffer_add_vprintf.3 event.3 evbuffer_drain.3 \
event.3 evbuffer_write.3 event.3 evbuffer_read.3 event.3 evbuffer_find.3 \
- event.3 evbuffer_readline.3
+ event.3 evbuffer_readline.3 \
+ evdns.3 evdns_init.3 evdns.3 evdns_shutdown.3 \
+ evdns.3 evdns_count_nameservers.3 \
+ evdns.3 evdns_clear_nameservers_and_suspend.3 \
+ evdns.3 evdns_resume.3 evdns.3 evdns_nameserver_ip_add.3 \
+ evdns.3 evdns_resolve_ipv4.3 evdns.3 evdns_resolve_reverse.3 \
+ evdns.3 evdns_resolv_conf_parse.3 evdns.3 evdns_search_clear.3 \
+ evdns.3 evdns_search_add.3 evdns.3 evdns_search_ndots_set.3 \
+ evdns.3 evdns_set_log_fn.3
-CFLAGS+= -I${.CURDIR} \
+CFLAGS+= -I${.CURDIR} -DNDEBUG \
+ -DDNS_USE_CPU_CLOCK_FOR_ID \
-DHAVE_CLOCK_GETTIME \
+ -DHAVE_CLOCK_MONOTONIC \
-DHAVE_FCNTL_H \
+ -DHAVE_GETADDRINFO \
+ -DHAVE_GETNAMEINFO \
-DHAVE_POLL \
-DHAVE_SELECT \
-DHAVE_SETFD \
-DHAVE_STDARG_H \
+ -DHAVE_STDINT_H \
+ -DHAVE_STRSEP \
+ -DHAVE_STRTOK_R \
+ -DHAVE_STRUCT_IN6_ADDR \
-DHAVE_SYS_IOCTL_H \
-DHAVE_SYS_TIME_H \
-DHAVE_UNISTD_H \
+ -DHAVE_UNISTD_H \
-DHAVE_VASPRINTF \
-DHAVE_WORKING_KQUEUE
diff --git a/lib/libevent/buffer.c b/lib/libevent/buffer.c
index 2c706bb4ff0..49495545585 100644
--- a/lib/libevent/buffer.c
+++ b/lib/libevent/buffer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: buffer.c,v 1.13 2007/02/04 18:59:12 millert Exp $ */
+/* $OpenBSD: buffer.c,v 1.14 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
@@ -63,7 +63,7 @@ struct evbuffer *
evbuffer_new(void)
{
struct evbuffer *buffer;
-
+
buffer = calloc(1, sizeof(struct evbuffer));
return (buffer);
@@ -114,7 +114,7 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
if (oldoff && outbuf->cb != NULL)
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
-
+
return (0);
}
@@ -140,6 +140,9 @@ evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
buffer = (char *)buf->buffer + buf->off;
space = buf->totallen - buf->misalign - buf->off;
+#ifndef va_copy
+#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
+#endif
va_copy(aq, ap);
#ifdef WIN32
@@ -161,7 +164,7 @@ evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
}
if (evbuffer_expand(buf, sz + 1) == -1)
return (-1);
-
+
}
/* NOTREACHED */
}
@@ -190,7 +193,7 @@ evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
memcpy(data, buf->buffer, nread);
evbuffer_drain(buf, nread);
-
+
return (nread);
}
@@ -205,7 +208,7 @@ evbuffer_readline(struct evbuffer *buffer)
u_char *data = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
char *line;
- u_int i;
+ unsigned int i;
for (i = 0; i < len; i++) {
if (data[i] == '\r' || data[i] == '\n')
@@ -290,7 +293,7 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen)
}
int
-evbuffer_add(struct evbuffer *buf, void *data, size_t datlen)
+evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
{
size_t need = buf->misalign + buf->off + datlen;
size_t oldoff = buf->off;
@@ -365,7 +368,7 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
if (n < EVBUFFER_MAX_READ)
n = EVBUFFER_MAX_READ;
}
-#endif
+#endif
if (howmuch < 0 || howmuch > n)
howmuch = n;
diff --git a/lib/libevent/evbuffer.c b/lib/libevent/evbuffer.c
index ad3491f1960..62e28320a49 100644
--- a/lib/libevent/evbuffer.c
+++ b/lib/libevent/evbuffer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: evbuffer.c,v 1.9 2006/11/26 15:22:58 brad Exp $ */
+/* $OpenBSD: evbuffer.c,v 1.10 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
@@ -165,12 +165,20 @@ bufferevent_writecb(int fd, short event, void *arg)
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;
diff --git a/lib/libevent/evdns.3 b/lib/libevent/evdns.3
new file mode 100644
index 00000000000..2a8ac5632d1
--- /dev/null
+++ b/lib/libevent/evdns.3
@@ -0,0 +1,302 @@
+.\" $OpenBSD: evdns.3,v 1.1 2007/03/19 15:12:49 millert Exp $
+.\"
+.\" 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 ``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.
+.\"
+.Dd October 7, 2006
+.Dt EVDNS 3
+.Os
+.Sh NAME
+.Nm evdns_init
+.Nm evdns_shutdown
+.Nm evdns_err_to_string
+.Nm evdns_nameserver_add
+.Nm evdns_count_nameservers
+.Nm evdns_clear_nameservers_and_suspend
+.Nm evdns_resume
+.Nm evdns_nameserver_ip_add
+.Nm evdns_resolve_ipv4
+.Nm evdns_resolve_reverse
+.Nm evdns_resolv_conf_parse
+.Nm evdns_search_clear
+.Nm evdns_search_add
+.Nm evdns_search_ndots_set
+.Nm evdns_set_log_fn
+.Nd asynchronous functions for DNS resolution.
+.Sh SYNOPSIS
+.Fd #include <sys/time.h>
+.Fd #include <event.h>
+.Fd #include <evdns.h>
+.Ft int
+.Fn evdns_init
+.Ft void
+.Fn evdns_shutdown "int fail_requests"
+.Ft "const char *"
+.Fn evdns_err_to_string "int err"
+.Ft int
+.Fn evdns_nameserver_add "unsigned long int address"
+.Ft int
+.Fn evdns_count_nameservers
+.Ft int
+.Fn evdns_clear_nameservers_and_suspend
+.Ft int
+.Fn evdns_resume
+.Ft int
+.Fn evdns_nameserver_ip_add(const char *ip_as_string);
+.Ft int
+.Fn evdns_resolve_ipv4 "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
+.Ft int
+.Fn evdns_resolve_reverse "struct in_addr *in" "int flags" "evdns_callback_type callback" "void *ptr"
+.Ft int
+.Fn evdns_resolv_conf_parse "int flags" "const char *"
+.Ft void
+.Fn evdns_search_clear
+.Ft void
+.Fn evdns_search_add "const char *domain"
+.Ft void
+.Fn evdns_search_ndots_set "const int ndots"
+.Ft void
+.Fn evdns_set_log_fn "evdns_debug_log_fn_type fn"
+.Sh DESCRIPTION
+Welcome, gentle reader
+.Pp
+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.
+.Pp
+This code is based on libevent and you must call event_init before
+any of the APIs in this file.
+.Pp
+The library keeps track of the state of nameservers and will avoid
+them when they go down.
+Otherwise it will round robin between them.
+.Pp
+Quick start guide:
+.Bd -literal -offset indent
+#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);
+.Ed
+.Pp
+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.
+.Pp
+Searching:
+.Pp
+In order for this library to be a good replacement for the libc 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.
+.Pp
+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.
+.Pp
+To disable searching:
+.Bl -enum -compact -offset indent
+.It
+Never set it up.
+If you never call
+.Fn evdns_resolv_conf_parse,
+.Fn evdns_init,
+or
+.Fn evdns_search_add
+then no searching will occur.
+.It
+If you do call
+.Fn evdns_resolv_conf_parse
+then don't pass
+.Va DNS_OPTION_SEARCH
+(or
+.Va DNS_OPTIONS_ALL,
+which implies it).
+.It
+When calling
+.Fn evdns_resolve,
+pass the
+.Va DNS_QUERY_NO_SEARCH
+flag.
+.El
+.Pp
+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.
+.Pp
+The ndots setting can either be set from a resolv.conf, or by calling
+evdns_search_ndots_set.
+.Pp
+For example, with ndots set to 1 (the default) and a search domain list of
+["myhome.net"]:
+ Query: www
+ Order: www.myhome.net, www.
+.Pp
+ Query: www.abc
+ Order: www.abc., www.abc.myhome.net
+.Pp
+.Sh API reference
+.Pp
+.Bl -tag -width 0123456
+.It Ft int Fn evdns_init
+Initializes support for non-blocking name resolution by calling
+.Fn evdns_resolv_conf_parse .
+.It Ft int Fn evdns_nameserver_add "unsigned long int address"
+Add a nameserver.
+The address should be an IP address in network byte order.
+The type of address is chosen so that
+it matches in_addr.s_addr.
+Returns non-zero on error.
+.It Ft int Fn evdns_nameserver_ip_add "const char *ip_as_string"
+This wraps the above function by parsing a string as an IP
+address and adds it as a nameserver.
+Returns non-zero on error
+.It Ft int Fn evdns_resolve "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
+Resolve a name.
+The name parameter should be a DNS name.
+The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+which disables searching for this query.
+(see defn of searching above).
+.Pp
+The callback argument is a function which is called when
+this query completes and ptr is an argument which is passed
+to that callback function.
+.Pp
+Returns non-zero on error
+.It Ft void Fn evdns_search_clear
+Clears the list of search domains
+.It Ft void Fn evdns_search_add "const char *domain"
+Add a domain to the list of search domains
+.It Ft void Fn evdns_search_ndots_set "int ndots"
+Set the number of dots which, when found in a name, causes
+the first query to be without any search domain.
+.It Ft int Fn evdns_count_nameservers "void"
+Return the number of configured nameservers (not necessarily the
+number of running nameservers).
+This is useful for double-checking whether our calls to the various
+nameserver configuration functions have been successful.
+.It Ft int Fn evdns_clear_nameservers_and_suspend "void"
+Remove all currently configured nameservers, and suspend all pending
+resolves.
+Resolves will not necessarily be re-attempted until
+evdns_resume() is called.
+.It Ft int Fn evdns_resume "void"
+Re-attempt resolves left in limbo after an earlier call to
+evdns_clear_nameservers_and_suspend().
+.It Ft int Fn evdns_resolv_conf_parse "int flags" "const char *filename"
+Parse a resolv.conf like file from the given filename.
+.Pp
+See the man page for resolv.conf for the format of this file.
+The flags argument determines what information is parsed from
+this file:
+.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -nested
+.It DNS_OPTION_SEARCH
+domain, search and ndots options
+.It DNS_OPTION_NAMESERVERS
+nameserver lines
+.It DNS_OPTION_MISC
+timeout and attempts options
+.It DNS_OPTIONS_ALL
+all of the above
+.El
+.Pp
+The following directives are not parsed from the file: lookup, sortlist.
+Additionally, the following
+.Dq options
+are ignored: debug, edns0, inet6, insecure1, insecure2.
+.Pp
+Returns non-zero on error:
+.Bl -tag -width "0" -offset indent -compact -nested
+.It 0
+no errors
+.It 1
+failed to open file
+.It 2
+failed to stat file
+.It 3
+file too large
+.It 4
+out of memory
+.It 5
+short read from file
+.El
+.El
+.Sh 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.
+.Pp
+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.
+.Pp
+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.
+.Sh SEE ALSO
+.Xr event 3 ,
+.Xr gethostbyname 3 ,
+.Xr resolv.conf 5
+.Sh HISTORY
+The
+.Nm evdns
+API was developed by Adam Langley on top of the
+.Nm libevent
+API.
+The code was integrate into
+.Nm Tor
+by Nick Mathewson and finally put into
+.Nm libevent
+itself by Niels Provos.
+.Sh AUTHORS
+The
+.Nm evdns
+API and code was written by Adam Langley with significant
+contributions by Nick Mathewson.
+.Sh BUGS
+This documentation is neither complete nor authoritative.
+If you are in doubt about the usage of this API then
+check the source code to find out how it works, write
+up the missing piece of documentation and send it to
+me for inclusion in this man page.
diff --git a/lib/libevent/evdns.c b/lib/libevent/evdns.c
new file mode 100644
index 00000000000..f50e6f42132
--- /dev/null
+++ b/lib/libevent/evdns.c
@@ -0,0 +1,3080 @@
+/* $OpenBSD: evdns.c,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/* 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
+
+#ifndef DNS_USE_CPU_CLOCK_FOR_ID
+#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
+#ifndef DNS_USE_OPENSSL_FOR_ID
+#error Must configure at least one id generation method.
+#error Please see the documentation.
+#endif
+#endif
+#endif
+
+// #define _POSIX_C_SOURCE 200507
+#define _GNU_SOURCE
+
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#error Multiple id options selected
+#endif
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <time.h>
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <openssl/rand.h>
+#endif
+
+#define _FORTIFY_SOURCE 3
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "evdns.h"
+#include "log.h"
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+#define EVDNS_LOG_DEBUG 0
+#define EVDNS_LOG_WARN 1
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#ifdef __USE_ISOC99B
+// libevent doesn't work without this
+typedef uint8_t u_char;
+typedef unsigned int uint;
+#endif
+#include <event.h>
+
+#define u64 uint64_t
+#define u32 uint32_t
+#define u16 uint16_t
+#define u8 uint8_t
+
+#define MAX_ADDRS 4 // maximum number of addresses from a single packet
+// which we bother recording
+
+#define TYPE_A EVDNS_TYPE_A
+#define TYPE_CNAME 5
+#define TYPE_PTR EVDNS_TYPE_PTR
+#define TYPE_AAAA EVDNS_TYPE_AAAA
+
+#define CLASS_INET EVDNS_CLASS_INET
+
+struct request {
+ u8 *request; // the dns packet data
+ unsigned int request_len;
+ int reissue_count;
+ int tx_count; // the number of times that this packet has been sent
+ unsigned int request_type; // TYPE_PTR or TYPE_A
+ void *user_pointer; // the pointer given to us for this request
+ evdns_callback_type user_callback;
+ struct nameserver *ns; // the server which we last sent it
+
+ // elements used by the searching code
+ int search_index;
+ struct search_state *search_state;
+ char *search_origname; // needs to be free()ed
+ int search_flags;
+
+ // these objects are kept in a circular list
+ struct request *next, *prev;
+
+ struct event timeout_event;
+
+ u16 trans_id; // the transaction id
+ char request_appended; // true if the request pointer is data which follows this struct
+ char transmit_me; // needs to be transmitted
+};
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+ u8 s6_addr[16];
+};
+#endif
+
+struct reply {
+ unsigned int type;
+ unsigned int have_answer;
+ union {
+ struct {
+ u32 addrcount;
+ u32 addresses[MAX_ADDRS];
+ } a;
+ struct {
+ u32 addrcount;
+ struct in6_addr addresses[MAX_ADDRS];
+ } aaaa;
+ struct {
+ char name[HOST_NAME_MAX];
+ } ptr;
+ } data;
+};
+
+struct nameserver {
+ int socket; // a connected UDP socket
+ u32 address;
+ int failed_times; // number of times which we have given this server a chance
+ int timedout; // number of times in a row a request has timed out
+ struct event event;
+ // these objects are kept in a circular list
+ struct nameserver *next, *prev;
+ struct event timeout_event; // used to keep the timeout for
+ // when we next probe this server.
+ // Valid if state == 0
+ char state; // zero if we think that this server is down
+ char choked; // true if we have an EAGAIN from this server's socket
+ char write_waiting; // true if we are waiting for EV_WRITE events
+};
+
+static struct request *req_head = NULL, *req_waiting_head = NULL;
+static struct nameserver *server_head = NULL;
+
+// Represents a local port where we're listening for DNS requests. Right now,
+// only UDP is supported.
+struct evdns_server_port {
+ int socket; // socket we use to read queries and write replies.
+ int refcnt; // reference count.
+ char choked; // Are we currently blocked from writing?
+ char closing; // Are we trying to close this port, pending writes?
+ evdns_request_callback_fn_type user_callback; // Fn to handle requests
+ void *user_data; // Opaque pointer passed to user_callback
+ struct event event; // Read/write event
+ // circular list of replies that we want to write.
+ struct server_request *pending_replies;
+};
+
+// Represents part of a reply being built. (That is, a single RR.)
+struct server_reply_item {
+ struct server_reply_item *next; // next item in sequence.
+ char *name; // name part of the RR
+ u16 type : 16; // The RR type
+ u16 class : 16; // The RR class (usually CLASS_INET)
+ u32 ttl; // The RR TTL
+ char is_name; // True iff data is a label
+ u16 datalen; // Length of data; -1 if data is a label
+ void *data; // The contents of the RR
+};
+
+// Represents a request that we've received as a DNS server, and holds
+// the components of the reply as we're constructing it.
+struct server_request {
+ // Pointers to the next and previous entries on the list of replies
+ // that we're waiting to write. Only set if we have tried to respond
+ // and gotten EAGAIN.
+ struct server_request *next_pending;
+ struct server_request *prev_pending;
+
+ u16 trans_id; // Transaction id.
+ struct evdns_server_port *port; // Which port received this request on?
+ struct sockaddr_storage addr; // Where to send the response
+ socklen_t addrlen; // length of addr
+
+ int n_answer; // how many answer RRs have been set?
+ int n_authority; // how many authority RRs have been set?
+ int n_additional; // how many additional RRs have been set?
+
+ struct server_reply_item *answer; // linked list of answer RRs
+ struct server_reply_item *authority; // linked list of authority RRs
+ struct server_reply_item *additional; // linked list of additional RRs
+
+ // Constructed response. Only set once we're ready to send a reply.
+ // Once this is set, the RR fields are cleared, and no more should be set.
+ char *response;
+ size_t response_len;
+
+ // Caller-visible fields: flags, questions.
+ struct evdns_server_request base;
+};
+
+// helper macro
+#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
+
+// Given a pointer to an evdns_server_request, get the corresponding
+// server_request.
+#define TO_SERVER_REQUEST(base_ptr) \
+ ((struct server_request*) \
+ (((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))
+
+// The number of good nameservers that we have
+static int global_good_nameservers = 0;
+
+// inflight requests are contained in the req_head list
+// and are actually going out across the network
+static int global_requests_inflight = 0;
+// requests which aren't inflight are in the waiting list
+// and are counted here
+static int global_requests_waiting = 0;
+
+static int global_max_requests_inflight = 64;
+
+static struct timeval global_timeout = {5, 0}; // 5 seconds
+static int global_max_reissues = 1; // a reissue occurs when we get some errors from the server
+static int global_max_retransmits = 3; // number of times we'll retransmit a request which timed out
+// number of timeouts in a row before we consider this server to be down
+static int global_max_nameserver_timeout = 3;
+
+// These are the timeout values for nameservers. If we find a nameserver is down
+// we try to probe it at intervals as given below. Values are in seconds.
+static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
+static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval);
+
+static const char *const evdns_error_strings[] = {"no error", "The name server was unable to interpret the query", "The name server suffered an internal error", "The requested domain name does not exist", "The name server refused to reply to the request"};
+
+static struct nameserver *nameserver_pick(void);
+static void evdns_request_insert(struct request *req, struct request **head);
+static void nameserver_ready_callback(int fd, short events, void *arg);
+static int evdns_transmit(void);
+static int evdns_request_transmit(struct request *req);
+static void nameserver_send_probe(struct nameserver *const ns);
+static void search_request_finished(struct request *const);
+static int search_try_next(struct request *const req);
+static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg);
+static void evdns_requests_pump_waiting_queue(void);
+static u16 transaction_id_pick(void);
+static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr);
+static void request_submit(struct request *req);
+
+static int server_request_free(struct server_request *req);
+static void server_request_free_answers(struct server_request *req);
+static void server_port_free(struct evdns_server_port *port);
+static void server_port_ready_callback(int fd, short events, void *arg);
+
+static int strtoint(const char *const str);
+
+#ifdef WIN32
+static int
+last_error(int sock)
+{
+ int optval, optvallen=sizeof(optval);
+ int err = WSAGetLastError();
+ if (err == WSAEWOULDBLOCK && sock >= 0) {
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
+ &optvallen))
+ return err;
+ if (optval)
+ return optval;
+ }
+ return err;
+
+}
+static int
+error_is_eagain(int err)
+{
+ return err == EAGAIN || err == WSAEWOULDBLOCK;
+}
+static int
+inet_aton(const char *c, struct in_addr *addr)
+{
+ uint32_t r;
+ if (strcmp(c, "255.255.255.255") == 0) {
+ addr->s_addr = 0xffffffffu;
+ } else {
+ r = inet_addr(c);
+ if (r == INADDR_NONE)
+ return 0;
+ addr->s_addr = r;
+ }
+ return 1;
+}
+#define CLOSE_SOCKET(x) closesocket(x)
+#else
+#define last_error(sock) (errno)
+#define error_is_eagain(err) ((err) == EAGAIN)
+#define CLOSE_SOCKET(x) close(x)
+#endif
+
+#define ISSPACE(c) isspace((int)(unsigned char)(c))
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+
+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_SERVERFAILED:
+ 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;
+ 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 inline 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, flags, questions, answers, authority, additional, datalength;
+ u32 ttl, ttl_r = 0xffffffff;
+ struct reply reply;
+ struct request *req;
+ 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;
+ // XXXX should the other return points also call reply_handle? -NM
+
+ memset(&reply, 0, sizeof(reply));
+
+ if (!(flags & 0x8000)) return -1; // must be an answer
+ if (flags & 0x020f) {
+ // there was an error
+ reply_handle(req, flags, 0, NULL);
+ return -1;
+ }
+ // 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) return -1;
+ }
+
+ // 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. */
+ return -1;
+ 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) return -1;
+ 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)
+ return -1;
+ 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. */
+ return -1;
+ 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) return -1;
+ 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:
+ return -1;
+}
+
+// Parse a raw request (packet,length) sent to a nameserver port (port) from
+// a DNS client (addr,addrlen), and if it's well-formed, call the corresponding
+// callback.
+static int
+request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen)
+{
+ int j = 0; // index into packet
+ u16 _t; // used by the macros
+ char tmp_name[256]; // used by the macros
+
+ int i;
+ u16 trans_id, flags, questions, answers, authority, additional;
+ struct server_request *server_req = NULL;
+
+ // Get the header fields
+ GET16(trans_id);
+ GET16(flags);
+ GET16(questions);
+ GET16(answers);
+ GET16(authority);
+ GET16(additional);
+
+ if (flags & 0x8000) return -1; // Must not be an answer.
+ if (flags & 0x7800) return -1; // only standard queries are supported
+ flags &= 0x0300; // Only TC and RD get preserved.
+
+ server_req = malloc(sizeof(struct server_request));
+ if (server_req == NULL) return -1;
+ memset(server_req, 0, sizeof(struct server_request));
+
+ server_req->trans_id = trans_id;
+ memcpy(&server_req->addr, addr, addrlen);
+ server_req->addrlen = addrlen;
+
+ server_req->base.flags = flags;
+ server_req->base.nquestions = 0;
+ server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions);
+ if (server_req->base.questions == NULL)
+ goto err;
+
+ for (i = 0; i < questions; ++i) {
+ u16 type, class;
+ struct evdns_server_question *q;
+ int namelen;
+ if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)
+ goto err;
+ GET16(type);
+ GET16(class);
+ namelen = strlen(tmp_name);
+ q = malloc(sizeof(struct evdns_server_question) + namelen);
+ if (!q)
+ goto err;
+ q->type = type;
+ q->class = class;
+ memcpy(q->name, tmp_name, namelen+1);
+ server_req->base.questions[server_req->base.nquestions++] = q;
+ }
+
+ // Ignore answers, authority, and additional.
+
+ server_req->port = port;
+ port->refcnt++;
+ port->user_callback(&(server_req->base), port->user_data);
+
+ return 0;
+err:
+ if (server_req) {
+ if (server_req->base.questions) {
+ for (i = 0; i < server_req->base.nquestions; ++i)
+ free(server_req->base.questions[i]);
+ free(server_req->base.questions);
+ }
+ free(server_req);
+ }
+ return -1;
+
+#undef SKIP_NAME
+#undef GET32
+#undef GET16
+#undef GET8
+}
+
+// Try to choose a strong transaction id which isn't already in flight
+static u16
+transaction_id_pick(void) {
+ for (;;) {
+ const struct request *req = req_head, *started_at;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+ struct timespec ts;
+ u16 trans_id;
+#ifdef CLOCK_MONOTONIC
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+#else
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
+#endif
+ event_err(1, "clock_gettime");
+ trans_id = ts.tv_nsec & 0xffff;
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+ struct timeval tv;
+ u16 trans_id;
+ gettimeofday(&tv, NULL);
+ trans_id = tv.tv_usec & 0xffff;
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+ u16 trans_id;
+ if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
+ /* // in the case that the RAND call fails we back
+ // down to using gettimeofday.
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ trans_id = tv.tv_usec & 0xffff; */
+ abort();
+ }
+#endif
+
+ if (trans_id == 0xffff) continue;
+ // now check to see if that id is already inflight
+ req = started_at = req_head;
+ if (req) {
+ do {
+ if (req->trans_id == trans_id) break;
+ req = req->next;
+ } while (req != started_at);
+ }
+ // we didn't find it, so this is a good id
+ if (req == started_at) return trans_id;
+ }
+}
+
+// choose a namesever to use. This function will try to ignore
+// nameservers which we think are down and load balance across the rest
+// by updating the server_head global each time.
+static struct nameserver *
+nameserver_pick(void) {
+ struct nameserver *started_at = server_head, *picked;
+ if (!server_head) return NULL;
+
+ // if we don't have any good nameservers then there's no
+ // point in trying to find one.
+ if (!global_good_nameservers) {
+ server_head = server_head->next;
+ return server_head;
+ }
+
+ // remember that nameservers are in a circular list
+ for (;;) {
+ if (server_head->state) {
+ // we think this server is currently good
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+
+ server_head = server_head->next;
+ if (server_head == started_at) {
+ // all the nameservers seem to be down
+ // so we just return this one and hope for the
+ // best
+ assert(global_good_nameservers == 0);
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+ }
+}
+
+// this is called when a namesever socket is ready for reading
+static void
+nameserver_read(struct nameserver *ns) {
+ u8 packet[1500];
+
+ for (;;) {
+ const int r = recv(ns->socket, packet, sizeof(packet), 0);
+ if (r < 0) {
+ int err = last_error(ns->socket);
+ if (error_is_eagain(err)) return;
+ nameserver_failed(ns, strerror(err));
+ return;
+ }
+ ns->timedout = 0;
+ reply_parse(packet, r);
+ }
+}
+
+// Read a packet from a DNS client on a server port s, parse it, and
+// act accordingly.
+static void
+server_port_read(struct evdns_server_port *s) {
+ u8 packet[1500];
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int r;
+
+ for (;;) {
+ addrlen = sizeof(struct sockaddr_storage);
+ r = recvfrom(s->socket, packet, sizeof(packet), 0,
+ (struct sockaddr*) &addr, &addrlen);
+ if (r < 0) {
+ int err = last_error(s->socket);
+ if (error_is_eagain(err)) return;
+ log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.",
+ strerror(err), err);
+ return;
+ }
+ request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen);
+ }
+}
+
+// Try to write all pending replies on a given DNS server port.
+static void
+server_port_flush(struct evdns_server_port *port)
+{
+ while (port->pending_replies) {
+ struct server_request *req = port->pending_replies;
+ int r = sendto(port->socket, req->response, req->response_len, 0,
+ (struct sockaddr*) &req->addr, req->addrlen);
+ if (r < 0) {
+ int err = last_error(port->socket);
+ if (error_is_eagain(err))
+ return;
+ log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", strerror(err), err);
+ }
+ if (server_request_free(req)) {
+ // we released the last reference to req->port.
+ return;
+ }
+ }
+
+ // We have no more pending requests; stop listening for 'writeable' events.
+ (void) event_del(&port->event);
+ event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
+ server_port_ready_callback, port);
+ if (event_add(&port->event, NULL) < 0) {
+ log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server.");
+ // ???? Do more?
+ }
+}
+
+// set if we are waiting for the ability to write to this server.
+// if waiting is true then we ask libevent for EV_WRITE events, otherwise
+// we stop these events.
+static void
+nameserver_write_waiting(struct nameserver *ns, char waiting) {
+ if (ns->write_waiting == waiting) return;
+
+ ns->write_waiting = waiting;
+ (void) event_del(&ns->event);
+ event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST,
+ nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+}
+
+// a callback function. Called by libevent when the kernel says that
+// a nameserver socket is ready for writing or reading
+static void
+nameserver_ready_callback(int fd, short events, void *arg) {
+ struct nameserver *ns = (struct nameserver *) arg;
+ (void)fd;
+
+ if (events & EV_WRITE) {
+ ns->choked = 0;
+ if (!evdns_transmit()) {
+ nameserver_write_waiting(ns, 0);
+ }
+ }
+ if (events & EV_READ) {
+ nameserver_read(ns);
+ }
+}
+
+// a callback function. Called by libevent when the kernel says that
+// a server socket is ready for writing or reading.
+static void
+server_port_ready_callback(int fd, short events, void *arg) {
+ struct evdns_server_port *port = (struct evdns_server_port *) arg;
+ (void) fd;
+
+ if (events & EV_WRITE) {
+ port->choked = 0;
+ server_port_flush(port);
+ }
+ if (events & EV_READ) {
+ server_port_read(port);
+ }
+}
+
+/* This is an inefficient representation; only use it via the dnslabel_table_*
+ * functions, so that is can be safely replaced with something smarter later. */
+#define MAX_LABELS 128
+// Structures used to implement name compression
+struct dnslabel_entry { char *v; int 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, int pos)
+{
+ char *v;
+ int p;
+ if (table->n_labels == MAX_LABELS)
+ return (-1);
+ v = strdup(label);
+ if (v == NULL)
+ return (-1);
+ p = table->n_labels++;
+ table->labels[p].v = v;
+ table->labels[p].pos = pos;
+
+ return (0);
+}
+
+// Converts a string to a length-prefixed set of DNS labels, starting
+// at buf[j]. name and buf must not overlap. name_len should be the length
+// of name. table is optional, and is used for compression.
+//
+// Input: abc.def
+// Output: <3>abc<3>def<0>
+//
+// Returns the first index after the encoded name, or negative on error.
+// -1 label was > 63 bytes
+// -2 name too long to fit in buffer.
+//
+static off_t
+dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j,
+ const char *name, const int name_len,
+ struct dnslabel_table *table) {
+ const char *end = name + name_len;
+ int ref = 0;
+ u16 _t;
+
+#define APPEND16(x) do { \
+ if (j + 2 > (off_t)buf_len) \
+ goto overflow; \
+ _t = htons(x); \
+ memcpy(buf + j, &_t, 2); \
+ j += 2; \
+ } while (0)
+#define APPEND32(x) do { \
+ if (j + 4 > (off_t)buf_len) \
+ goto overflow; \
+ _t32 = htonl(x); \
+ memcpy(buf + j, &_t32, 4); \
+ j += 4; \
+ } while (0)
+
+ if (name_len > 255) return -2;
+
+ for (;;) {
+ const char *const start = name;
+ if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) {
+ APPEND16(ref | 0xc000);
+ return j;
+ }
+ name = strchr(name, '.');
+ if (!name) {
+ const unsigned int label_len = end - start;
+ if (label_len > 63) return -1;
+ if ((size_t)(j+label_len+1) > buf_len) return -2;
+ if (table) dnslabel_table_add(table, start, j);
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, end - start);
+ j += end - start;
+ break;
+ } else {
+ // append length of the label.
+ const unsigned int label_len = name - start;
+ if (label_len > 63) return -1;
+ if ((size_t)(j+label_len+1) > buf_len) return -2;
+ if (table) dnslabel_table_add(table, start, j);
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, name - start);
+ j += name - start;
+ // hop over the '.'
+ name++;
+ }
+ }
+
+ // the labels must be terminated by a 0.
+ // It's possible that the name ended in a .
+ // in which case the zero is already there
+ if (!j || buf[j-1]) buf[j++] = 0;
+ return j;
+ overflow:
+ return (-2);
+}
+
+// Finds the length of a dns request for a DNS name of the given
+// length. The actual request may be smaller than the value returned
+// here
+static int
+evdns_request_len(const int name_len) {
+ return 96 + // length of the DNS standard header
+ name_len + 2 +
+ 4; // space for the resource type
+}
+
+// build a dns request packet into buf. buf should be at least as long
+// as evdns_request_len told you it should be.
+//
+// Returns the amount of space used. Negative on error.
+static int
+evdns_request_data_build(const char *const name, const int name_len,
+ const u16 trans_id, const u16 type, const u16 class,
+ u8 *const buf, size_t buf_len) {
+ off_t j = 0; // current offset into buf
+ u16 _t; // used by the macros
+
+ APPEND16(trans_id);
+ APPEND16(0x0100); // standard query, recusion needed
+ APPEND16(1); // one question
+ APPEND16(0); // no answers
+ APPEND16(0); // no authority
+ APPEND16(0); // no additional
+
+ j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL);
+ if (j < 0) {
+ return (int)j;
+ }
+
+ APPEND16(type);
+ APPEND16(class);
+
+ return (int)j;
+ overflow:
+ return (-1);
+}
+
+// exported function
+struct evdns_server_port *
+evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data)
+{
+ struct evdns_server_port *port;
+ if (!(port = malloc(sizeof(struct evdns_server_port))))
+ return NULL;
+ memset(port, 0, sizeof(struct evdns_server_port));
+
+ assert(!is_tcp); // TCP sockets not yet implemented
+ port->socket = socket;
+ port->refcnt = 1;
+ port->choked = 0;
+ port->closing = 0;
+ port->user_callback = cb;
+ port->user_data = user_data;
+ port->pending_replies = NULL;
+
+ event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
+ server_port_ready_callback, port);
+ event_add(&port->event, NULL); // check return.
+ return port;
+}
+
+// exported function
+void
+evdns_close_server_port(struct evdns_server_port *port)
+{
+ if (--port->refcnt == 0)
+ server_port_free(port);
+ port->closing = 1;
+}
+
+// exported function
+int
+evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data)
+{
+ struct server_request *req = TO_SERVER_REQUEST(_req);
+ struct server_reply_item **itemp, *item;
+ int *countp;
+
+ if (req->response) /* have we already answered? */
+ return (-1);
+
+ switch (section) {
+ case EVDNS_ANSWER_SECTION:
+ itemp = &req->answer;
+ countp = &req->n_answer;
+ break;
+ case EVDNS_AUTHORITY_SECTION:
+ itemp = &req->authority;
+ countp = &req->n_authority;
+ break;
+ case EVDNS_ADDITIONAL_SECTION:
+ itemp = &req->additional;
+ countp = &req->n_additional;
+ break;
+ default:
+ return (-1);
+ }
+ while (*itemp) {
+ itemp = &((*itemp)->next);
+ }
+ item = malloc(sizeof(struct server_reply_item));
+ if (!item)
+ return -1;
+ item->next = NULL;
+ if (!(item->name = strdup(name))) {
+ free(item);
+ return -1;
+ }
+ item->type = type;
+ item->class = class;
+ item->ttl = ttl;
+ item->is_name = is_name != 0;
+ item->datalen = 0;
+ item->data = NULL;
+ if (data) {
+ if (item->is_name) {
+ if (!(item->data = strdup(data))) {
+ free(item->name);
+ free(item);
+ return -1;
+ }
+ item->datalen = -1;
+ } else {
+ if (!(item->data = malloc(datalen))) {
+ free(item->name);
+ free(item);
+ return -1;
+ }
+ item->datalen = datalen;
+ memcpy(item->data, data, datalen);
+ }
+ }
+
+ *itemp = item;
+ ++(*countp);
+ return 0;
+}
+
+// exported function
+int
+evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
+{
+ return evdns_server_request_add_reply(
+ req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
+ ttl, n*4, 0, addrs);
+}
+
+// exported function
+int
+evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl)
+{
+ return evdns_server_request_add_reply(
+ req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET,
+ ttl, n*16, 0, addrs);
+}
+
+// exported function
+int
+evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl)
+{
+ u32 a;
+ char buf[32];
+ assert(in || inaddr_name);
+ assert(!(in && inaddr_name));
+ if (in) {
+ a = ntohl(in->s_addr);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+ (int)(u8)((a )&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>24)&0xff));
+ inaddr_name = buf;
+ }
+ return evdns_server_request_add_reply(
+ req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET,
+ ttl, -1, 1, hostname);
+}
+
+// exported function
+int
+evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl)
+{
+ return evdns_server_request_add_reply(
+ req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
+ ttl, -1, 1, cname);
+}
+
+
+static int
+evdns_server_request_format_response(struct server_request *req, int err)
+{
+ unsigned char buf[1500];
+ size_t buf_len = sizeof(buf);
+ off_t j = 0, r;
+ u16 _t;
+ u32 _t32;
+ int i;
+ u16 flags;
+ struct dnslabel_table table;
+
+ if (err < 0 || err > 15) return -1;
+
+ /* Set response bit and error code; copy OPCODE and RD fields from
+ * question; copy RA and AA if set by caller. */
+ flags = req->base.flags;
+ flags |= (0x8000 | err);
+
+ dnslabel_table_init(&table);
+ APPEND16(req->trans_id);
+ APPEND16(flags);
+ APPEND16(req->base.nquestions);
+ APPEND16(req->n_answer);
+ APPEND16(req->n_authority);
+ APPEND16(req->n_additional);
+
+ /* Add questions. */
+ for (i=0; i < req->base.nquestions; ++i) {
+ const char *s = req->base.questions[i]->name;
+ j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table);
+ if (j < 0) {
+ dnslabel_clear(&table);
+ return (int) j;
+ }
+ APPEND16(req->base.questions[i]->type);
+ APPEND16(req->base.questions[i]->class);
+ }
+
+ /* Add answer, authority, and additional sections. */
+ for (i=0; i<3; ++i) {
+ struct server_reply_item *item;
+ if (i==0)
+ item = req->answer;
+ else if (i==1)
+ item = req->authority;
+ else
+ item = req->additional;
+ while (item) {
+ r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table);
+ if (r < 0)
+ goto overflow;
+ j = r;
+
+ APPEND16(item->type);
+ APPEND16(item->class);
+ APPEND32(item->ttl);
+ if (item->is_name) {
+ off_t len_idx = j, name_start;
+ j += 2;
+ name_start = j;
+ r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table);
+ if (r < 0)
+ goto overflow;
+ j = r;
+ _t = htons( (j-name_start) );
+ memcpy(buf+len_idx, &_t, 2);
+ } else {
+ APPEND16(item->datalen);
+ if (j+item->datalen > (off_t)buf_len)
+ goto overflow;
+ memcpy(buf+j, item->data, item->datalen);
+ j += item->datalen;
+ }
+ item = item->next;
+ }
+ }
+
+ if (j > 512) {
+overflow:
+ j = 512;
+ buf[3] |= 0x02; /* set the truncated bit. */
+ }
+
+ req->response_len = j;
+
+ if (!(req->response = malloc(req->response_len))) {
+ server_request_free_answers(req);
+ dnslabel_clear(&table);
+ return (-1);
+ }
+ memcpy(req->response, buf, req->response_len);
+ server_request_free_answers(req);
+ dnslabel_clear(&table);
+ return (0);
+}
+
+// exported function
+int
+evdns_server_request_respond(struct evdns_server_request *_req, int err)
+{
+ struct server_request *req = TO_SERVER_REQUEST(_req);
+ struct evdns_server_port *port = req->port;
+ int r;
+ if (!req->response) {
+ if ((r = evdns_server_request_format_response(req, err))<0)
+ return r;
+ }
+
+ r = sendto(port->socket, req->response, req->response_len, 0,
+ (struct sockaddr*) &req->addr, req->addrlen);
+ if (r<0) {
+ int err = last_error(port->socket);
+ if (! error_is_eagain(err))
+ return -1;
+
+ if (port->pending_replies) {
+ req->prev_pending = port->pending_replies->prev_pending;
+ req->next_pending = port->pending_replies;
+ req->prev_pending->next_pending =
+ req->next_pending->prev_pending = req;
+ } else {
+ req->prev_pending = req->next_pending = req;
+ port->pending_replies = req;
+ port->choked = 1;
+
+ (void) event_del(&port->event);
+ event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port);
+
+ if (event_add(&port->event, NULL) < 0) {
+ log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server");
+ }
+
+ }
+
+ return 1;
+ }
+ if (server_request_free(req))
+ return 0;
+
+ if (req->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);
+ /* XXXX free(victim?) -NM */
+ 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]);
+ }
+
+ 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;
+}
+
+#undef APPEND16
+#undef APPEND32
+
+// this is a libevent callback function which is called when a request
+// has timed out.
+static void
+evdns_request_timeout_callback(int fd, short events, void *arg) {
+ struct request *const req = (struct request *) arg;
+ (void) fd;
+ (void) events;
+
+ log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg);
+
+ req->ns->timedout++;
+ if (req->ns->timedout > global_max_nameserver_timeout) {
+ req->ns->timedout = 0;
+ nameserver_failed(req->ns, "request timed out.");
+ }
+
+ (void) evtimer_del(&req->timeout_event);
+ if (req->tx_count >= global_max_retransmits) {
+ // this request has failed
+ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
+ request_finished(req, &req_head);
+ } else {
+ // retransmit it
+ evdns_request_transmit(req);
+ }
+}
+
+// try to send a request to a given server.
+//
+// return:
+// 0 ok
+// 1 temporary failure
+// 2 other failure
+static int
+evdns_request_transmit_to(struct request *req, struct nameserver *server) {
+ const int r = send(server->socket, req->request, req->request_len, 0);
+ if (r < 0) {
+ int err = last_error(server->socket);
+ if (error_is_eagain(err)) return 1;
+ nameserver_failed(req->ns, strerror(err));
+ return 2;
+ } else if (r != (int)req->request_len) {
+ return 1; // short write
+ } else {
+ return 0;
+ }
+}
+
+// try to send a request, updating the fields of the request
+// as needed
+//
+// return:
+// 0 ok
+// 1 failed
+static int
+evdns_request_transmit(struct request *req) {
+ int retcode = 0, r;
+
+ // if we fail to send this packet then this flag marks it
+ // for evdns_transmit
+ req->transmit_me = 1;
+ if (req->trans_id == 0xffff) abort();
+
+ if (req->ns->choked) {
+ // don't bother trying to write to a socket
+ // which we have had EAGAIN from
+ return 1;
+ }
+
+ r = evdns_request_transmit_to(req, req->ns);
+ switch (r) {
+ case 1:
+ // temp failure
+ req->ns->choked = 1;
+ nameserver_write_waiting(req->ns, 1);
+ return 1;
+ case 2:
+ // failed in some other way
+ retcode = 1;
+ // fall through
+ default:
+ // all ok
+ log(EVDNS_LOG_DEBUG,
+ "Setting timeout for request %lx", (unsigned long) req);
+ evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req);
+ if (evtimer_add(&req->timeout_event, &global_timeout) < 0) {
+ log(EVDNS_LOG_WARN,
+ "Error from libevent when adding timer for request %lx",
+ (unsigned long) req);
+ // ???? Do more?
+ }
+ req->tx_count++;
+ req->transmit_me = 0;
+ return retcode;
+ }
+}
+
+static void
+nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
+ struct nameserver *const ns = (struct nameserver *) arg;
+ (void) type;
+ (void) count;
+ (void) ttl;
+ (void) addresses;
+
+ if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
+ // this is a good reply
+ nameserver_up(ns);
+ } else nameserver_probe_failed(ns);
+}
+
+static void
+nameserver_send_probe(struct nameserver *const ns) {
+ struct request *req;
+ // here we need to send a probe to a given nameserver
+ // in the hope that it is up now.
+
+ log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address));
+
+ req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns);
+ if (!req) return;
+ // we force this into the inflight queue no matter what
+ request_trans_id_set(req, transaction_id_pick());
+ req->ns = ns;
+ request_submit(req);
+}
+
+// returns:
+// 0 didn't try to transmit anything
+// 1 tried to transmit something
+static int
+evdns_transmit(void) {
+ char did_try_to_transmit = 0;
+
+ if (req_head) {
+ struct request *const started_at = req_head, *req = req_head;
+ // first transmit all the requests which are currently waiting
+ do {
+ if (req->transmit_me) {
+ did_try_to_transmit = 1;
+ evdns_request_transmit(req);
+ }
+
+ req = req->next;
+ } while (req != started_at);
+ }
+
+ return did_try_to_transmit;
+}
+
+// exported function
+int
+evdns_count_nameservers(void)
+{
+ const struct nameserver *server = server_head;
+ int n = 0;
+ if (!server)
+ return 0;
+ do {
+ ++n;
+ server = server->next;
+ } while (server != server_head);
+ return n;
+}
+
+// exported function
+int
+evdns_clear_nameservers_and_suspend(void)
+{
+ struct nameserver *server = server_head, *started_at = server_head;
+ struct request *req = req_head, *req_started_at = req_head;
+
+ if (!server)
+ return 0;
+ while (1) {
+ struct nameserver *next = server->next;
+ (void) event_del(&server->event);
+ (void) evtimer_del(&server->timeout_event);
+ if (server->socket >= 0)
+ CLOSE_SOCKET(server->socket);
+ free(server);
+ if (next == started_at)
+ break;
+ server = next;
+ }
+ server_head = NULL;
+ global_good_nameservers = 0;
+
+ while (req) {
+ struct request *next = req->next;
+ req->tx_count = req->reissue_count = 0;
+ req->ns = NULL;
+ // ???? What to do about searches?
+ (void) evtimer_del(&req->timeout_event);
+ req->trans_id = 0;
+ req->transmit_me = 0;
+
+ global_requests_waiting++;
+ evdns_request_insert(req, &req_waiting_head);
+ /* We want to insert these suspended elements at the front of
+ * the waiting queue, since they were pending before any of
+ * the waiting entries were added. This is a circular list,
+ * so we can just shift the start back by one.*/
+ req_waiting_head = req_waiting_head->prev;
+
+ if (next == req_started_at)
+ break;
+ req = next;
+ }
+ req_head = NULL;
+ global_requests_inflight = 0;
+
+ return 0;
+}
+
+
+// exported function
+int
+evdns_resume(void)
+{
+ evdns_requests_pump_waiting_queue();
+ return 0;
+}
+
+static int
+_evdns_nameserver_add_impl(unsigned long int address, int port) {
+ // first check to see if we already have this nameserver
+
+ const struct nameserver *server = server_head, *const started_at = server_head;
+ struct nameserver *ns;
+ struct sockaddr_in sin;
+ int err = 0;
+ if (server) {
+ do {
+ if (server->address == address) return 3;
+ server = server->next;
+ } while (server != started_at);
+ }
+
+ ns = (struct nameserver *) malloc(sizeof(struct nameserver));
+ if (!ns) return -1;
+
+ memset(ns, 0, sizeof(struct nameserver));
+
+ ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ns->socket < 0) { err = 1; goto out1; }
+#ifdef WIN32
+ {
+ u_long nonblocking = 1;
+ ioctlsocket(ns->socket, FIONBIO, &nonblocking);
+ }
+#else
+ fcntl(ns->socket, F_SETFL, O_NONBLOCK);
+#endif
+ sin.sin_addr.s_addr = address;
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+ if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
+ err = 2;
+ goto out2;
+ }
+
+ ns->address = address;
+ ns->state = 1;
+ event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ err = 2;
+ goto out2;
+ }
+
+ log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address));
+
+ // insert this nameserver into the list of them
+ if (!server_head) {
+ ns->next = ns->prev = ns;
+ server_head = ns;
+ } else {
+ ns->next = server_head->next;
+ ns->prev = server_head;
+ server_head->next = ns;
+ if (server_head->prev == server_head) {
+ server_head->prev = ns;
+ }
+ }
+
+ global_good_nameservers++;
+
+ return 0;
+
+out2:
+ CLOSE_SOCKET(ns->socket);
+out1:
+ free(ns);
+ log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err);
+ return err;
+}
+
+// exported function
+int
+evdns_nameserver_add(unsigned long int address) {
+ return _evdns_nameserver_add_impl(address, 53);
+}
+
+// exported function
+int
+evdns_nameserver_ip_add(const char *ip_as_string) {
+ struct in_addr ina;
+ int port;
+ char buf[20];
+ const char *cp;
+ cp = strchr(ip_as_string, ':');
+ if (! cp) {
+ cp = ip_as_string;
+ port = 53;
+ } else {
+ port = strtoint(cp+1);
+ if (port < 0 || port > 65535) {
+ return 4;
+ }
+ if ((cp-ip_as_string) >= (int)sizeof(buf)) {
+ return 4;
+ }
+ memcpy(buf, ip_as_string, cp-ip_as_string);
+ buf[cp-ip_as_string] = '\0';
+ cp = buf;
+ }
+ if (!inet_aton(cp, &ina)) {
+ return 4;
+ }
+ return _evdns_nameserver_add_impl(ina.s_addr, port);
+}
+
+// insert into the tail of the queue
+static void
+evdns_request_insert(struct request *req, struct request **head) {
+ if (!*head) {
+ *head = req;
+ req->next = req->prev = req;
+ return;
+ }
+
+ req->prev = (*head)->prev;
+ req->prev->next = req;
+ req->next = *head;
+ (*head)->prev = req;
+}
+
+static int
+string_num_dots(const char *s) {
+ int count = 0;
+ while ((s = strchr(s, '.'))) {
+ s++;
+ count++;
+ }
+ return count;
+}
+
+static struct request *
+request_new(int type, const char *name, int flags,
+ evdns_callback_type callback, void *user_ptr) {
+ const char issuing_now =
+ (global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
+
+ const int name_len = strlen(name);
+ const int request_max_len = evdns_request_len(name_len);
+ const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
+ // the request data is alloced in a single block with the header
+ struct request *const req =
+ (struct request *) malloc(sizeof(struct request) + request_max_len);
+ int rlen;
+ (void) flags;
+
+ if (!req) return NULL;
+ memset(req, 0, sizeof(struct request));
+
+ // request data lives just after the header
+ req->request = ((u8 *) req) + sizeof(struct request);
+ // denotes that the request data shouldn't be free()ed
+ req->request_appended = 1;
+ rlen = evdns_request_data_build(name, name_len, trans_id,
+ type, CLASS_INET, req->request, request_max_len);
+ if (rlen < 0)
+ goto err1;
+ req->request_len = rlen;
+ req->trans_id = trans_id;
+ req->tx_count = 0;
+ req->request_type = type;
+ req->user_pointer = user_ptr;
+ req->user_callback = callback;
+ req->ns = issuing_now ? nameserver_pick() : NULL;
+ req->next = req->prev = NULL;
+
+ return req;
+err1:
+ free(req);
+ return NULL;
+}
+
+static void
+request_submit(struct request *const req) {
+ if (req->ns) {
+ // if it has a nameserver assigned then this is going
+ // straight into the inflight queue
+ evdns_request_insert(req, &req_head);
+ global_requests_inflight++;
+ evdns_request_transmit(req);
+ } else {
+ evdns_request_insert(req, &req_waiting_head);
+ global_requests_waiting++;
+ }
+}
+
+// exported function
+int evdns_resolve_ipv4(const char *name, int flags,
+ evdns_callback_type callback, void *ptr) {
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
+ if (flags & DNS_QUERY_NO_SEARCH) {
+ struct request *const req =
+ request_new(TYPE_A, name, flags, callback, ptr);
+ if (req == NULL)
+ return (1);
+ request_submit(req);
+ return (0);
+ } else {
+ return (search_request_new(TYPE_A, name, flags, callback, ptr));
+ }
+}
+
+// exported function
+int evdns_resolve_ipv6(const char *name, int flags,
+ evdns_callback_type callback, void *ptr) {
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
+ if (flags & DNS_QUERY_NO_SEARCH) {
+ struct request *const req =
+ request_new(TYPE_AAAA, name, flags, callback, ptr);
+ if (req == NULL)
+ return (1);
+ request_submit(req);
+ return (0);
+ } else {
+ return (search_request_new(TYPE_AAAA, name, flags, callback, ptr));
+ }
+}
+
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) {
+ char buf[32];
+ struct request *req;
+ u32 a;
+ assert(in);
+ a = ntohl(in->s_addr);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+ (int)(u8)((a )&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>24)&0xff));
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+ req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+}
+
+int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) {
+ char buf[64];
+ char *cp;
+ struct request *req;
+ int i;
+ assert(in);
+ cp = buf;
+ for (i=15; i >= 0; --i) {
+ u8 byte = in->s6_addr[i];
+ *cp++ = "0123456789abcdef"[byte & 0x0f];
+ *cp++ = '.';
+ *cp++ = "0123456789abcdef"[byte >> 4];
+ *cp++ = '.';
+ }
+ assert(cp + strlen(".ip6.arpa") < buf+sizeof(buf));
+ memcpy(cp, ".ip6.arpa", strlen(".ip6.arpa")+1);
+ log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+ req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// Search support
+//
+// the libc resolver has support for searching a number of domains
+// to find a name. If nothing else then it takes the single domain
+// from the gethostname() call.
+//
+// It can also be configured via the domain and search options in a
+// resolv.conf.
+//
+// The ndots option controls how many dots it takes for the resolver
+// to decide that a name is non-local and so try a raw lookup first.
+
+struct search_domain {
+ int len;
+ struct search_domain *next;
+ // the text string is appended to this structure
+};
+
+struct search_state {
+ int refcount;
+ int ndots;
+ int num_domains;
+ struct search_domain *head;
+};
+
+static struct search_state *global_search_state = NULL;
+
+static void
+search_state_decref(struct search_state *const state) {
+ if (!state) return;
+ state->refcount--;
+ if (!state->refcount) {
+ struct search_domain *next, *dom;
+ for (dom = state->head; dom; dom = next) {
+ next = dom->next;
+ free(dom);
+ }
+ free(state);
+ }
+}
+
+static struct search_state *
+search_state_new(void) {
+ struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state));
+ if (!state) return NULL;
+ memset(state, 0, sizeof(struct search_state));
+ state->refcount = 1;
+ state->ndots = 1;
+
+ return state;
+}
+
+static void
+search_postfix_clear(void) {
+ search_state_decref(global_search_state);
+
+ global_search_state = search_state_new();
+}
+
+// exported function
+void
+evdns_search_clear(void) {
+ search_postfix_clear();
+}
+
+static void
+search_postfix_add(const char *domain) {
+ int domain_len;
+ struct search_domain *sdomain;
+ while (domain[0] == '.') domain++;
+ domain_len = strlen(domain);
+
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->num_domains++;
+
+ sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len);
+ if (!sdomain) return;
+ memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len);
+ sdomain->next = global_search_state->head;
+ sdomain->len = domain_len;
+
+ global_search_state->head = sdomain;
+}
+
+// reverse the order of members in the postfix list. This is needed because,
+// when parsing resolv.conf we push elements in the wrong order
+static void
+search_reverse(void) {
+ struct search_domain *cur, *prev = NULL, *next;
+ cur = global_search_state->head;
+ while (cur) {
+ next = cur->next;
+ cur->next = prev;
+ prev = cur;
+ cur = next;
+ }
+
+ global_search_state->head = prev;
+}
+
+// exported function
+void
+evdns_search_add(const char *domain) {
+ search_postfix_add(domain);
+}
+
+// exported function
+void
+evdns_search_ndots_set(const int ndots) {
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->ndots = ndots;
+}
+
+static void
+search_set_from_hostname(void) {
+ char hostname[HOST_NAME_MAX + 1], *domainname;
+
+ search_postfix_clear();
+ if (gethostname(hostname, sizeof(hostname))) return;
+ domainname = strchr(hostname, '.');
+ if (!domainname) return;
+ search_postfix_add(domainname);
+}
+
+// warning: returns malloced string
+static char *
+search_make_new(const struct search_state *const state, int n, const char *const base_name) {
+ const int base_len = strlen(base_name);
+ const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+ struct search_domain *dom;
+
+ for (dom = state->head; dom; dom = dom->next) {
+ if (!n--) {
+ // this is the postfix we want
+ // the actual postfix string is kept at the end of the structure
+ const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain);
+ const int postfix_len = dom->len;
+ char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1);
+ if (!newname) return NULL;
+ memcpy(newname, base_name, base_len);
+ if (need_to_append_dot) newname[base_len] = '.';
+ memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len);
+ newname[base_len + need_to_append_dot + postfix_len] = 0;
+ return newname;
+ }
+ }
+
+ // we ran off the end of the list and still didn't find the requested string
+ abort();
+ return NULL; /* unreachable; stops warnings in some compilers. */
+}
+
+static int
+search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) {
+ assert(type == TYPE_A || type == TYPE_AAAA);
+ if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) &&
+ global_search_state &&
+ global_search_state->num_domains) {
+ // we have some domains to search
+ struct request *req;
+ if (string_num_dots(name) >= global_search_state->ndots) {
+ req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ req->search_index = -1;
+ } else {
+ char *const new_name = search_make_new(global_search_state, 0, name);
+ if (!new_name) return 1;
+ req = request_new(type, new_name, flags, user_callback, user_arg);
+ free(new_name);
+ if (!req) return 1;
+ req->search_index = 0;
+ }
+ req->search_origname = strdup(name);
+ req->search_state = global_search_state;
+ req->search_flags = flags;
+ global_search_state->refcount++;
+ request_submit(req);
+ return 0;
+ } else {
+ struct request *const req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+ }
+}
+
+// this is called when a request has failed to find a name. We need to check
+// if it is part of a search and, if so, try the next name in the list
+// returns:
+// 0 another request has been submitted
+// 1 no more requests needed
+static int
+search_try_next(struct request *const req) {
+ if (req->search_state) {
+ // it is part of a search
+ char *new_name;
+ struct request *newreq;
+ req->search_index++;
+ if (req->search_index >= req->search_state->num_domains) {
+ // no more postfixes to try, however we may need to try
+ // this name without a postfix
+ if (string_num_dots(req->search_origname) < req->search_state->ndots) {
+ // yep, we need to try it raw
+ struct request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer);
+ log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
+ if (newreq) {
+ request_submit(newreq);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ new_name = search_make_new(req->search_state, req->search_index, req->search_origname);
+ if (!new_name) return 1;
+ log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index);
+ newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer);
+ free(new_name);
+ if (!newreq) return 1;
+ newreq->search_origname = req->search_origname;
+ req->search_origname = NULL;
+ newreq->search_state = req->search_state;
+ newreq->search_flags = req->search_flags;
+ newreq->search_index = req->search_index;
+ newreq->search_state->refcount++;
+ request_submit(newreq);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+search_request_finished(struct request *const req) {
+ if (req->search_state) {
+ search_state_decref(req->search_state);
+ req->search_state = NULL;
+ }
+ if (req->search_origname) {
+ free(req->search_origname);
+ req->search_origname = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Parsing resolv.conf files
+
+static void
+evdns_resolv_set_defaults(int flags) {
+ // if the file isn't found then we assume a local resolver
+ if (flags & DNS_OPTION_SEARCH) search_set_from_hostname();
+ if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1");
+}
+
+#ifndef HAVE_STRTOK_R
+static char *
+strtok_r(char *s, const char *delim, char **state) {
+ return strtok(s, delim);
+}
+#endif
+
+// helper version of atoi which returns -1 on error
+static int
+strtoint(const char *const str) {
+ char *endptr;
+ const int r = strtol(str, &endptr, 10);
+ if (*endptr) return -1;
+ return r;
+}
+
+// helper version of atoi that returns -1 on error and clips to bounds.
+static int
+strtoint_clipped(const char *const str, int min, int max)
+{
+ int r = strtoint(str);
+ if (r == -1)
+ return r;
+ else if (r<min)
+ return min;
+ else if (r>max)
+ return max;
+ else
+ return r;
+}
+
+// exported function
+int
+evdns_set_option(const char *option, const char *val, int flags)
+{
+ if (!strncmp(option, "ndots:", 6)) {
+ const int ndots = strtoint(val);
+ if (ndots == -1) return -1;
+ if (!(flags & DNS_OPTION_SEARCH)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots);
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return -1;
+ global_search_state->ndots = ndots;
+ } else if (!strncmp(option, "timeout:", 8)) {
+ const int timeout = strtoint(val);
+ if (timeout == -1) return -1;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout);
+ global_timeout.tv_sec = timeout;
+ } else if (!strncmp(option, "max-timeouts:", 12)) {
+ const int maxtimeout = strtoint_clipped(val, 1, 255);
+ if (maxtimeout == -1) return -1;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d",
+ maxtimeout);
+ global_max_nameserver_timeout = maxtimeout;
+ } else if (!strncmp(option, "max-inflight:", 13)) {
+ const int maxinflight = strtoint_clipped(val, 1, 65000);
+ if (maxinflight == -1) return -1;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d",
+ maxinflight);
+ global_max_requests_inflight = maxinflight;
+ } else if (!strncmp(option, "attempts:", 9)) {
+ int retries = strtoint(val);
+ if (retries == -1) return -1;
+ if (retries > 255) retries = 255;
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
+ global_max_retransmits = retries;
+ }
+ return 0;
+}
+
+static void
+resolv_conf_parse_line(char *const start, int flags) {
+ char *strtok_state;
+ static const char *const delims = " \t";
+#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
+
+ char *const first_token = strtok_r(start, delims, &strtok_state);
+ if (!first_token) return;
+
+ if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) {
+ const char *const nameserver = NEXT_TOKEN;
+ struct in_addr ina;
+
+ if (inet_aton(nameserver, &ina)) {
+ // address is valid
+ evdns_nameserver_add(ina.s_addr);
+ }
+ } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) {
+ const char *const domain = NEXT_TOKEN;
+ if (domain) {
+ search_postfix_clear();
+ search_postfix_add(domain);
+ }
+ } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) {
+ const char *domain;
+ search_postfix_clear();
+
+ while ((domain = NEXT_TOKEN)) {
+ search_postfix_add(domain);
+ }
+ search_reverse();
+ } else if (!strcmp(first_token, "options")) {
+ const char *option;
+ while ((option = NEXT_TOKEN)) {
+ const char *val = strchr(option, ':');
+ evdns_set_option(option, val ? val+1 : "", flags);
+ }
+ }
+#undef NEXT_TOKEN
+}
+
+// exported function
+// returns:
+// 0 no errors
+// 1 failed to open file
+// 2 failed to stat file
+// 3 file too large
+// 4 out of memory
+// 5 short read from file
+int
+evdns_resolv_conf_parse(int flags, const char *const filename) {
+ struct stat st;
+ int fd;
+ 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; }
+
+ if (read(fd, resolv, (size_t)st.st_size) != st.st_size) {
+ err = 5; goto out2;
+ }
+ resolv[st.st_size] = 0; // we malloced an extra byte
+
+ start = (char *) resolv;
+ for (;;) {
+ char *const newline = strchr(start, '\n');
+ if (!newline) {
+ resolv_conf_parse_line(start, flags);
+ break;
+ } else {
+ *newline = 0;
+ resolv_conf_parse_line(start, flags);
+ start = newline + 1;
+ }
+ }
+
+ if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) {
+ // no nameservers were configured.
+ evdns_nameserver_ip_add("127.0.0.1");
+ err = 6;
+ }
+ if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) {
+ search_set_from_hostname();
+ }
+
+out2:
+ free(resolv);
+out1:
+ close(fd);
+ return err;
+}
+
+#ifdef WIN32
+// Add multiple nameservers from a space-or-comma-separated list.
+static int
+evdns_nameserver_ip_add_line(const char *ips) {
+ const char *addr;
+ char *buf;
+ int r;
+ while (*ips) {
+ while (ISSPACE(*ips) || *ips == ',' || *ips == '\t')
+ ++ips;
+ addr = ips;
+ while (ISDIGIT(*ips) || *ips == '.' || *ips == ':')
+ ++ips;
+ buf = malloc(ips-addr+1);
+ if (!buf) return 4;
+ memcpy(buf, addr, ips-addr);
+ buf[ips-addr] = '\0';
+ r = evdns_nameserver_ip_add(buf);
+ free(buf);
+ if (r) return r;
+ }
+ return 0;
+}
+
+typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*);
+
+// Use the windows GetNetworkParams interface in iphlpapi.dll to
+// figure out what our nameservers are.
+static int
+load_nameservers_with_getnetworkparams(void)
+{
+ // Based on MSDN examples and inspection of c-ares code.
+ FIXED_INFO *fixed;
+ HMODULE handle = 0;
+ ULONG size = sizeof(FIXED_INFO);
+ void *buf = NULL;
+ int status = 0, r, added_any;
+ IP_ADDR_STRING *ns;
+ GetNetworkParams_fn_t fn;
+
+ if (!(handle = LoadLibrary("iphlpapi.dll"))) {
+ log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll");
+ status = -1;
+ goto done;
+ }
+ if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) {
+ log(EVDNS_LOG_WARN, "Could not get address of function.");
+ status = -1;
+ goto done;
+ }
+
+ buf = malloc(size);
+ if (!buf) { status = 4; goto done; }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) {
+ status = -1;
+ goto done;
+ }
+ if (r != ERROR_SUCCESS) {
+ free(buf);
+ buf = malloc(size);
+ if (!buf) { status = 4; goto done; }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG, "fn() failed.");
+ status = -1;
+ goto done;
+ }
+ }
+
+ assert(fixed);
+ added_any = 0;
+ ns = &(fixed->DnsServerList);
+ while (ns) {
+ r = evdns_nameserver_ip_add_line(ns->IpAddress.String);
+ if (r) {
+ log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d",
+ (ns->IpAddress.String),(int)GetLastError());
+ status = r;
+ goto done;
+ } else {
+ log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String);
+ }
+
+ added_any++;
+ ns = ns->Next;
+ }
+
+ if (!added_any) {
+ log(EVDNS_LOG_DEBUG, "No nameservers added.");
+ status = -1;
+ }
+
+ done:
+ if (buf)
+ free(buf);
+ if (handle)
+ FreeLibrary(handle);
+ return status;
+}
+
+static int
+config_nameserver_from_reg_key(HKEY key, const char *subkey)
+{
+ char *buf;
+ DWORD bufsz = 0, type = 0;
+ int status = 0;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz)
+ != ERROR_MORE_DATA)
+ return -1;
+ if (!(buf = malloc(bufsz)))
+ return -1;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz)
+ == ERROR_SUCCESS && bufsz > 1) {
+ status = evdns_nameserver_ip_add_line(buf);
+ }
+
+ free(buf);
+ return status;
+}
+
+#define SERVICES_KEY "System\\CurrentControlSet\\Services\\"
+#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP"
+#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters"
+
+static int
+load_nameservers_from_registry(void)
+{
+ int found = 0;
+ int r;
+#define TRY(k, name) \
+ if (!found && config_nameserver_from_reg_key(k,name) == 0) { \
+ log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \
+ found = 1; \
+ } else if (!found) { \
+ log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \
+ #k,#name); \
+ }
+
+ if (((int)GetVersion()) > 0) { /* NT */
+ HKEY nt_key = 0, interfaces_key = 0;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
+ KEY_READ, &nt_key) != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError());
+ return -1;
+ }
+ r = RegOpenKeyEx(nt_key, "Interfaces", 0,
+ KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
+ &interfaces_key);
+ if (r != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError());
+ return -1;
+ }
+ TRY(nt_key, "NameServer");
+ TRY(nt_key, "DhcpNameServer");
+ TRY(interfaces_key, "NameServer");
+ TRY(interfaces_key, "DhcpNameServer");
+ RegCloseKey(interfaces_key);
+ RegCloseKey(nt_key);
+ } else {
+ HKEY win_key = 0;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0,
+ KEY_READ, &win_key) != ERROR_SUCCESS) {
+ log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError());
+ return -1;
+ }
+ TRY(win_key, "NameServer");
+ RegCloseKey(win_key);
+ }
+
+ if (found == 0) {
+ log(EVDNS_LOG_WARN,"Didn't find any nameservers.");
+ }
+
+ return found ? 0 : -1;
+#undef TRY
+}
+
+int
+evdns_config_windows_nameservers(void)
+{
+ if (load_nameservers_with_getnetworkparams() == 0)
+ return 0;
+ return load_nameservers_from_registry();
+}
+#endif
+
+int
+evdns_init(void)
+{
+ int res = 0;
+#ifdef WIN32
+ evdns_config_windows_nameservers();
+#else
+ res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+#endif
+
+ return (res);
+}
+
+const char *
+evdns_err_to_string(int err)
+{
+ switch (err) {
+ case DNS_ERR_NONE: return "no error";
+ case DNS_ERR_FORMAT: return "misformatted query";
+ case DNS_ERR_SERVERFAILED: return "server failed";
+ case DNS_ERR_NOTEXIST: return "name does not exist";
+ case DNS_ERR_NOTIMPL: return "query not implemented";
+ case DNS_ERR_REFUSED: return "refused";
+
+ case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed";
+ case DNS_ERR_UNKNOWN: return "unknown";
+ case DNS_ERR_TIMEOUT: return "request timed out";
+ case DNS_ERR_SHUTDOWN: return "dns subsystem shut down";
+ default: return "[Unknown error code]";
+ }
+}
+
+void
+evdns_shutdown(int fail_requests)
+{
+ struct nameserver *server, *server_next;
+ struct search_domain *dom, *dom_next;
+
+ while (req_head) {
+ if (fail_requests)
+ reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL);
+ request_finished(req_head, &req_head);
+ }
+ while (req_waiting_head) {
+ if (fail_requests)
+ reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
+ request_finished(req_waiting_head, &req_waiting_head);
+ }
+ global_requests_inflight = global_requests_waiting = 0;
+
+ for (server = server_head; server; server = server_next) {
+ server_next = server->next;
+ if (server->socket >= 0)
+ CLOSE_SOCKET(server->socket);
+ (void) event_del(&server->event);
+ free(server);
+ if (server_next == server_head)
+ break;
+ }
+ server_head = NULL;
+ global_good_nameservers = 0;
+
+ if (global_search_state) {
+ for (dom = global_search_state->head; dom; dom = dom_next) {
+ dom_next = dom->next;
+ free(dom);
+ }
+ free(global_search_state);
+ global_search_state = NULL;
+ }
+ evdns_log_fn = NULL;
+}
+
+#ifdef EVDNS_MAIN
+void
+main_callback(int result, char type, int count, int ttl,
+ void *addrs, void *orig) {
+ char *n = (char*)orig;
+ int i;
+ for (i = 0; i < count; ++i) {
+ if (type == DNS_IPv4_A) {
+ printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
+ } else if (type == DNS_PTR) {
+ printf("%s: %s\n", n, ((char**)addrs)[i]);
+ }
+ }
+ if (!count) {
+ printf("%s: No answer (%d)\n", n, result);
+ }
+ fflush(stdout);
+}
+void
+evdns_server_callback(struct evdns_server_request *req, void *data)
+{
+ int i, r;
+ (void)data;
+ /* dummy; give 192.168.11.11 as an answer for all A questions,
+ * give foo.bar.example.com as an answer for all PTR questions. */
+ for (i = 0; i < req->nquestions; ++i) {
+ u32 ans = htonl(0xc0a80b0bUL);
+ if (req->questions[i]->type == EVDNS_TYPE_A &&
+ req->questions[i]->class == EVDNS_CLASS_INET) {
+ printf(" -- replying for %s (A)\n", req->questions[i]->name);
+ r = evdns_server_request_add_a_reply(req, req->questions[i]->name,
+ 1, &ans, 10);
+ if (r<0)
+ printf("eeep, didn't work.\n");
+ } else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
+ req->questions[i]->class == EVDNS_CLASS_INET) {
+ printf(" -- replying for %s (PTR)\n", req->questions[i]->name);
+ r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name,
+ "foo.bar.example.com", 10);
+ } else {
+ printf(" -- skipping %s [%d %d]\n", req->questions[i]->name,
+ req->questions[i]->type, req->questions[i]->class);
+ }
+ }
+
+ r = evdns_request_respond(req, 0);
+ if (r<0)
+ printf("eeek, couldn't send reply.\n");
+}
+
+void
+logfn(int is_warn, const char *msg) {
+ (void) is_warn;
+ fprintf(stderr, "%s\n", msg);
+}
+int
+main(int c, char **v) {
+ int idx;
+ int reverse = 0, verbose = 1, servertest = 0;
+ if (c<2) {
+ fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
+ fprintf(stderr, "syntax: %s [-servertest]\n", v[0]);
+ return 1;
+ }
+ idx = 1;
+ while (idx < c && v[idx][0] == '-') {
+ if (!strcmp(v[idx], "-x"))
+ reverse = 1;
+ else if (!strcmp(v[idx], "-v"))
+ verbose = 1;
+ else if (!strcmp(v[idx], "-servertest"))
+ servertest = 1;
+ else
+ fprintf(stderr, "Unknown option %s\n", v[idx]);
+ ++idx;
+ }
+ event_init();
+ if (verbose)
+ evdns_set_log_fn(logfn);
+ evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
+ if (servertest) {
+ int sock;
+ struct sockaddr_in my_addr;
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(10053);
+ my_addr.sin_addr.s_addr = INADDR_ANY;
+ if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) {
+ perror("bind");
+ exit(1);
+ }
+ evdns_add_server_port(sock, 0, evdns_server_callback, NULL);
+ }
+ for (; idx < c; ++idx) {
+ if (reverse) {
+ struct in_addr addr;
+ if (!inet_aton(v[idx], &addr)) {
+ fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
+ continue;
+ }
+ fprintf(stderr, "resolving %s...\n",v[idx]);
+ evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
+ } else {
+ fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+ evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
+ }
+ }
+ fflush(stdout);
+ event_dispatch();
+ return 0;
+}
+#endif
diff --git a/lib/libevent/evdns.h b/lib/libevent/evdns.h
new file mode 100644
index 00000000000..c524fc319f2
--- /dev/null
+++ b/lib/libevent/evdns.h
@@ -0,0 +1,367 @@
+/* $OpenBSD: evdns.h,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/*
+ * 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)
+ */
+
+/*
+ * Welcome, gentle reader
+ *
+ * Async DNS lookups are really a whole lot harder than they should be,
+ * mostly stemming from the fact that the libc resolver has never been
+ * very good at them. Before you use this library you should see if libc
+ * can do the job for you with the modern async call getaddrinfo_a
+ * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
+ * please continue.
+ *
+ * This code is based on libevent and you must call event_init before
+ * any of the APIs in this file. You must also seed the OpenSSL random
+ * source if you are using OpenSSL for ids (see below).
+ *
+ * This library is designed to be included and shipped with your source
+ * code. You statically link with it. You should also test for the
+ * existence of strtok_r and define HAVE_STRTOK_R if you have it.
+ *
+ * The DNS protocol requires a good source of id numbers and these
+ * numbers should be unpredictable for spoofing reasons. There are
+ * three methods for generating them here and you must define exactly
+ * one of them. In increasing order of preference:
+ *
+ * DNS_USE_GETTIMEOFDAY_FOR_ID:
+ * Using the bottom 16 bits of the usec result from gettimeofday. This
+ * is a pretty poor solution but should work anywhere.
+ * DNS_USE_CPU_CLOCK_FOR_ID:
+ * Using the bottom 16 bits of the nsec result from the CPU's time
+ * counter. This is better, but may not work everywhere. Requires
+ * POSIX realtime support and you'll need to link against -lrt on
+ * glibc systems at least.
+ * DNS_USE_OPENSSL_FOR_ID:
+ * Uses the OpenSSL RAND_bytes call to generate the data. You must
+ * have seeded the pool before making any calls to this library.
+ *
+ * The library keeps track of the state of nameservers and will avoid
+ * them when they go down. Otherwise it will round robin between them.
+ *
+ * Quick start guide:
+ * #include "evdns.h"
+ * void callback(int result, char type, int count, int ttl,
+ * void *addresses, void *arg);
+ * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ * evdns_resolve("www.hostname.com", 0, callback, NULL);
+ *
+ * When the lookup is complete the callback function is called. The
+ * first argument will be one of the DNS_ERR_* defines in evdns.h.
+ * Hopefully it will be DNS_ERR_NONE, in which case type will be
+ * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
+ * which the data can be cached for (in seconds), addresses will point
+ * to an array of uint32_t's and arg will be whatever you passed to
+ * evdns_resolve.
+ *
+ * Searching:
+ *
+ * In order for this library to be a good replacement for glibc's resolver it
+ * supports searching. This involves setting a list of default domains, in
+ * which names will be queried for. The number of dots in the query name
+ * determines the order in which this list is used.
+ *
+ * Searching appears to be a single lookup from the point of view of the API,
+ * although many DNS queries may be generated from a single call to
+ * evdns_resolve. Searching can also drastically slow down the resolution
+ * of names.
+ *
+ * To disable searching:
+ * 1. Never set it up. If you never call evdns_resolv_conf_parse or
+ * evdns_search_add then no searching will occur.
+ *
+ * 2. If you do call evdns_resolv_conf_parse then don't pass
+ * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
+ *
+ * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
+ *
+ * The order of searches depends on the number of dots in the name. If the
+ * number is greater than the ndots setting then the names is first tried
+ * globally. Otherwise each search domain is appended in turn.
+ *
+ * The ndots setting can either be set from a resolv.conf, or by calling
+ * evdns_search_ndots_set.
+ *
+ * For example, with ndots set to 1 (the default) and a search domain list of
+ * ["myhome.net"]:
+ * Query: www
+ * Order: www.myhome.net, www.
+ *
+ * Query: www.abc
+ * Order: www.abc., www.abc.myhome.net
+ *
+ * API reference:
+ *
+ * int evdns_nameserver_add(unsigned long int address)
+ * Add a nameserver. The address should be an IP address in
+ * network byte order. The type of address is chosen so that
+ * it matches in_addr.s_addr.
+ * Returns non-zero on error.
+ *
+ * int evdns_nameserver_ip_add(const char *ip_as_string)
+ * This wraps the above function by parsing a string as an IP
+ * address and adds it as a nameserver.
+ * Returns non-zero on error
+ *
+ * int evdns_resolve(const char *name, int flags,
+ * evdns_callback_type callback,
+ * void *ptr)
+ * Resolve a name. The name parameter should be a DNS name.
+ * The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+ * which disables searching for this query. (see defn of
+ * searching above).
+ *
+ * The callback argument is a function which is called when
+ * this query completes and ptr is an argument which is passed
+ * to that callback function.
+ *
+ * Returns non-zero on error
+ *
+ * void evdns_search_clear()
+ * Clears the list of search domains
+ *
+ * void evdns_search_add(const char *domain)
+ * Add a domain to the list of search domains
+ *
+ * void evdns_search_ndots_set(int ndots)
+ * Set the number of dots which, when found in a name, causes
+ * the first query to be without any search domain.
+ *
+ * int evdns_count_nameservers(void)
+ * Return the number of configured nameservers (not necessarily the
+ * number of running nameservers). This is useful for double-checking
+ * whether our calls to the various nameserver configuration functions
+ * have been successful.
+ *
+ * int evdns_clear_nameservers_and_suspend(void)
+ * Remove all currently configured nameservers, and suspend all pending
+ * resolves. Resolves will not necessarily be re-attempted until
+ * evdns_resume() is called.
+ *
+ * int evdns_resume(void)
+ * Re-attempt resolves left in limbo after an earlier call to
+ * evdns_clear_nameservers_and_suspend().
+ *
+ * int evdns_config_windows_nameservers(void)
+ * Attempt to configure a set of nameservers based on platform settings on
+ * a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
+ * looks in the registry. Returns 0 on success, nonzero on failure.
+ *
+ * int evdns_resolv_conf_parse(int flags, const char *filename)
+ * Parse a resolv.conf like file from the given filename.
+ *
+ * See the man page for resolv.conf for the format of this file.
+ * The flags argument determines what information is parsed from
+ * this file:
+ * DNS_OPTION_SEARCH - domain, search and ndots options
+ * DNS_OPTION_NAMESERVERS - nameserver lines
+ * DNS_OPTION_MISC - timeout and attempts options
+ * DNS_OPTIONS_ALL - all of the above
+ * The following directives are not parsed from the file:
+ * sortlist, rotate, no-check-names, inet6, debug
+ *
+ * Returns non-zero on error:
+ * 0 no errors
+ * 1 failed to open file
+ * 2 failed to stat file
+ * 3 file too large
+ * 4 out of memory
+ * 5 short read from file
+ * 6 no nameservers in file
+ *
+ * Internals:
+ *
+ * Requests are kept in two queues. The first is the inflight queue. In
+ * this queue requests have an allocated transaction id and nameserver.
+ * They will soon be transmitted if they haven't already been.
+ *
+ * The second is the waiting queue. The size of the inflight ring is
+ * limited and all other requests wait in waiting queue for space. This
+ * bounds the number of concurrent requests so that we don't flood the
+ * nameserver. Several algorithms require a full walk of the inflight
+ * queue and so bounding its size keeps thing going nicely under huge
+ * (many thousands of requests) loads.
+ *
+ * If a nameserver loses too many requests it is considered down and we
+ * try not to use it. After a while we send a probe to that nameserver
+ * (a lookup for google.com) and, if it replies, we consider it working
+ * again. If the nameserver fails a probe we wait longer to try again
+ * with the next probe.
+ */
+
+#ifndef EVENTDNS_H
+#define EVENTDNS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Error codes 0-5 are as described in RFC 1035. */
+#define DNS_ERR_NONE 0
+/* The name server was unable to interpret the query */
+#define DNS_ERR_FORMAT 1
+/* The name server was unable to process this query due to a problem with the
+ * name server */
+#define DNS_ERR_SERVERFAILED 2
+/* The domain name does not exist */
+#define DNS_ERR_NOTEXIST 3
+/* The name server does not support the requested kind of query */
+#define DNS_ERR_NOTIMPL 4
+/* The name server refuses to reform the specified operation for policy
+ * reasons */
+#define DNS_ERR_REFUSED 5
+/* The reply was truncated or ill-formated */
+#define DNS_ERR_TRUNCATED 65
+/* An unknown error occurred */
+#define DNS_ERR_UNKNOWN 66
+/* Communication with the server timed out */
+#define DNS_ERR_TIMEOUT 67
+/* The request was canceled because the DNS subsystem was shut down. */
+#define DNS_ERR_SHUTDOWN 68
+
+#define DNS_IPv4_A 1
+#define DNS_PTR 2
+#define DNS_IPv6_AAAA 3
+
+#define DNS_QUERY_NO_SEARCH 1
+
+#define DNS_OPTION_SEARCH 1
+#define DNS_OPTION_NAMESERVERS 2
+#define DNS_OPTION_MISC 4
+#define DNS_OPTIONS_ALL 7
+
+/*
+ * The callback that contains the results from a lookup.
+ * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA
+ * - count contains the number of addresses of form type
+ * - ttl is the number of seconds the resolution may be cached for.
+ * - addresses needs to be cast according to type
+ */
+typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
+
+int evdns_init(void);
+void evdns_shutdown(int fail_requests);
+const char *evdns_err_to_string(int err);
+int evdns_nameserver_add(unsigned long int address);
+int evdns_count_nameservers(void);
+int evdns_clear_nameservers_and_suspend(void);
+int evdns_resume(void);
+int evdns_nameserver_ip_add(const char *ip_as_string);
+int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
+int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr);
+struct in_addr;
+struct in6_addr;
+int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
+int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr);
+int evdns_set_option(const char *option, const char *val, int flags);
+int evdns_resolv_conf_parse(int flags, const char *);
+#ifdef MS_WINDOWS
+int evdns_config_windows_nameservers(void);
+#endif
+void evdns_search_clear(void);
+void evdns_search_add(const char *domain);
+void evdns_search_ndots_set(const int ndots);
+
+typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
+void evdns_set_log_fn(evdns_debug_log_fn_type fn);
+
+#define DNS_NO_SEARCH 1
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Structures and functions used to implement a DNS server.
+ */
+
+struct evdns_server_request {
+ int flags;
+ int nquestions;
+ struct evdns_server_question **questions;
+};
+struct evdns_server_question {
+ int type;
+ int class;
+ char name[1];
+};
+typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
+#define EVDNS_ANSWER_SECTION 0
+#define EVDNS_AUTHORITY_SECTION 1
+#define EVDNS_ADDITIONAL_SECTION 2
+
+#define EVDNS_TYPE_A 1
+#define EVDNS_TYPE_NS 2
+#define EVDNS_TYPE_CNAME 5
+#define EVDNS_TYPE_SOA 6
+#define EVDNS_TYPE_PTR 12
+#define EVDNS_TYPE_MX 15
+#define EVDNS_TYPE_TXT 16
+#define EVDNS_TYPE_AAAA 28
+
+#define EVDNS_QTYPE_AXFR 252
+#define EVDNS_QTYPE_ALL 255
+
+#define EVDNS_CLASS_INET 1
+
+struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data);
+void evdns_close_server_port(struct evdns_server_port *port);
+
+int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data);
+int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
+int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
+int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl);
+int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl);
+
+int evdns_server_request_respond(struct evdns_server_request *req, int err);
+int evdns_server_request_drop(struct evdns_server_request *req);
+
+#endif // !EVENTDNS_H
diff --git a/lib/libevent/event-internal.h b/lib/libevent/event-internal.h
index becb6691d52..9a944b3ef0e 100644
--- a/lib/libevent/event-internal.h
+++ b/lib/libevent/event-internal.h
@@ -1,3 +1,5 @@
+/* $OpenBSD: event-internal.h,v 1.4 2007/03/19 15:12:49 millert Exp $ */
+
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
diff --git a/lib/libevent/event.3 b/lib/libevent/event.3
index 4bb7f80f4de..350cdc8155b 100644
--- a/lib/libevent/event.3
+++ b/lib/libevent/event.3
@@ -1,4 +1,4 @@
-.\" $OpenBSD: event.3,v 1.28 2006/11/26 15:22:58 brad Exp $
+.\" $OpenBSD: event.3,v 1.29 2007/03/19 15:12:49 millert Exp $
.\"
.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
.\" All rights reserved.
@@ -72,7 +72,9 @@
.Nm evbuffer_write ,
.Nm evbuffer_read ,
.Nm evbuffer_find ,
-.Nm evbuffer_readline
+.Nm evbuffer_readline ,
+.Nm evhttp_start ,
+.Nm evhttp_free
.Nd execute a function when a specific event occurs
.Sh SYNOPSIS
.Fd #include <sys/time.h>
@@ -152,7 +154,7 @@
.Ft void
.Fn "evbuffer_free" "struct evbuffer *buf"
.Ft int
-.Fn "evbuffer_add" "struct evbuffer *buf" "u_char *data" "size_t size"
+.Fn "evbuffer_add" "struct evbuffer *buf" "const void *data" "size_t size"
.Ft int
.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
.Ft int
@@ -169,6 +171,10 @@
.Fn "evbuffer_find" "struct evbuffer *buf" "const u_char *data" "size_t size"
.Ft "char *"
.Fn "evbuffer_readline" "struct evbuffer *buf"
+.Ft "struct evhttp *"
+.Fn "evhttp_start" "const char *address" "u_short port"
+.Ft "void"
+.Fn "evhttp_free" "struct evhttp* http"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft volatile sig_atomic_t
@@ -489,7 +495,7 @@ The error callback has to be always provided.
.Pp
Once initialized, the bufferevent structure can be used repeatedly with
bufferevent_enable() and bufferevent_disable().
-The flags parameter can be a combination of
+The flags parameter can be a combination of
.Va EV_READ
and
.Va EV_WRITE .
@@ -515,6 +521,28 @@ returning the amount of data read.
.Pp
If multiple bases are in use, bufferevent_base_set() must be called before
enabling the bufferevent for the first time.
+.Sh NON-BLOCKING HTTP SUPPORT
+.Nm libevent
+provides a very thin HTTP layer that can be used both to host an HTTP
+server and also to make HTTP requests.
+An HTTP server can be created by calling
+.Fn evhttp_start .
+When the HTTP server is no longer used, it can be freed via
+.Fn evhttp_free .
+.Pp
+To be notified of HTTP requests, a user needs to register callbacks with the
+HTTP server.
+This can be done by calling
+.Fn evhttp_set_cb .
+The second argument is the URI for which a callback is being registered.
+The corresponding callback will receive an
+.Va struct evhttp_request
+object that contains all information about the request.
+.Pp
+This section does not document all the possible function calls, please
+check
+.Va event.h
+for the public interfaces.
.Sh RETURN VALUES
Upon successful completion
.Fn event_add
diff --git a/lib/libevent/event.c b/lib/libevent/event.c
index cffcd7f961c..83d40bf97d3 100644
--- a/lib/libevent/event.c
+++ b/lib/libevent/event.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: event.c,v 1.16 2007/02/13 20:10:57 millert Exp $ */
+/* $OpenBSD: event.c,v 1.17 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
@@ -58,6 +58,9 @@
#include "event-internal.h"
#include "log.h"
+#ifdef HAVE_EVENT_PORTS
+extern const struct eventop evportops;
+#endif
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
@@ -82,6 +85,9 @@ extern const struct eventop win32ops;
/* In order of preference */
const struct eventop *eventops[] = {
+#ifdef HAVE_EVENT_PORTS
+ &evportops,
+#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
@@ -145,8 +151,12 @@ gettime(struct timeval *tp)
{
#ifdef HAVE_CLOCK_GETTIME
struct timespec ts;
-
+
+#ifdef HAVE_CLOCK_MONOTONIC
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+#else
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
+#endif
return (-1);
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
@@ -173,11 +183,11 @@ event_init(void)
event_sigcb = NULL;
event_gotsig = 0;
gettime(&current_base->event_tv);
-
+
RB_INIT(&current_base->timetree);
TAILQ_INIT(&current_base->eventqueue);
TAILQ_INIT(&signalqueue);
-
+
current_base->evbase = NULL;
for (i = 0; eventops[i] && !current_base->evbase; i++) {
current_base->evsel = eventops[i];
@@ -205,7 +215,7 @@ event_base_free(struct event_base *base)
if (base == NULL && current_base)
base = current_base;
- if (base == current_base)
+ if (base == current_base)
current_base = NULL;
assert(base);
@@ -293,9 +303,11 @@ event_process_active(struct event_base *base)
}
}
+ assert(activeq != NULL);
+
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
event_queue_remove(base, ev, EVLIST_ACTIVE);
-
+
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
@@ -403,7 +415,7 @@ event_base_loop(struct event_base *base, int flags)
timeout_next(base, &tv);
else
timerclear(&tv);
-
+
/* If we have no events, we just exit */
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
@@ -613,7 +625,7 @@ event_add(struct event *ev, struct timeval *tv)
/* Abort loop */
*ev->ev_pncalls = 0;
}
-
+
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
diff --git a/lib/libevent/event.h b/lib/libevent/event.h
index 26a7305273d..690322ee2af 100644
--- a/lib/libevent/event.h
+++ b/lib/libevent/event.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: event.h,v 1.17 2007/02/13 20:08:38 millert Exp $ */
+/* $OpenBSD: event.h,v 1.18 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
@@ -40,9 +40,10 @@ extern "C" {
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
typedef unsigned char u_char;
+typedef unsigned short u_short;
#endif
-#define LIBEVENT_VERSION "1.1b"
+#define LIBEVENT_VERSION "1.3b"
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
@@ -104,14 +105,28 @@ struct event {
int ev_flags;
};
-#define EVENT_SIGNAL(ev) (int)ev->ev_fd
-#define EVENT_FD(ev) (int)ev->ev_fd
+#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd
+#define EVENT_FD(ev) (int)(ev)->ev_fd
+
+/*
+ * Key-Value pairs. Can be used for HTTP headers but also for
+ * query argument parsing.
+ */
+struct evkeyval {
+ TAILQ_ENTRY(evkeyval) next;
+
+ char *key;
+ char *value;
+};
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
+struct event_list;
+struct evkeyvalq;
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
+TAILQ_HEAD (evkeyvalq, evkeyval);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
@@ -175,7 +190,7 @@ void event_active(struct event *, int, short);
int event_pending(struct event *, short, struct timeval *);
#ifdef WIN32
-#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != INVALID_HANDLE_VALUE)
+#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
@@ -262,7 +277,7 @@ void bufferevent_settimeout(struct bufferevent *bufev,
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *);
int evbuffer_expand(struct evbuffer *, size_t);
-int evbuffer_add(struct evbuffer *, void *, size_t);
+int evbuffer_add(struct evbuffer *, const void *, size_t);
int evbuffer_remove(struct evbuffer *, void *, size_t);
char *evbuffer_readline(struct evbuffer *);
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
@@ -274,6 +289,49 @@ int evbuffer_read(struct evbuffer *, int, int);
u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
+/*
+ * Marshaling tagged data - We assume that all tags are inserted in their
+ * numeric order - so that unknown tags will always be higher than the
+ * known ones - and we can just ignore the end of an event buffer.
+ */
+
+void evtag_init(void);
+
+void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data,
+ u_int32_t len);
+
+void encode_int(struct evbuffer *evbuf, u_int32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag,
+ u_int32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag,
+ const char *string);
+
+void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag,
+ struct timeval *tv);
+
+void evtag_test(void);
+
+int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag,
+ struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_consume(struct evbuffer *evbuf);
+
+int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
+ char **pstring);
+
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
+ struct timeval *ptv);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libevent/event_tagging.c b/lib/libevent/event_tagging.c
new file mode 100644
index 00000000000..4564fbb72b9
--- /dev/null
+++ b/lib/libevent/event_tagging.c
@@ -0,0 +1,374 @@
+/* $OpenBSD: event_tagging.c,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#else
+#include <sys/ioctl.h>
+#endif
+
+#include <sys/tree.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#endif
+#include <unistd.h>
+
+#include "event.h"
+#include "log.h"
+
+int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
+
+static struct evbuffer *_buf; /* not thread safe */
+
+void
+evtag_init()
+{
+ if (_buf != NULL)
+ return;
+
+ if ((_buf = evbuffer_new()) == NULL)
+ event_err(1, "%s: malloc", __func__);
+}
+
+/*
+ * We encode integer's by nibbles; the first nibble contains the number
+ * of significant nibbles - 1; this allows us to encode up to 64-bit
+ * integers. This function is byte-order independent.
+ */
+
+void
+encode_int(struct evbuffer *evbuf, u_int32_t number)
+{
+ int off = 1, nibbles = 0;
+ u_int8_t data[5];
+
+ memset(data, 0, sizeof(data));
+ while (number) {
+ if (off & 0x1)
+ data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f);
+ else
+ data[off/2] = (data[off/2] & 0x0f) |
+ ((number & 0x0f) << 4);
+ number >>= 4;
+ off++;
+ }
+
+ if (off > 2)
+ nibbles = off - 2;
+
+ /* Off - 1 is the number of encoded nibbles */
+ data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4);
+
+ evbuffer_add(evbuf, data, (off + 1) / 2);
+}
+
+/*
+ * Marshal a data type, the general format is as follows:
+ *
+ * tag number: one byte; length: var bytes; payload: var bytes
+ */
+
+void
+evtag_marshal(struct evbuffer *evbuf, u_int8_t tag,
+ const void *data, u_int32_t len)
+{
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, len);
+ evbuffer_add(evbuf, (void *)data, len);
+}
+
+/* Marshaling for integers */
+void
+evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag, u_int32_t integer)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ encode_int(_buf, integer);
+
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, EVBUFFER_LENGTH(_buf));
+ evbuffer_add_buffer(evbuf, _buf);
+}
+
+void
+evtag_marshal_string(struct evbuffer *buf, u_int8_t tag, const char *string)
+{
+ evtag_marshal(buf, tag, string, strlen(string));
+}
+
+void
+evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_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 __inline
+decode_int_internal(u_int32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+{
+ u_int32_t number = 0;
+ u_int8_t *data = EVBUFFER_DATA(evbuf);
+ int len = EVBUFFER_LENGTH(evbuf);
+ int nibbles = 0, off;
+
+ if (!len)
+ return (-1);
+
+ nibbles = ((data[0] & 0xf0) >> 4) + 1;
+ if (nibbles > 8 || (nibbles >> 1) > len - 1)
+ return (-1);
+
+ off = nibbles;
+ while (off > 0) {
+ number <<= 4;
+ if (off & 0x1)
+ number |= data[off >> 1] & 0x0f;
+ else
+ number |= (data[off >> 1] & 0xf0) >> 4;
+ off--;
+ }
+
+ len = (nibbles >> 1) + 1;
+ if (dodrain)
+ evbuffer_drain(evbuf, len);
+
+ *pnumber = number;
+
+ return (len);
+}
+
+int
+decode_int(u_int32_t *pnumber, struct evbuffer *evbuf)
+{
+ return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
+}
+
+int
+evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag)
+{
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+ *ptag = EVBUFFER_DATA(evbuf)[0];
+
+ return (0);
+}
+
+int
+evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ *plength += res + 1;
+
+ return (0);
+}
+
+int
+evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+evtag_consume(struct evbuffer *evbuf)
+{
+ u_int32_t len;
+ evbuffer_drain(evbuf, 1);
+ if (decode_int(&len, evbuf) == -1)
+ return (-1);
+ evbuffer_drain(evbuf, len);
+
+ return (0);
+}
+
+/* Reads the data type from an event buffer */
+
+int
+evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag, struct evbuffer *dst)
+{
+ u_int8_t tag;
+ u_int32_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag))
+ return (-1);
+ if (decode_int(&integer, src) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(src) < len)
+ return (-1);
+
+ if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1)
+ return (-1);
+
+ evbuffer_drain(src, len);
+
+ *ptag = tag;
+ return (len);
+}
+
+/* Marshaling for integers */
+
+int
+evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger)
+{
+ u_int8_t tag;
+ u_int32_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) ||
+ tag != need_tag)
+ return (-1);
+ if (decode_int(&integer, evbuf) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(evbuf) < len)
+ return (-1);
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1)
+ return (-1);
+
+ evbuffer_drain(evbuf, len);
+
+ return (decode_int(pinteger, _buf));
+}
+
+/* Unmarshal a fixed length tag */
+
+int
+evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len)
+{
+ u_int8_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, u_int8_t need_tag,
+ char **pstring)
+{
+ u_int8_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, u_int8_t need_tag,
+ struct timeval *ptv)
+{
+ u_int8_t tag;
+ u_int32_t integer;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_sec = integer;
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_usec = integer;
+
+ return (0);
+}
diff --git a/lib/libevent/evhttp.h b/lib/libevent/evhttp.h
new file mode 100644
index 00000000000..60317051258
--- /dev/null
+++ b/lib/libevent/evhttp.h
@@ -0,0 +1,216 @@
+/* $OpenBSD: evhttp.h,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+/*
+ * Basic support for HTTP serving.
+ *
+ * As libevent is a library for dealing with event notification and most
+ * interesting applications are networked today, I have often found the
+ * need to write HTTP code. The following prototypes and definitions provide
+ * an application with a minimal interface for making HTTP requests and for
+ * creating a very simple HTTP server.
+ */
+
+/* Response codes */
+#define HTTP_OK 200
+#define HTTP_NOCONTENT 204
+#define HTTP_MOVEPERM 301
+#define HTTP_MOVETEMP 302
+#define HTTP_NOTMODIFIED 304
+#define HTTP_BADREQUEST 400
+#define HTTP_NOTFOUND 404
+#define HTTP_SERVUNAVAIL 503
+
+struct evhttp;
+struct evhttp_request;
+struct evkeyvalq;
+
+/* Start an HTTP server on the specified address and port */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * Free the previously create HTTP server. Works only if no requests are
+ * currently being served.
+ */
+void evhttp_free(struct evhttp* http);
+
+/* Set a callback for a specified URI */
+void evhttp_set_cb(struct evhttp *, const char *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+/* Set a callback for all requests that are not caught by specific callbacks */
+void evhttp_set_gencb(struct evhttp *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+void evhttp_set_timeout(struct evhttp *, int timeout_in_secs);
+
+/* Request/Response functionality */
+
+void evhttp_send_error(struct evhttp_request *, int, const char *);
+void evhttp_send_reply(struct evhttp_request *, int, const char *,
+ struct evbuffer *);
+
+/* Low-level response interface, for streaming/chunked replies */
+void evhttp_send_reply_start(struct evhttp_request *, int, const char *);
+void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *);
+void evhttp_send_reply_end(struct evhttp_request *);
+
+/* Interfaces for making requests */
+enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
+
+enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
+
+/*
+ * the request structure that a server receives.
+ * WARNING: expect this structure to change. I will try to provide
+ * reasonable accessors.
+ */
+struct evhttp_request {
+ TAILQ_ENTRY(evhttp_request) next;
+
+ /* the connection object that this request belongs to */
+ struct evhttp_connection *evcon;
+ int flags;
+#define EVHTTP_REQ_OWN_CONNECTION 0x0001
+#define EVHTTP_PROXY_REQUEST 0x0002
+
+ struct evkeyvalq *input_headers;
+ struct evkeyvalq *output_headers;
+
+ /* address of the remote host and the port connection came from */
+ char *remote_host;
+ u_short remote_port;
+
+ enum evhttp_request_kind kind;
+ enum evhttp_cmd_type type;
+
+ char *uri; /* uri after HTTP request was parsed */
+
+ char major; /* HTTP Major number */
+ char minor; /* HTTP Minor number */
+
+ int got_firstline;
+ int response_code; /* HTTP Response code */
+ char *response_code_line; /* Readable response */
+
+ struct evbuffer *input_buffer; /* read data */
+ int ntoread;
+ int chunked;
+
+ struct evbuffer *output_buffer; /* outgoing post or data */
+
+ /* Callback */
+ void (*cb)(struct evhttp_request *, void *);
+ void *cb_arg;
+
+ /*
+ * Chunked data callback - call for each completed chunk if
+ * specified. If not specified, all the data is delivered via
+ * the regular callback.
+ */
+ void (*chunk_cb)(struct evhttp_request *, void *);
+};
+
+/*
+ * Creates a new request object that needs to be filled in with the request
+ * parameters. The callback is executed when the request completed or an
+ * error occurred.
+ */
+struct evhttp_request *evhttp_request_new(
+ void (*cb)(struct evhttp_request *, void *), void *arg);
+void evhttp_request_set_chunked_cb(struct evhttp_request *,
+ void (*cb)(struct evhttp_request *, void *));
+
+/* Frees the request object and removes associated events. */
+void evhttp_request_free(struct evhttp_request *req);
+
+/*
+ * A connection object that can be used to for making HTTP requests. The
+ * connection object tries to establish the connection when it is given an
+ * http request object.
+ */
+struct evhttp_connection *evhttp_connection_new(
+ const char *address, unsigned short port);
+
+/* Frees an http connection */
+void evhttp_connection_free(struct evhttp_connection *evcon);
+
+/* Sets the timeout for events related to this connection */
+void evhttp_connection_set_timeout(struct evhttp_connection *evcon,
+ int timeout_in_secs);
+
+/* Sets the retry limit for this connection - -1 repeats indefnitely */
+void evhttp_connection_set_retries(struct evhttp_connection *evcon,
+ int retry_max);
+
+/* Set a callback for connection close. */
+void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
+ void (*)(struct evhttp_connection *, void *), void *);
+
+/* Get the remote address and port associated with this connection. */
+void evhttp_connection_get_peer(struct evhttp_connection *evcon,
+ char **address, u_short *port);
+
+/* The connection gets ownership of the request */
+int evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri);
+
+const char *evhttp_request_uri(struct evhttp_request *req);
+
+/* Interfaces for dealing with HTTP headers */
+
+const char *evhttp_find_header(const struct evkeyvalq *, const char *);
+int evhttp_remove_header(struct evkeyvalq *, const char *);
+int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
+void evhttp_clear_headers(struct evkeyvalq *);
+
+/* Miscellaneous utility functions */
+char *evhttp_encode_uri(const char *uri);
+char *evhttp_decode_uri(const char *uri);
+void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+char *evhttp_htmlescape(const char *html);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVHTTP_H_ */
diff --git a/lib/libevent/http-internal.h b/lib/libevent/http-internal.h
new file mode 100644
index 00000000000..61c5418d15f
--- /dev/null
+++ b/lib/libevent/http-internal.h
@@ -0,0 +1,124 @@
+/* $OpenBSD: http-internal.h,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/*
+ * 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 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 *address;
+ 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 evhttp_cb {
+ TAILQ_ENTRY(evhttp_cb) next;
+
+ char *what;
+
+ void (*cb)(struct evhttp_request *req, void *);
+ void *cbarg;
+};
+
+struct evhttp {
+ struct event bind_ev;
+
+ TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
+ TAILQ_HEAD(evconq, evhttp_connection) connections;
+
+ int timeout;
+
+ void (*gencb)(struct evhttp_request *req, void *);
+ void *gencbarg;
+};
+
+/* 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/lib/libevent/http.c b/lib/libevent/http.c
new file mode 100644
index 00000000000..ed3d8b73457
--- /dev/null
+++ b/lib/libevent/http.c
@@ -0,0 +1,2296 @@
+/* $OpenBSD: http.c,v 1.1 2007/03/19 15:12:49 millert Exp $ */
+
+/*
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.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/tree.h>
+#include <sys/queue.h>
+
+#ifndef WIN32
+#include <netinet/in.h>
+#include <netdb.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>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+
+#undef timeout_pending
+#undef timeout_initialized
+
+#include "event.h"
+#include "evhttp.h"
+#include "log.h"
+#include "http-internal.h"
+
+#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;
+ he = gethostbyname(hostname);
+ if (!he)
+ return (-1);
+ ai->ai_family = he->h_addrtype;
+ ai->ai_socktype = SOCK_STREAM;
+ ai->ai_protocol = 0;
+ ai->ai_addrlen = he->h_length;
+ if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen)))
+ return (-1);
+ memcpy(ai->ai_addr, &he->h_addr_list[0], ai->ai_addrlen);
+ 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
+
+static int
+event_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;
+}
+
+extern int debug;
+
+static int make_socket_ai(int should_bind, struct addrinfo *);
+static int make_socket(int should_bind, 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
+static char *
+strsep(char **s, const char *del)
+{
+ char *d, *tok;
+ if (!s || !*s)
+ return NULL;
+ tok = *s;
+ d = strstr(tok, del);
+ if (d)
+ *s = d + strlen(del);
+ else
+ *s = NULL;
+ return tok;
+}
+#endif
+
+static const char *
+html_replace(char ch)
+{
+ static char buf[2];
+
+ 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)
+{
+ size_t i, new_size = 1, old_size = strlen(html);
+ char *escaped_html;
+
+ for (i = 0; i < old_size; ++i)
+ new_size += strlen(html_replace(html[i]));
+
+ escaped_html = malloc(new_size);
+ if (escaped_html == NULL)
+ event_err(1, "%s: malloc(%d)", __func__, new_size);
+ *escaped_html = '\0';
+ for (i = 0; i < old_size; ++i) {
+ const char *replaced = html_replace(html[i]);
+ strlcat(escaped_html, replaced, new_size);
+ }
+
+ return (escaped_html);
+}
+
+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;
+
+ 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_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT);
+}
+
+/*
+ * Create the headers need for an HTTP reply
+ */
+static void
+evhttp_make_header_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ static 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);
+}
+
+/*
+ * Create the headers needed for an HTTP reply
+ */
+static void
+evhttp_make_header_response(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ static 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));
+
+ /* 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");
+ }
+
+ /*
+ * we need to add the content length if the user did
+ * not give it, this is required for persistent
+ * connections to work.
+ */
+ if (evhttp_find_header(req->output_headers,
+ "Transfer-Encoding") == NULL &&
+ evhttp_find_header(req->output_headers,
+ "Content-Length") == NULL) {
+ static char len[12];
+ snprintf(len, sizeof(len), "%ld",
+ (long)EVBUFFER_LENGTH(req->output_buffer));
+ evhttp_add_header(req->output_headers,
+ "Content-Length", len);
+ }
+ }
+
+ /* 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)
+{
+ static 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)
+{
+ static char host[1024];
+ static char file[1024];
+ char *p, *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);
+ 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;
+ }
+
+
+ if (req->cb != NULL)
+ (*req->cb)(NULL, 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);
+}
+
+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_warn("%s: evbuffer_write", __func__);
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+
+ if (n == 0) {
+ event_warnx("%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);
+}
+
+void
+evhttp_connection_done(struct evhttp_connection *evcon)
+{
+ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+ /*
+ * if this is an incoming connection, we need to leave the request
+ * on the connection, so that we can reply to it.
+ */
+ if (evcon->flags & EVHTTP_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 (evcon->flags & EVHTTP_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)
+ continue;
+ req->ntoread = strtol(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);
+}
+
+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_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_warn("%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);
+}
+
+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)
+ close(evcon->fd);
+
+ 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);
+}
+
+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);
+
+ close(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);
+ 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_warnx("%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_warn("%s: getsockopt for \"%s:%d\" on %d",
+ __func__, evcon->address, evcon->port, evcon->fd);
+ goto cleanup;
+ }
+
+ if (error) {
+ event_warnx("%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_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);
+ }
+}
+
+/*
+ * Check if we got a valid response code.
+ */
+
+int
+evhttp_valid_response_code(int code)
+{
+ if (code == 0)
+ return (0);
+
+ return (1);
+}
+
+/* Parses the status line of a web server */
+
+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_warnx("%s: bad protocol \"%s\"",
+ __func__, protocol);
+ return (-1);
+ }
+
+ req->response_code = atoi(number);
+ if (!evhttp_valid_response_code(req->response_code)) {
+ event_warnx("%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 */
+
+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_warnx("%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_warnx("%s: bad version %s on request %p from %s",
+ __func__, version, req, req->remote_host);
+ return (-1);
+ }
+
+ if ((req->uri = strdup(uri)) == NULL) {
+ event_warn("%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;
+
+ 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)
+{
+ u_char *endp;
+ int done = 0;
+
+ struct evkeyvalq* headers = req->input_headers;
+ while ((endp = evbuffer_find(buffer, (u_char *)"\r\n", 2)) != NULL) {
+ char *skey, *svalue;
+
+ if (strncmp((char *)EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
+ evbuffer_drain(buffer, 2);
+ /* Last header - Done */
+ done = 1;
+ break;
+ }
+
+ *endp = '\0';
+ endp += 2;
+
+ /* Processing of header lines */
+ if (req->got_firstline == 0) {
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ if (evhttp_parse_request_line(req,
+ (char *)EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ case EVHTTP_RESPONSE:
+ if (evhttp_parse_response_line(req,
+ (char *)EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ req->got_firstline = 1;
+ } else {
+ /* Regular header */
+ svalue = (char *)EVBUFFER_DATA(buffer);
+ skey = strsep(&svalue, ":");
+ if (svalue == NULL)
+ return (-1);
+
+ svalue += strspn(svalue, " ");
+
+ if (evhttp_add_header(headers, skey, svalue) == -1)
+ return (-1);
+ }
+
+ /* Move the uncompleted headers forward */
+ evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
+ }
+
+ return (done);
+}
+
+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 = strtol(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(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_warnx("%s: no more data on %d", __func__, fd);
+ evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ return;
+ }
+ if (n == -1) {
+ event_warnx("%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_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;
+
+ /* Do async connection to HTTP server */
+ if ((evcon->fd = make_socket(
+ 0, evcon->address, evcon->port)) == -1) {
+ event_warn("%s: failed to connect to \"%s:%d\"",
+ __func__, evcon->address, evcon->port);
+ return (-1);
+ }
+
+ /* Set up a callback for successful connection setup */
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
+ 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_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
+}
+
+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)
+{
+ char *fmt = "<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, fmt, error, reason);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+}
+
+/* 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 expost 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",
+ EVBUFFER_LENGTH(databuf));
+ }
+ evbuffer_add_buffer(req->evcon->output_buffer, databuf);
+ 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(%d)", __func__, 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(uri[i+1]) &&
+ isxdigit(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);
+}
+
+void
+evhttp_handle_request(struct evhttp_request *req, void *arg)
+{
+ struct evhttp *http = arg;
+ struct evhttp_cb *cb;
+
+ if (req->uri == NULL) {
+ evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
+ return;
+ }
+
+ /* Test for different URLs */
+ TAILQ_FOREACH(cb, &http->callbacks, next) {
+ int res;
+ char *p = strchr(req->uri, '?');
+ if (p == NULL)
+ res = strcmp(cb->what, req->uri) == 0;
+ else
+ res = strncmp(cb->what, req->uri,
+ (size_t)(p - req->uri)) == 0;
+ if (res) {
+ (*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 */
+ char *fmt = "<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, fmt, escaped_html);
+
+ free(escaped_html);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+ }
+}
+
+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 (event_make_socket_nonblocking(fd) < 0)
+ return;
+
+ evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen);
+}
+
+static int
+bind_socket(struct evhttp *http, const char *address, u_short port)
+{
+ struct event *ev = &http->bind_ev;
+ int fd;
+
+ if ((fd = make_socket(1, address, port)) == -1)
+ return (-1);
+
+ if (listen(fd, 10) == -1) {
+ event_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ /* Schedule the socket for accepting */
+ event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
+ event_add(ev, NULL);
+
+ event_debug(("Bound to port %d - Awaiting connections ... ", port));
+
+ return (0);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, u_short port)
+{
+ struct evhttp *http;
+
+ 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);
+
+ if (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);
+ close(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);
+}
+
+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(
+ 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);
+ 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(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.
+ */
+
+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
+}
+
+static void
+name_from_addr(struct sockaddr *sa, socklen_t salen,
+ char **phost, char **pport)
+{
+#ifdef HAVE_GETNAMEINFO
+ 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
+make_socket_ai(int should_bind, struct addrinfo *ai)
+{
+ struct linger linger;
+ 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 (event_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));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+
+ if (should_bind)
+ r = bind(fd, ai->ai_addr, ai->ai_addrlen);
+ else
+ r = connect(fd, ai->ai_addr, ai->ai_addrlen);
+ if (r == -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
+ }
+
+ return (fd);
+
+ out:
+ serrno = errno;
+ close(fd);
+ errno = serrno;
+ return (-1);
+}
+
+static int
+make_socket(int should_bind, const char *address, u_short port)
+{
+ int fd;
+ struct addrinfo ai, *aitop = NULL;
+#ifdef HAVE_GETADDRINFO
+ 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 = should_bind ? AI_PASSIVE : 0;
+ 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 (-1);
+ }
+#else
+ if (fake_getaddrinfo(address, &ai) < 0) {
+ event_warn("fake_getaddrinfo");
+ return (-1);
+ }
+ aitop = &ai;
+#endif
+
+ fd = make_socket_ai(should_bind, aitop);
+
+#ifdef HAVE_GETADDRINFO
+ freeaddrinfo(aitop);
+#else
+ fake_freeaddrinfo(aitop);
+#endif
+
+ return (fd);
+}
diff --git a/lib/libevent/kqueue.c b/lib/libevent/kqueue.c
index d05144de0d4..c85334c29aa 100644
--- a/lib/libevent/kqueue.c
+++ b/lib/libevent/kqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kqueue.c,v 1.21 2006/11/05 17:07:07 brad Exp $ */
+/* $OpenBSD: kqueue.c,v 1.22 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
@@ -101,7 +101,7 @@ kq_init(void)
return (NULL);
/* Initalize the kernel queue */
-
+
if ((kq = kqueue()) == -1) {
event_warn("kqueue");
free (kqueueop);
@@ -295,7 +295,7 @@ kq_add(void *arg, struct event *ev)
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
@@ -318,7 +318,7 @@ kq_add(void *arg, struct event *ev)
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
@@ -333,7 +333,7 @@ kq_add(void *arg, struct event *ev)
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
@@ -359,7 +359,7 @@ kq_del(void *arg, struct event *ev)
kev.ident = nsignal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_DELETE;
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
@@ -375,7 +375,7 @@ kq_del(void *arg, struct event *ev)
kev.ident = ev->ev_fd;
kev.filter = EVFILT_READ;
kev.flags = EV_DELETE;
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
@@ -387,7 +387,7 @@ kq_del(void *arg, struct event *ev)
kev.ident = ev->ev_fd;
kev.filter = EVFILT_WRITE;
kev.flags = EV_DELETE;
-
+
if (kq_insert(kqop, &kev) == -1)
return (-1);
diff --git a/lib/libevent/log.c b/lib/libevent/log.c
index 3d9b5000b25..239c47842c9 100644
--- a/lib/libevent/log.c
+++ b/lib/libevent/log.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.4 2005/05/04 03:17:48 brad Exp $ */
+/* $OpenBSD: log.c,v 1.5 2007/03/19 15:12:49 millert Exp $ */
/*
* log.c
@@ -102,7 +102,7 @@ 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);
@@ -113,7 +113,7 @@ void
event_warn(const char *fmt, ...)
{
va_list ap;
-
+
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_WARN, errno, fmt, ap);
va_end(ap);
@@ -123,7 +123,7 @@ 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);
@@ -134,7 +134,7 @@ void
event_warnx(const char *fmt, ...)
{
va_list ap;
-
+
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_WARN, -1, fmt, ap);
va_end(ap);
@@ -144,7 +144,7 @@ void
event_msgx(const char *fmt, ...)
{
va_list ap;
-
+
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_MSG, -1, fmt, ap);
va_end(ap);
@@ -154,7 +154,7 @@ void
_event_debugx(const char *fmt, ...)
{
va_list ap;
-
+
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap);
va_end(ap);
diff --git a/lib/libevent/log.h b/lib/libevent/log.h
index 614018d546c..7aa4231b9b6 100644
--- a/lib/libevent/log.h
+++ b/lib/libevent/log.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.h,v 1.3 2005/04/22 00:56:25 brad Exp $ */
+/* $OpenBSD: log.h,v 1.4 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
@@ -39,7 +39,7 @@ void _event_debugx(const char *fmt, ...);
#ifdef USE_DEBUG
#define event_debug(x) _event_debugx x
#else
-#define event_debug(x)
+#define event_debug(x) do {;} while (0)
#endif
#endif
diff --git a/lib/libevent/select.c b/lib/libevent/select.c
index f3efa390f6e..75a344a3a56 100644
--- a/lib/libevent/select.c
+++ b/lib/libevent/select.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: select.c,v 1.12 2006/03/30 06:32:36 brad Exp $ */
+/* $OpenBSD: select.c,v 1.13 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
@@ -133,7 +133,7 @@ check_selectop(struct selectop *sop)
}
#else
-#define check_selectop(sop)
+#define check_selectop(sop) do { (void) sop; } while (0)
#endif
/*
diff --git a/lib/libevent/shlib_version b/lib/libevent/shlib_version
index 79d3d3afd76..1ce8f711fee 100644
--- a/lib/libevent/shlib_version
+++ b/lib/libevent/shlib_version
@@ -1,2 +1,2 @@
major=1
-minor=3
+minor=4
diff --git a/lib/libevent/signal.c b/lib/libevent/signal.c
index fa623a40c5d..6b2d052d787 100644
--- a/lib/libevent/signal.c
+++ b/lib/libevent/signal.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: signal.c,v 1.10 2005/12/02 16:54:25 deraadt Exp $ */
+/* $OpenBSD: signal.c,v 1.11 2007/03/19 15:12:49 millert Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
@@ -197,16 +197,14 @@ evsignal_process(void)
struct event *ev;
sig_atomic_t ncalls;
+ evsignal_caught = 0;
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);
+ evsigcaught[EVENT_SIGNAL(ev)] = 0;
}
}
-
- memset(evsigcaught, 0, sizeof(evsigcaught));
- evsignal_caught = 0;
}
-