diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2010-04-21 20:02:41 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2010-04-21 20:02:41 +0000 |
commit | 483c556c961377c65bc37a43cba770bfb7f24db2 (patch) | |
tree | f12eebcedd009fa08292bf672d9230e65b401f59 /lib/libevent | |
parent | 478d2f807269d4a1dc2066ed302f9716adb8b3f9 (diff) |
Update libevent to 1.4.13.
This is the core library only, the DNS parts are removed and it does not
include the other extra bits (HTTP, DNS, and RPC), a separate port for
these will appear in due course.
Thanks to jsg, sthen, alek, gilles, jacekm, bernd and any others I've
forgotten for testing/comments.
Note that /usr/include/evdns.h should be removed after updating.
ok deraadt
Diffstat (limited to 'lib/libevent')
-rw-r--r-- | lib/libevent/Makefile | 107 | ||||
-rw-r--r-- | lib/libevent/buffer.c | 150 | ||||
-rw-r--r-- | lib/libevent/evbuffer.c | 72 | ||||
-rw-r--r-- | lib/libevent/evdns.3 | 335 | ||||
-rw-r--r-- | lib/libevent/evdns.c | 3120 | ||||
-rw-r--r-- | lib/libevent/evdns.h | 369 | ||||
-rw-r--r-- | lib/libevent/event-config.h | 269 | ||||
-rw-r--r-- | lib/libevent/event-internal.h | 43 | ||||
-rw-r--r-- | lib/libevent/event.3 | 256 | ||||
-rw-r--r-- | lib/libevent/event.c | 409 | ||||
-rw-r--r-- | lib/libevent/event.h | 993 | ||||
-rw-r--r-- | lib/libevent/event_tagging.c | 223 | ||||
-rw-r--r-- | lib/libevent/evsignal.h | 14 | ||||
-rw-r--r-- | lib/libevent/evutil.c | 279 | ||||
-rw-r--r-- | lib/libevent/evutil.h | 188 | ||||
-rw-r--r-- | lib/libevent/kqueue.c | 167 | ||||
-rw-r--r-- | lib/libevent/log.c | 54 | ||||
-rw-r--r-- | lib/libevent/log.h | 22 | ||||
-rw-r--r-- | lib/libevent/min_heap.h | 151 | ||||
-rw-r--r-- | lib/libevent/poll.c | 61 | ||||
-rw-r--r-- | lib/libevent/select.c | 67 | ||||
-rw-r--r-- | lib/libevent/shlib_version | 2 | ||||
-rw-r--r-- | lib/libevent/signal.c | 247 |
23 files changed, 2972 insertions, 4626 deletions
diff --git a/lib/libevent/Makefile b/lib/libevent/Makefile index 457d36b9610..c3de4ef7aac 100644 --- a/lib/libevent/Makefile +++ b/lib/libevent/Makefile @@ -1,61 +1,94 @@ -# $OpenBSD: Makefile,v 1.28 2008/05/02 09:18:26 jmc Exp $ +# $OpenBSD: Makefile,v 1.29 2010/04/21 20:02:40 nicm Exp $ LIB= event WANTLINT= -SRCS= buffer.c evbuffer.c evdns.c event.c event_tagging.c kqueue.c \ +SRCS= buffer.c evbuffer.c event.c event_tagging.c evutil.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 event_base_set.3 \ - event.3 event_base_free.3 event.3 event_add.3 event.3 event_del.3 \ - event.3 event_once.3 event.3 event_base_once.3 \ - event.3 event_pending.3 event.3 event_initialized.3 \ - event.3 event_priority_init.3 event.3 event_priority_set.3 \ - event.3 evtimer_set.3 event.3 evtimer_add.3 \ - event.3 evtimer_del.3 event.3 evtimer_pending.3 \ - event.3 evtimer_initialized.3 event.3 signal_set.3 \ - event.3 signal_add.3 event.3 signal_del.3 event.3 signal_pending.3 \ - event.3 signal_initialized.3 event.3 bufferevent_new.3 \ - event.3 bufferevent_free.3 event.3 bufferevent_write.3 \ - event.3 bufferevent_write_buffer.3 event.3 bufferevent_read.3 \ - event.3 bufferevent_enable.3 event.3 bufferevent_disable.3 \ - event.3 bufferevent_settimeout.3 event.3 bufferevent_base_set.3 \ - event.3 evbuffer_new.3 event.3 evbuffer_free.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 \ - evdns.3 evdns_init.3 evdns.3 evdns_shutdown.3 \ - evdns.3 evdns_err_to_string.3 evdns.3 evdns_nameserver_add.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 +HDRS= event.h event-config.h evutil.h +MAN= event.3 +MLINKS= event.3 bufferevent_base_set.3 \ + event.3 bufferevent_disable.3 \ + event.3 bufferevent_enable.3 \ + event.3 bufferevent_free.3 \ + event.3 bufferevent_new.3 \ + event.3 bufferevent_read.3 \ + event.3 bufferevent_settimeout.3 \ + event.3 bufferevent_write.3 \ + event.3 bufferevent_write_buffer.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_find.3 \ + event.3 evbuffer_free.3 \ + event.3 evbuffer_new.3 \ + event.3 evbuffer_read.3 \ + event.3 evbuffer_readline.3 \ + event.3 evbuffer_write.3 \ + event.3 event_add.3 \ + event.3 event_base_dispatch.3 \ + event.3 event_base_free.3 \ + event.3 event_base_loop.3 \ + event.3 event_base_loopbreak.3 \ + event.3 event_base_loopexit.3 \ + event.3 event_base_once.3 \ + event.3 event_base_set.3 \ + event.3 event_del.3 \ + event.3 event_dispatch.3 \ + event.3 event_init.3 \ + event.3 event_initialized.3 \ + event.3 event_loop.3 \ + event.3 event_loopbreak.3 \ + event.3 event_loopexit.3 \ + event.3 event_once.3 \ + event.3 event_pending.3 \ + event.3 event_priority_init.3 \ + event.3 event_priority_set.3 \ + event.3 event_set.3 \ + event.3 evtimer_add.3 \ + event.3 evtimer_del.3 \ + event.3 evtimer_initialized.3 \ + event.3 evtimer_pending.3 \ + event.3 evtimer_set.3 \ + event.3 signal_add.3 \ + event.3 signal_del.3 \ + event.3 signal_initialized.3 \ + event.3 signal_pending.3 \ + event.3 signal_set.3 CFLAGS+= -I${.CURDIR} -DNDEBUG \ - -DDNS_USE_ARC4RANDOM_FOR_ID \ -DHAVE_CLOCK_GETTIME \ -DHAVE_CLOCK_MONOTONIC \ -DHAVE_FCNTL_H \ + -DHAVE_FD_MASK \ -DHAVE_GETADDRINFO \ + -DHAVE_GETEGID \ + -DHAVE_GETEUID \ -DHAVE_GETNAMEINFO \ + -DHAVE_INTTYPES_H \ + -DHAVE_ISSETUGID \ -DHAVE_POLL \ -DHAVE_SELECT \ -DHAVE_SETFD \ + -DHAVE_SIGACTION \ -DHAVE_STDARG_H \ -DHAVE_STDINT_H \ + -DHAVE_STDLIB_H \ + -DHAVE_STRLCPY \ -DHAVE_STRSEP \ -DHAVE_STRTOK_R \ + -DHAVE_STRTOLL \ -DHAVE_STRUCT_IN6_ADDR \ -DHAVE_SYS_IOCTL_H \ + -DHAVE_SYS_PARAM_H \ + -DHAVE_SYS_QUEUE_H \ + -DHAVE_SYS_SELECT_H \ + -DHAVE_SYS_SOCKET_H \ + -DHAVE_SYS_STAT_H \ -DHAVE_SYS_TIME_H \ - -DHAVE_UNISTD_H \ + -DHAVE_SYS_TYPES_H \ + -DHAVE_TAILQFOREACH \ -DHAVE_UNISTD_H \ -DHAVE_VASPRINTF \ -DHAVE_WORKING_KQUEUE diff --git a/lib/libevent/buffer.c b/lib/libevent/buffer.c index 0f531f979ea..142d5b33a4f 100644 --- a/lib/libevent/buffer.c +++ b/lib/libevent/buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.c,v 1.16 2009/10/13 12:16:33 jacekm Exp $ */ +/* $OpenBSD: buffer.c,v 1.17 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu> @@ -31,6 +31,11 @@ #include "config.h" #endif +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + #ifdef HAVE_VASPRINTF /* If we have vasprintf, we need to define this before we include stdio.h. */ #define _GNU_SOURCE @@ -59,12 +64,13 @@ #endif #include "event.h" +#include "evutil.h" struct evbuffer * evbuffer_new(void) { struct evbuffer *buffer; - + buffer = calloc(1, sizeof(struct evbuffer)); return (buffer); @@ -78,7 +84,7 @@ evbuffer_free(struct evbuffer *buffer) free(buffer); } -/* +/* * This is a destructive add. The data from one buffer moves into * the other buffer. */ @@ -106,7 +112,7 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) SWAP(outbuf, inbuf); SWAP(inbuf, &tmp); - /* + /* * Optimization comes with a price; we need to notify the * buffer if necessary of the changes. oldoff is the amount * of data that we transferred from inbuf to outbuf @@ -115,7 +121,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); } @@ -150,18 +156,13 @@ evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) #endif va_copy(aq, ap); -#ifdef WIN32 - sz = vsnprintf(buffer, space - 1, fmt, aq); - buffer[space - 1] = '\0'; -#else - sz = vsnprintf(buffer, space, fmt, aq); -#endif + sz = evutil_vsnprintf(buffer, space, fmt, aq); va_end(aq); if (sz < 0) return (-1); - if (sz < space) { + if ((size_t)sz < space) { buf->off += sz; if (buf->cb != NULL) (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); @@ -198,7 +199,7 @@ evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) memcpy(data, buf->buffer, nread); evbuffer_drain(buf, nread); - + return (nread); } @@ -248,6 +249,90 @@ evbuffer_readline(struct evbuffer *buffer) return (line); } + +char * +evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style) +{ + u_char *data = EVBUFFER_DATA(buffer); + u_char *start_of_eol, *end_of_eol; + size_t len = EVBUFFER_LENGTH(buffer); + char *line; + unsigned int i, n_to_copy, n_to_drain; + + /* depending on eol_style, set start_of_eol to the first character + * in the newline, and end_of_eol to one after the last character. */ + switch (eol_style) { + case EVBUFFER_EOL_ANY: + for (i = 0; i < len; i++) { + if (data[i] == '\r' || data[i] == '\n') + break; + } + if (i == len) + return (NULL); + start_of_eol = data+i; + ++i; + for ( ; i < len; i++) { + if (data[i] != '\r' && data[i] != '\n') + break; + } + end_of_eol = data+i; + break; + case EVBUFFER_EOL_CRLF: + end_of_eol = memchr(data, '\n', len); + if (!end_of_eol) + return (NULL); + if (end_of_eol > data && *(end_of_eol-1) == '\r') + start_of_eol = end_of_eol - 1; + else + start_of_eol = end_of_eol; + end_of_eol++; /*point to one after the LF. */ + break; + case EVBUFFER_EOL_CRLF_STRICT: { + u_char *cp = data; + while ((cp = memchr(cp, '\r', len-(cp-data)))) { + if (cp < data+len-1 && *(cp+1) == '\n') + break; + if (++cp >= data+len) { + cp = NULL; + break; + } + } + if (!cp) + return (NULL); + start_of_eol = cp; + end_of_eol = cp+2; + break; + } + case EVBUFFER_EOL_LF: + start_of_eol = memchr(data, '\n', len); + if (!start_of_eol) + return (NULL); + end_of_eol = start_of_eol + 1; + break; + default: + return (NULL); + } + + n_to_copy = start_of_eol - data; + n_to_drain = end_of_eol - data; + + if ((line = malloc(n_to_copy+1)) == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + evbuffer_drain(buffer, n_to_drain); + return (NULL); + } + + memcpy(line, data, n_to_copy); + line[n_to_copy] = '\0'; + + evbuffer_drain(buffer, n_to_drain); + if (n_read_out) + *n_read_out = (size_t)n_to_copy; + + return (line); +} + /* Adds data to an event buffer */ static void @@ -352,12 +437,14 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch) u_char *p; size_t oldoff = buf->off; int n = EVBUFFER_MAX_READ; + +#if defined(FIONREAD) #ifdef WIN32 - DWORD dwBytesRead; + long lng = n; + if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) <= 0) { +#else + if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) { #endif - -#ifdef FIONREAD - if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) { n = EVBUFFER_MAX_READ; } else if (n > EVBUFFER_MAX_READ && n > howmuch) { /* @@ -367,12 +454,12 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch) * about it. If the reader does not tell us how much * data we should read, we artifically limit it. */ - if (n > buf->totallen << 2) + if ((size_t)n > buf->totallen << 2) n = buf->totallen << 2; if (n < EVBUFFER_MAX_READ) n = EVBUFFER_MAX_READ; } -#endif +#endif if (howmuch < 0 || howmuch > n) howmuch = n; @@ -385,18 +472,13 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch) #ifndef WIN32 n = read(fd, p, howmuch); +#else + n = recv(fd, p, howmuch, 0); +#endif if (n == -1) return (-1); if (n == 0) return (0); -#else - n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL); - if (n == 0) - return (-1); - if (dwBytesRead == 0) - return (0); - n = dwBytesRead; -#endif buf->off += n; @@ -411,24 +493,16 @@ int evbuffer_write(struct evbuffer *buffer, int fd) { int n; -#ifdef WIN32 - DWORD dwBytesWritten; -#endif #ifndef WIN32 n = write(fd, buffer->buffer, buffer->off); +#else + n = send(fd, buffer->buffer, buffer->off, 0); +#endif if (n == -1) return (-1); if (n == 0) return (0); -#else - n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL); - if (n == 0) - return (-1); - if (dwBytesWritten == 0) - return (0); - n = dwBytesWritten; -#endif evbuffer_drain(buffer, n); return (n); diff --git a/lib/libevent/evbuffer.c b/lib/libevent/evbuffer.c index 25655cf548d..1c0f46e0a2c 100644 --- a/lib/libevent/evbuffer.c +++ b/lib/libevent/evbuffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: evbuffer.c,v 1.11 2008/05/02 18:26:42 brad Exp $ */ +/* $OpenBSD: evbuffer.c,v 1.12 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu> @@ -45,11 +45,15 @@ #include <stdarg.h> #endif +#ifdef WIN32 +#include <winsock2.h> +#endif + +#include "evutil.h" #include "event.h" /* prototypes */ -void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t); void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); static int @@ -58,7 +62,7 @@ bufferevent_add(struct event *ev, int timeout) struct timeval tv, *ptv = NULL; if (timeout) { - timerclear(&tv); + evutil_timerclear(&tv); tv.tv_sec = timeout; ptv = &tv; } @@ -66,7 +70,7 @@ bufferevent_add(struct event *ev, int timeout) return (event_add(ev, ptv)); } -/* +/* * This callback is executed when the size of the input buffer changes. * We use it to apply back pressure on the reading side. */ @@ -75,7 +79,7 @@ void bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now, void *arg) { struct bufferevent *bufev = arg; - /* + /* * If we are below the watermark then reschedule reading if it's * still enabled. */ @@ -105,8 +109,17 @@ bufferevent_readcb(int fd, short event, void *arg) * If we have a high watermark configured then we don't want to * read more data than would make us reach the watermark. */ - if (bufev->wm_read.high != 0) - howmuch = bufev->wm_read.high; + if (bufev->wm_read.high != 0) { + howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input); + /* we might have lowered the watermark, stop reading */ + if (howmuch <= 0) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + evbuffer_setcb(buf, + bufferevent_read_pressure_cb, bufev); + return; + } + } res = evbuffer_read(bufev->input, fd, howmuch); if (res == -1) { @@ -128,13 +141,12 @@ bufferevent_readcb(int fd, short event, void *arg) len = EVBUFFER_LENGTH(bufev->input); if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) return; - if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) { + if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) { struct evbuffer *buf = bufev->input; event_del(&bufev->ev_read); - /* Now schedule a callback for us */ + /* Now schedule a callback for us when the buffer changes */ evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); - return; } /* Invoke the user callback - must always be called last */ @@ -243,11 +255,7 @@ bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev); event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev); - bufev->readcb = readcb; - bufev->writecb = writecb; - bufev->errorcb = errorcb; - - bufev->cbarg = cbarg; + bufferevent_setcb(bufev, readcb, writecb, errorcb, cbarg); /* * Set to EV_WRITE so that using bufferevent_write is going to @@ -259,6 +267,33 @@ bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, return (bufev); } +void +bufferevent_setcb(struct bufferevent *bufev, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg) +{ + bufev->readcb = readcb; + bufev->writecb = writecb; + bufev->errorcb = errorcb; + + bufev->cbarg = cbarg; +} + +void +bufferevent_setfd(struct bufferevent *bufev, int fd) +{ + event_del(&bufev->ev_read); + event_del(&bufev->ev_write); + + event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev); + event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev); + if (bufev->ev_base != NULL) { + event_base_set(bufev->ev_base, &bufev->ev_read); + event_base_set(bufev->ev_base, &bufev->ev_write); + } + + /* might have to manually trigger event registration */ +} + int bufferevent_priority_set(struct bufferevent *bufev, int priority) { @@ -376,6 +411,11 @@ bufferevent_settimeout(struct bufferevent *bufev, int timeout_read, int timeout_write) { bufev->timeout_read = timeout_read; bufev->timeout_write = timeout_write; + + if (event_pending(&bufev->ev_read, EV_READ, NULL)) + bufferevent_add(&bufev->ev_read, timeout_read); + if (event_pending(&bufev->ev_write, EV_WRITE, NULL)) + bufferevent_add(&bufev->ev_write, timeout_write); } /* @@ -406,6 +446,8 @@ bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) { int res; + bufev->ev_base = base; + res = event_base_set(base, &bufev->ev_read); if (res == -1) return (res); diff --git a/lib/libevent/evdns.3 b/lib/libevent/evdns.3 deleted file mode 100644 index f0993264362..00000000000 --- a/lib/libevent/evdns.3 +++ /dev/null @@ -1,335 +0,0 @@ -.\" $OpenBSD: evdns.3,v 1.5 2007/05/31 19:19:35 jmc 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 $Mdocdate: May 31 2007 $ -.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 -Asynchronous 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 -.Fn getaddrinfo_a -(see http://www.imperialviolet.org/page25.html#e498). -Otherwise, please continue. -.Pp -This code is based on libevent and -.Fn event_init -must be called 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 -.Aq Pa evdns.h . -Hopefully it will be -.Dv DNS_ERR_NONE , -in which case type will be -.Dv DNS_IPv4_A , -.Fa count -will be the number of IP addresses, -.Fa ttl -is the time which the data can be cached for (in seconds), -.Fa addresses -will point to an array of uint32_t's and -.Fa arg -will be whatever was passed to -.Fn 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 -.Fn evdns_resolve . -Searching can also drastically slow down the resolution of names. -.Pp -To disable searching: -.Pp -.Bl -enum -compact -offset indent -.It -Never set it up. -If -.Fn evdns_resolv_conf_parse , -.Fn evdns_init , -and -.Fn evdns_search_add -are never called -then no searching will occur. -.It -If you do call -.Fn evdns_resolv_conf_parse -then don't pass -.Dv DNS_OPTION_SEARCH -(or -.Dv DNS_OPTIONS_ALL , -which implies it). -.It -When calling -.Fn evdns_resolve , -pass the -.Dv 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 -.Xr resolv.conf 5 , -or by calling -.Fn evdns_search_ndots_set . -.Pp -For example, with ndots set to 1 (the default) and a search domain list of -["myhome.net"]: -.Bd -literal -offset indent -Query: www -Order: www.myhome.net, www. - -Query: www.abc -Order: www.abc., www.abc.myhome.net -.Ed -.Sh API REFERENCE -.Bl -tag -width 0123456 -.It Fn "int evdns_init" -Initializes support for non-blocking name resolution by calling -.Fn evdns_resolv_conf_parse . -.It Fn "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. -.It Fn "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 -.It Fn "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 -.Dv DNS_QUERY_NO_SEARCH -which disables searching for this query -(see the definition of searching, above). -.Pp -The -.Fa callback -argument is a function which is called when -this query completes and -.Fa ptr -is an argument which is passed -to that callback function. -.Pp -Returns non-zero on error. -.It Fn "void evdns_search_clear" -Clears the list of search domains -.It Fn "void evdns_search_add" "const char *domain" -Add a domain to the list of search domains -.It Fn "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. -.It Fn "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 calls to the various -nameserver configuration functions have been successful. -.It Fn "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 -.Fn evdns_resume -is called. -.It Fn "int evdns_resume" "void" -Re-attempt resolves left in limbo after an earlier call to -.Fn evdns_clear_nameservers_and_suspend . -.It Fn "int 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 -.Xr resolv.conf 5 -for the format of this file. -The flags argument determines what information is parsed from -this file: -.Pp -.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -.It Dv DNS_OPTION_SEARCH -Domain, search, and ndots options. -.It Dv DNS_OPTION_NAMESERVERS -Nameserver lines. -.It Dv DNS_OPTION_MISC -Timeout and attempts options. -.It Dv DNS_OPTIONS_ALL -All of the above. -.El -.Pp -The following directives are not parsed from the file: -lookup and sortlist. -Additionally, the following -.Dq options -are ignored: debug, edns0, inet6, insecure1, and insecure2. -.Pp -Returns non-zero on error: -.Pp -.Bl -tag -width "0XXX" -offset indent -compact -.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. -.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 limits the number of concurrent requests -so that the nameserver does not get flooded. -Several algorithms require a full walk of the inflight -queue so limiting 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 a probe is sent 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 integrated 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 deleted file mode 100644 index f39703f3cd9..00000000000 --- a/lib/libevent/evdns.c +++ /dev/null @@ -1,3120 +0,0 @@ -/* $OpenBSD: evdns.c,v 1.4 2008/05/02 15:55:58 brad 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 -#ifndef DNS_USE_ARC4RANDOM_FOR_ID -#error Must configure at least one id generation method. -#error Please see the documentation. -#endif -#endif -#endif -#endif - -/* #define _POSIX_C_SOURCE 200507 */ -#define _GNU_SOURCE - -#ifdef DNS_USE_CPU_CLOCK_FOR_ID -#ifdef DNS_USE_OPENSSL_FOR_ID -#error Multiple id options selected -#endif -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID -#error Multiple id options selected -#endif -#include <time.h> -#endif - -#ifdef DNS_USE_OPENSSL_FOR_ID -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID -#error Multiple id options selected -#endif -#include <openssl/rand.h> -#endif - -#define _FORTIFY_SOURCE 3 - -#include <string.h> -#include <fcntl.h> -#include <sys/time.h> -#ifdef HAVE_STDINT_H -#include <stdint.h> -#endif -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <unistd.h> -#include <limits.h> -#include <sys/stat.h> -#include <ctype.h> -#include <stdio.h> -#include <stdarg.h> - -#include "evdns.h" -#include "log.h" -#ifdef WIN32 -#include <windows.h> -#include <winsock2.h> -#include <iphlpapi.h> -#else -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#endif - -#ifdef HAVE_NETINET_IN6_H -#include <netinet/in6.h> -#endif - -#ifdef WIN32 -typedef int socklen_t; -#endif - -#define EVDNS_LOG_DEBUG 0 -#define EVDNS_LOG_WARN 1 - -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX 255 -#endif - -#ifndef NDEBUG -#include <stdio.h> -#endif - -#undef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) - -#ifdef __USE_ISOC99B -/* libevent doesn't work without this */ -typedef uint8_t u_char; -typedef unsigned int uint; -#endif -#include <event.h> - -#define u64 uint64_t -#define u32 uint32_t -#define u16 uint16_t -#define u8 uint8_t - -#define MAX_ADDRS 32 /* maximum number of addresses from a single packet */ -/* which we bother recording */ - -#define TYPE_A EVDNS_TYPE_A -#define TYPE_CNAME 5 -#define TYPE_PTR EVDNS_TYPE_PTR -#define TYPE_AAAA EVDNS_TYPE_AAAA - -#define CLASS_INET EVDNS_CLASS_INET - -struct request { - u8 *request; /* the dns packet data */ - unsigned int request_len; - int reissue_count; - int tx_count; /* the number of times that this packet has been sent */ - unsigned int request_type; /* TYPE_PTR or TYPE_A */ - void *user_pointer; /* the pointer given to us for this request */ - evdns_callback_type user_callback; - struct nameserver *ns; /* the server which we last sent it */ - - /* elements used by the searching code */ - int search_index; - struct search_state *search_state; - char *search_origname; /* needs to be free()ed */ - int search_flags; - - /* these objects are kept in a circular list */ - struct request *next, *prev; - - struct event timeout_event; - - u16 trans_id; /* the transaction id */ - char request_appended; /* true if the request pointer is data which follows this struct */ - char transmit_me; /* needs to be transmitted */ -}; - -#ifndef HAVE_STRUCT_IN6_ADDR -struct in6_addr { - u8 s6_addr[16]; -}; -#endif - -struct reply { - unsigned int type; - unsigned int have_answer; - union { - struct { - u32 addrcount; - u32 addresses[MAX_ADDRS]; - } a; - struct { - u32 addrcount; - struct in6_addr addresses[MAX_ADDRS]; - } aaaa; - struct { - char name[HOST_NAME_MAX]; - } ptr; - } data; -}; - -struct nameserver { - int socket; /* a connected UDP socket */ - u32 address; - int failed_times; /* number of times which we have given this server a chance */ - int timedout; /* number of times in a row a request has timed out */ - struct event event; - /* these objects are kept in a circular list */ - struct nameserver *next, *prev; - struct event timeout_event; /* used to keep the timeout for */ - /* when we next probe this server. */ - /* Valid if state == 0 */ - char state; /* zero if we think that this server is down */ - char choked; /* true if we have an EAGAIN from this server's socket */ - char write_waiting; /* true if we are waiting for EV_WRITE events */ -}; - -static struct request *req_head = NULL, *req_waiting_head = NULL; -static struct nameserver *server_head = NULL; - -/* Represents a local port where we're listening for DNS requests. Right now, */ -/* only UDP is supported. */ -struct evdns_server_port { - int socket; /* socket we use to read queries and write replies. */ - int refcnt; /* reference count. */ - char choked; /* Are we currently blocked from writing? */ - char closing; /* Are we trying to close this port, pending writes? */ - evdns_request_callback_fn_type user_callback; /* Fn to handle requests */ - void *user_data; /* Opaque pointer passed to user_callback */ - struct event event; /* Read/write event */ - /* circular list of replies that we want to write. */ - struct server_request *pending_replies; -}; - -/* Represents part of a reply being built. (That is, a single RR.) */ -struct server_reply_item { - struct server_reply_item *next; /* next item in sequence. */ - char *name; /* name part of the RR */ - u16 type : 16; /* The RR type */ - u16 class : 16; /* The RR class (usually CLASS_INET) */ - u32 ttl; /* The RR TTL */ - char is_name; /* True iff data is a label */ - u16 datalen; /* Length of data; -1 if data is a label */ - void *data; /* The contents of the RR */ -}; - -/* Represents a request that we've received as a DNS server, and holds */ -/* the components of the reply as we're constructing it. */ -struct server_request { - /* Pointers to the next and previous entries on the list of replies */ - /* that we're waiting to write. Only set if we have tried to respond */ - /* and gotten EAGAIN. */ - struct server_request *next_pending; - struct server_request *prev_pending; - - u16 trans_id; /* Transaction id. */ - struct evdns_server_port *port; /* Which port received this request on? */ - struct sockaddr_storage addr; /* Where to send the response */ - socklen_t addrlen; /* length of addr */ - - int n_answer; /* how many answer RRs have been set? */ - int n_authority; /* how many authority RRs have been set? */ - int n_additional; /* how many additional RRs have been set? */ - - struct server_reply_item *answer; /* linked list of answer RRs */ - struct server_reply_item *authority; /* linked list of authority RRs */ - struct server_reply_item *additional; /* linked list of additional RRs */ - - /* Constructed response. Only set once we're ready to send a reply. */ - /* Once this is set, the RR fields are cleared, and no more should be set. */ - char *response; - size_t response_len; - - /* Caller-visible fields: flags, questions. */ - struct evdns_server_request base; -}; - -/* helper macro */ -#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0)) - -/* Given a pointer to an evdns_server_request, get the corresponding */ -/* server_request. */ -#define TO_SERVER_REQUEST(base_ptr) \ - ((struct server_request*) \ - (((char*)(base_ptr) - OFFSET_OF(struct server_request, base)))) - -/* The number of good nameservers that we have */ -static int global_good_nameservers = 0; - -/* inflight requests are contained in the req_head list */ -/* and are actually going out across the network */ -static int global_requests_inflight = 0; -/* requests which aren't inflight are in the waiting list */ -/* and are counted here */ -static int global_requests_waiting = 0; - -static int global_max_requests_inflight = 64; - -static struct timeval global_timeout = {5, 0}; /* 5 seconds */ -static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ -static int global_max_retransmits = 3; /* number of times we'll retransmit a request which timed out */ -/* number of timeouts in a row before we consider this server to be down */ -static int global_max_nameserver_timeout = 3; - -/* These are the timeout values for nameservers. If we find a nameserver is down */ -/* we try to probe it at intervals as given below. Values are in seconds. */ -static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}}; -static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval); - -static struct nameserver *nameserver_pick(void); -static void evdns_request_insert(struct request *req, struct request **head); -static void nameserver_ready_callback(int fd, short events, void *arg); -static int evdns_transmit(void); -static int evdns_request_transmit(struct request *req); -static void nameserver_send_probe(struct nameserver *const ns); -static void search_request_finished(struct request *const); -static int search_try_next(struct request *const req); -static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg); -static void evdns_requests_pump_waiting_queue(void); -static u16 transaction_id_pick(void); -static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr); -static void request_submit(struct request *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_NOTIMPL: - case DNS_ERR_REFUSED: - /* we regard these errors as marking a bad nameserver */ - if (req->reissue_count < global_max_reissues) { - char msg[64]; - snprintf(msg, sizeof(msg), "Bad response %d (%s)", - error, evdns_err_to_string(error)); - nameserver_failed(req->ns, msg); - if (!request_reissue(req)) return; - } - break; - case DNS_ERR_SERVERFAILED: - /* rcode 2 (servfailed) sometimes means "we are broken" and - * sometimes (with some binds) means "that request was very - * confusing." Treat this as a timeout, not a failure. - */ - log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; " - "will allow the request to time out.", - debug_ntoa(req->ns->address)); - break; - default: - /* we got a good reply from the nameserver */ - nameserver_up(req->ns); - } - - if (req->search_state && req->request_type != TYPE_PTR) { - /* if we have a list of domains to search in, try the next one */ - if (!search_try_next(req)) { - /* a new request was issued so this request is finished and */ - /* the user callback will be made when that request (or a */ - /* child of it) finishes. */ - request_finished(req, &req_head); - return; - } - } - - /* all else failed. Pass the failure up */ - reply_callback(req, 0, error, NULL); - request_finished(req, &req_head); - } else { - /* all ok, tell the user */ - reply_callback(req, ttl, 0, reply); - nameserver_up(req->ns); - request_finished(req, &req_head); - } -} - -static int -name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) { - int name_end = -1; - int j = *idx; - int ptr_count = 0; -#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0) -#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0) -#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0) - - char *cp = name_out; - const char *const end = name_out + name_out_len; - - /* Normally, names are a series of length prefixed strings terminated */ - /* with a length of 0 (the lengths are u8's < 63). */ - /* However, the length can start with a pair of 1 bits and that */ - /* means that the next 14 bits are a pointer within the current */ - /* packet. */ - - for(;;) { - u8 label_len; - if (j >= length) return -1; - GET8(label_len); - if (!label_len) break; - if (label_len & 0xc0) { - u8 ptr_low; - GET8(ptr_low); - if (name_end < 0) name_end = j; - j = (((int)label_len & 0x3f) << 8) + ptr_low; - /* Make sure that the target offset is in-bounds. */ - if (j < 0 || j >= length) return -1; - /* If we've jumped more times than there are characters in the - * message, we must have a loop. */ - if (++ptr_count > length) return -1; - continue; - } - if (label_len > 63) return -1; - if (cp != name_out) { - if (cp + 1 >= end) return -1; - *cp++ = '.'; - } - if (cp + label_len >= end) return -1; - memcpy(cp, packet + j, label_len); - cp += label_len; - j += label_len; - } - if (cp >= end) return -1; - *cp = '\0'; - if (name_end < 0) - *idx = j; - else - *idx = name_end; - return 0; - err: - return -1; -} - -/* parses a raw request from a nameserver */ -static int -reply_parse(u8 *packet, int length) { - int j = 0; /* index into packet */ - u16 _t; /* used by the macros */ - u32 _t32; /* used by the macros */ - char tmp_name[256]; /* used by the macros */ - - u16 trans_id, questions, answers, authority, additional, datalength; - u16 flags = 0; - u32 ttl, ttl_r = 0xffffffff; - struct reply reply; - struct request *req = NULL; - unsigned int i; - - GET16(trans_id); - GET16(flags); - GET16(questions); - GET16(answers); - GET16(authority); - GET16(additional); - (void) authority; /* suppress "unused variable" warnings. */ - (void) additional; /* suppress "unused variable" warnings. */ - - req = request_find_from_trans_id(trans_id); - if (!req) return -1; - - memset(&reply, 0, sizeof(reply)); - - /* If it's not an answer, it doesn't correspond to any request. */ - if (!(flags & 0x8000)) return -1; /* must be an answer */ - if (flags & 0x020f) { - /* there was an error */ - goto err; - } - /* if (!answers) return; */ /* must have an answer of some form */ - - /* This macro skips a name in the DNS reply. */ -#define SKIP_NAME \ - do { tmp_name[0] = '\0'; \ - if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \ - goto err; \ - } while(0); - - reply.type = req->request_type; - - /* skip over each question in the reply */ - for (i = 0; i < questions; ++i) { - /* the question looks like - * <label:name><u16:type><u16:class> - */ - SKIP_NAME; - j += 4; - if (j > length) goto err; - } - - /* now we have the answer section which looks like - * <label:name><u16:type><u16:class><u32:ttl><u16:len><data...> - */ - - for (i = 0; i < answers; ++i) { - u16 type, class; - - SKIP_NAME; - GET16(type); - GET16(class); - GET32(ttl); - GET16(datalength); - - if (type == TYPE_A && class == CLASS_INET) { - int addrcount, addrtocopy; - if (req->request_type != TYPE_A) { - j += datalength; continue; - } - if ((datalength & 3) != 0) /* not an even number of As. */ - goto err; - addrcount = datalength >> 2; - addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount); - - ttl_r = MIN(ttl_r, ttl); - /* we only bother with the first four addresses. */ - if (j + 4*addrtocopy > length) goto err; - memcpy(&reply.data.a.addresses[reply.data.a.addrcount], - packet + j, 4*addrtocopy); - j += 4*addrtocopy; - reply.data.a.addrcount += addrtocopy; - reply.have_answer = 1; - if (reply.data.a.addrcount == MAX_ADDRS) break; - } else if (type == TYPE_PTR && class == CLASS_INET) { - if (req->request_type != TYPE_PTR) { - j += datalength; continue; - } - if (name_parse(packet, length, &j, reply.data.ptr.name, - sizeof(reply.data.ptr.name))<0) - goto err; - ttl_r = MIN(ttl_r, ttl); - reply.have_answer = 1; - break; - } else if (type == TYPE_AAAA && class == CLASS_INET) { - int addrcount, addrtocopy; - if (req->request_type != TYPE_AAAA) { - j += datalength; continue; - } - if ((datalength & 15) != 0) /* not an even number of AAAAs. */ - goto err; - addrcount = datalength >> 4; /* each address is 16 bytes long */ - addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount); - ttl_r = MIN(ttl_r, ttl); - - /* we only bother with the first four addresses. */ - if (j + 16*addrtocopy > length) goto err; - memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount], - packet + j, 16*addrtocopy); - reply.data.aaaa.addrcount += addrtocopy; - j += 16*addrtocopy; - reply.have_answer = 1; - if (reply.data.aaaa.addrcount == MAX_ADDRS) break; - } else { - /* skip over any other type of resource */ - j += datalength; - } - } - - reply_handle(req, flags, ttl_r, &reply); - return 0; - err: - if (req) - reply_handle(req, flags, 0, NULL); - return -1; -} - -/* Parse a raw request (packet,length) sent to a nameserver port (port) from */ -/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */ -/* callback. */ -static int -request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen) -{ - int j = 0; /* index into packet */ - u16 _t; /* used by the macros */ - char tmp_name[256]; /* used by the macros */ - - int i; - u16 trans_id, flags, questions, answers, authority, additional; - struct server_request *server_req = NULL; - - /* Get the header fields */ - GET16(trans_id); - GET16(flags); - GET16(questions); - GET16(answers); - GET16(authority); - GET16(additional); - - if (flags & 0x8000) return -1; /* Must not be an answer. */ - if (flags & 0x7800) return -1; /* only standard queries are supported */ - flags &= 0x0300; /* Only TC and RD get preserved. */ - - server_req = malloc(sizeof(struct server_request)); - if (server_req == NULL) return -1; - memset(server_req, 0, sizeof(struct server_request)); - - server_req->trans_id = trans_id; - memcpy(&server_req->addr, addr, addrlen); - server_req->addrlen = addrlen; - - server_req->base.flags = flags; - server_req->base.nquestions = 0; - server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions); - if (server_req->base.questions == NULL) - goto err; - - for (i = 0; i < questions; ++i) { - u16 type, class; - struct evdns_server_question *q; - int namelen; - if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) - goto err; - GET16(type); - GET16(class); - namelen = strlen(tmp_name); - q = malloc(sizeof(struct evdns_server_question) + namelen); - if (!q) - goto err; - q->type = type; - q->class = class; - memcpy(q->name, tmp_name, namelen+1); - server_req->base.questions[server_req->base.nquestions++] = q; - } - - /* Ignore answers, authority, and additional. */ - - server_req->port = port; - port->refcnt++; - port->user_callback(&(server_req->base), port->user_data); - - return 0; -err: - if (server_req) { - if (server_req->base.questions) { - for (i = 0; i < server_req->base.nquestions; ++i) - free(server_req->base.questions[i]); - free(server_req->base.questions); - } - free(server_req); - } - return -1; - -#undef SKIP_NAME -#undef GET32 -#undef GET16 -#undef GET8 -} - -/* Try to choose a strong transaction id which isn't already in flight */ -static u16 -transaction_id_pick(void) { - for (;;) { - const struct request *req = req_head, *started_at; -#ifdef DNS_USE_CPU_CLOCK_FOR_ID - struct timespec ts; - u16 trans_id; -#ifdef CLOCK_MONOTONIC - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) -#else - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) -#endif - event_err(1, "clock_gettime"); - trans_id = ts.tv_nsec & 0xffff; -#endif - -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID - struct timeval tv; - u16 trans_id; - gettimeofday(&tv, NULL); - trans_id = tv.tv_usec & 0xffff; -#endif - -#ifdef DNS_USE_OPENSSL_FOR_ID - u16 trans_id; - if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) { - /* in the case that the RAND call fails we back */ - /* down to using gettimeofday. */ - struct timeval tv; - gettimeofday(&tv, NULL); - trans_id = tv.tv_usec & 0xffff; */ - abort(); - } -#endif - -#ifdef DNS_USE_ARC4RANDOM_FOR_ID - u16 trans_id; - trans_id = arc4random() & 0xffff; -#endif - - if (trans_id == 0xffff) continue; - /* now check to see if that id is already inflight */ - req = started_at = req_head; - if (req) { - do { - if (req->trans_id == trans_id) break; - req = req->next; - } while (req != started_at); - } - /* we didn't find it, so this is a good id */ - if (req == started_at) return trans_id; - } -} - -/* choose a namesever to use. This function will try to ignore */ -/* nameservers which we think are down and load balance across the rest */ -/* by updating the server_head global each time. */ -static struct nameserver * -nameserver_pick(void) { - struct nameserver *started_at = server_head, *picked; - if (!server_head) return NULL; - - /* if we don't have any good nameservers then there's no */ - /* point in trying to find one. */ - if (!global_good_nameservers) { - server_head = server_head->next; - return server_head; - } - - /* remember that nameservers are in a circular list */ - for (;;) { - if (server_head->state) { - /* we think this server is currently good */ - picked = server_head; - server_head = server_head->next; - return picked; - } - - server_head = server_head->next; - if (server_head == started_at) { - /* all the nameservers seem to be down */ - /* so we just return this one and hope for the */ - /* best */ - assert(global_good_nameservers == 0); - picked = server_head; - server_head = server_head->next; - return picked; - } - } -} - -/* this is called when a namesever socket is ready for reading */ -static void -nameserver_read(struct nameserver *ns) { - u8 packet[1500]; - - for (;;) { - const int r = recv(ns->socket, packet, sizeof(packet), 0); - if (r < 0) { - int err = last_error(ns->socket); - if (error_is_eagain(err)) return; - nameserver_failed(ns, strerror(err)); - return; - } - ns->timedout = 0; - reply_parse(packet, r); - } -} - -/* Read a packet from a DNS client on a server port s, parse it, and */ -/* act accordingly. */ -static void -server_port_read(struct evdns_server_port *s) { - u8 packet[1500]; - struct sockaddr_storage addr; - socklen_t addrlen; - int r; - - for (;;) { - addrlen = sizeof(struct sockaddr_storage); - r = recvfrom(s->socket, packet, sizeof(packet), 0, - (struct sockaddr*) &addr, &addrlen); - if (r < 0) { - int err = last_error(s->socket); - if (error_is_eagain(err)) return; - log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.", - strerror(err), err); - return; - } - request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen); - } -} - -/* Try to write all pending replies on a given DNS server port. */ -static void -server_port_flush(struct evdns_server_port *port) -{ - while (port->pending_replies) { - struct server_request *req = port->pending_replies; - int r = sendto(port->socket, req->response, req->response_len, 0, - (struct sockaddr*) &req->addr, req->addrlen); - if (r < 0) { - int err = last_error(port->socket); - if (error_is_eagain(err)) - return; - log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", strerror(err), err); - } - if (server_request_free(req)) { - /* we released the last reference to req->port. */ - return; - } - } - - /* We have no more pending requests; stop listening for 'writeable' events. */ - (void) event_del(&port->event); - event_set(&port->event, port->socket, EV_READ | EV_PERSIST, - server_port_ready_callback, port); - if (event_add(&port->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server."); - /* ???? Do more? */ - } -} - -/* set if we are waiting for the ability to write to this server. */ -/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */ -/* we stop these events. */ -static void -nameserver_write_waiting(struct nameserver *ns, char waiting) { - if (ns->write_waiting == waiting) return; - - ns->write_waiting = waiting; - (void) event_del(&ns->event); - event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST, - nameserver_ready_callback, ns); - if (event_add(&ns->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s", - debug_ntoa(ns->address)); - /* ???? Do more? */ - } -} - -/* a callback function. Called by libevent when the kernel says that */ -/* a nameserver socket is ready for writing or reading */ -static void -nameserver_ready_callback(int fd, short events, void *arg) { - struct nameserver *ns = (struct nameserver *) arg; - (void)fd; - - if (events & EV_WRITE) { - ns->choked = 0; - if (!evdns_transmit()) { - nameserver_write_waiting(ns, 0); - } - } - if (events & EV_READ) { - nameserver_read(ns); - } -} - -/* a callback function. Called by libevent when the kernel says that */ -/* a server socket is ready for writing or reading. */ -static void -server_port_ready_callback(int fd, short events, void *arg) { - struct evdns_server_port *port = (struct evdns_server_port *) arg; - (void) fd; - - if (events & EV_WRITE) { - port->choked = 0; - server_port_flush(port); - } - if (events & EV_READ) { - server_port_read(port); - } -} - -/* This is an inefficient representation; only use it via the dnslabel_table_* - * functions, so that is can be safely replaced with something smarter later. */ -#define MAX_LABELS 128 -/* Structures used to implement name compression */ -struct dnslabel_entry { char *v; off_t pos; }; -struct dnslabel_table { - int n_labels; /* number of current entries */ - /* map from name to position in message */ - struct dnslabel_entry labels[MAX_LABELS]; -}; - -/* Initialize dnslabel_table. */ -static void -dnslabel_table_init(struct dnslabel_table *table) -{ - table->n_labels = 0; -} - -/* Free all storage held by table, but not the table itself. */ -static void -dnslabel_clear(struct dnslabel_table *table) -{ - int i; - for (i = 0; i < table->n_labels; ++i) - free(table->labels[i].v); - table->n_labels = 0; -} - -/* return the position of the label in the current message, or -1 if the label */ -/* hasn't been used yet. */ -static int -dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label) -{ - int i; - for (i = 0; i < table->n_labels; ++i) { - if (!strcmp(label, table->labels[i].v)) - return table->labels[i].pos; - } - return -1; -} - -/* remember that we've used the label at position pos */ -static int -dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos) -{ - char *v; - int p; - if (table->n_labels == MAX_LABELS) - return (-1); - v = strdup(label); - if (v == NULL) - return (-1); - p = table->n_labels++; - table->labels[p].v = v; - table->labels[p].pos = pos; - - return (0); -} - -/* Converts a string to a length-prefixed set of DNS labels, starting */ -/* at buf[j]. name and buf must not overlap. name_len should be the length */ -/* of name. table is optional, and is used for compression. */ -/* */ -/* Input: abc.def */ -/* Output: <3>abc<3>def<0> */ -/* */ -/* Returns the first index after the encoded name, or negative on error. */ -/* -1 label was > 63 bytes */ -/* -2 name too long to fit in buffer. */ -/* */ -static off_t -dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j, - const char *name, const int name_len, - struct dnslabel_table *table) { - const char *end = name + name_len; - int ref = 0; - u16 _t; - -#define APPEND16(x) do { \ - if (j + 2 > (off_t)buf_len) \ - goto overflow; \ - _t = htons(x); \ - memcpy(buf + j, &_t, 2); \ - j += 2; \ - } while (0) -#define APPEND32(x) do { \ - if (j + 4 > (off_t)buf_len) \ - goto overflow; \ - _t32 = htonl(x); \ - memcpy(buf + j, &_t32, 4); \ - j += 4; \ - } while (0) - - if (name_len > 255) return -2; - - for (;;) { - const char *const start = name; - if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) { - APPEND16(ref | 0xc000); - return j; - } - name = strchr(name, '.'); - if (!name) { - const unsigned int label_len = end - start; - if (label_len > 63) return -1; - if ((size_t)(j+label_len+1) > buf_len) return -2; - if (table) dnslabel_table_add(table, start, j); - buf[j++] = label_len; - - memcpy(buf + j, start, end - start); - j += end - start; - break; - } else { - /* append length of the label. */ - const unsigned int label_len = name - start; - if (label_len > 63) return -1; - if ((size_t)(j+label_len+1) > buf_len) return -2; - if (table) dnslabel_table_add(table, start, j); - buf[j++] = label_len; - - memcpy(buf + j, start, name - start); - j += name - start; - /* hop over the '.' */ - name++; - } - } - - /* the labels must be terminated by a 0. */ - /* It's possible that the name ended in a . */ - /* in which case the zero is already there */ - if (!j || buf[j-1]) buf[j++] = 0; - return j; - overflow: - return (-2); -} - -/* Finds the length of a dns request for a DNS name of the given */ -/* length. The actual request may be smaller than the value returned */ -/* here */ -static int -evdns_request_len(const int name_len) { - return 96 + /* length of the DNS standard header */ - name_len + 2 + - 4; /* space for the resource type */ -} - -/* build a dns request packet into buf. buf should be at least as long */ -/* as evdns_request_len told you it should be. */ -/* */ -/* Returns the amount of space used. Negative on error. */ -static int -evdns_request_data_build(const char *const name, const int name_len, - const u16 trans_id, const u16 type, const u16 class, - u8 *const buf, size_t buf_len) { - off_t j = 0; /* current offset into buf */ - u16 _t; /* used by the macros */ - - APPEND16(trans_id); - APPEND16(0x0100); /* standard query, recusion needed */ - APPEND16(1); /* one question */ - APPEND16(0); /* no answers */ - APPEND16(0); /* no authority */ - APPEND16(0); /* no additional */ - - j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL); - if (j < 0) { - return (int)j; - } - - APPEND16(type); - APPEND16(class); - - return (int)j; - overflow: - return (-1); -} - -/* exported function */ -struct evdns_server_port * -evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data) -{ - struct evdns_server_port *port; - if (!(port = malloc(sizeof(struct evdns_server_port)))) - return NULL; - memset(port, 0, sizeof(struct evdns_server_port)); - - assert(!is_tcp); /* TCP sockets not yet implemented */ - port->socket = socket; - port->refcnt = 1; - port->choked = 0; - port->closing = 0; - port->user_callback = cb; - port->user_data = user_data; - port->pending_replies = NULL; - - event_set(&port->event, port->socket, EV_READ | EV_PERSIST, - server_port_ready_callback, port); - event_add(&port->event, NULL); /* check return. */ - return port; -} - -/* exported function */ -void -evdns_close_server_port(struct evdns_server_port *port) -{ - if (--port->refcnt == 0) - server_port_free(port); - port->closing = 1; -} - -/* exported function */ -int -evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - struct server_reply_item **itemp, *item; - int *countp; - - if (req->response) /* have we already answered? */ - return (-1); - - switch (section) { - case EVDNS_ANSWER_SECTION: - itemp = &req->answer; - countp = &req->n_answer; - break; - case EVDNS_AUTHORITY_SECTION: - itemp = &req->authority; - countp = &req->n_authority; - break; - case EVDNS_ADDITIONAL_SECTION: - itemp = &req->additional; - countp = &req->n_additional; - break; - default: - return (-1); - } - while (*itemp) { - itemp = &((*itemp)->next); - } - item = malloc(sizeof(struct server_reply_item)); - if (!item) - return -1; - item->next = NULL; - if (!(item->name = strdup(name))) { - free(item); - return -1; - } - item->type = type; - item->class = class; - item->ttl = ttl; - item->is_name = is_name != 0; - item->datalen = 0; - item->data = NULL; - if (data) { - if (item->is_name) { - if (!(item->data = strdup(data))) { - free(item->name); - free(item); - return -1; - } - item->datalen = (u16)-1; - } else { - if (!(item->data = malloc(datalen))) { - free(item->name); - free(item); - return -1; - } - item->datalen = datalen; - memcpy(item->data, data, datalen); - } - } - - *itemp = item; - ++(*countp); - return 0; -} - -/* exported function */ -int -evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, - ttl, n*4, 0, addrs); -} - -/* exported function */ -int -evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET, - ttl, n*16, 0, addrs); -} - -/* exported function */ -int -evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl) -{ - u32 a; - char buf[32]; - assert(in || inaddr_name); - assert(!(in && inaddr_name)); - if (in) { - a = ntohl(in->s_addr); - snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", - (int)(u8)((a )&0xff), - (int)(u8)((a>>8 )&0xff), - (int)(u8)((a>>16)&0xff), - (int)(u8)((a>>24)&0xff)); - inaddr_name = buf; - } - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET, - ttl, -1, 1, hostname); -} - -/* exported function */ -int -evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, - ttl, -1, 1, cname); -} - - -static int -evdns_server_request_format_response(struct server_request *req, int err) -{ - unsigned char buf[1500]; - size_t buf_len = sizeof(buf); - off_t j = 0, r; - u16 _t; - u32 _t32; - int i; - u16 flags; - struct dnslabel_table table; - - if (err < 0 || err > 15) return -1; - - /* Set response bit and error code; copy OPCODE and RD fields from - * question; copy RA and AA if set by caller. */ - flags = req->base.flags; - flags |= (0x8000 | err); - - dnslabel_table_init(&table); - APPEND16(req->trans_id); - APPEND16(flags); - APPEND16(req->base.nquestions); - APPEND16(req->n_answer); - APPEND16(req->n_authority); - APPEND16(req->n_additional); - - /* Add questions. */ - for (i=0; i < req->base.nquestions; ++i) { - const char *s = req->base.questions[i]->name; - j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table); - if (j < 0) { - dnslabel_clear(&table); - return (int) j; - } - APPEND16(req->base.questions[i]->type); - APPEND16(req->base.questions[i]->class); - } - - /* Add answer, authority, and additional sections. */ - for (i=0; i<3; ++i) { - struct server_reply_item *item; - if (i==0) - item = req->answer; - else if (i==1) - item = req->authority; - else - item = req->additional; - while (item) { - r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table); - if (r < 0) - goto overflow; - j = r; - - APPEND16(item->type); - APPEND16(item->class); - APPEND32(item->ttl); - if (item->is_name) { - off_t len_idx = j, name_start; - j += 2; - name_start = j; - r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table); - if (r < 0) - goto overflow; - j = r; - _t = htons( (j-name_start) ); - memcpy(buf+len_idx, &_t, 2); - } else { - APPEND16(item->datalen); - if (j+item->datalen > (off_t)buf_len) - goto overflow; - memcpy(buf+j, item->data, item->datalen); - j += item->datalen; - } - item = item->next; - } - } - - if (j > 512) { -overflow: - j = 512; - buf[3] |= 0x02; /* set the truncated bit. */ - } - - req->response_len = j; - - if (!(req->response = malloc(req->response_len))) { - server_request_free_answers(req); - dnslabel_clear(&table); - return (-1); - } - memcpy(req->response, buf, req->response_len); - server_request_free_answers(req); - dnslabel_clear(&table); - return (0); -} - -/* exported function */ -int -evdns_server_request_respond(struct evdns_server_request *_req, int err) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - struct evdns_server_port *port = req->port; - int r; - if (!req->response) { - if ((r = evdns_server_request_format_response(req, err))<0) - return r; - } - - r = sendto(port->socket, req->response, req->response_len, 0, - (struct sockaddr*) &req->addr, req->addrlen); - if (r<0) { - int err = last_error(port->socket); - if (! error_is_eagain(err)) - return -1; - - if (port->pending_replies) { - req->prev_pending = port->pending_replies->prev_pending; - req->next_pending = port->pending_replies; - req->prev_pending->next_pending = - req->next_pending->prev_pending = req; - } else { - req->prev_pending = req->next_pending = req; - port->pending_replies = req; - port->choked = 1; - - (void) event_del(&port->event); - event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port); - - if (event_add(&port->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server"); - } - - } - - return 1; - } - if (server_request_free(req)) - return 0; - - if (port->pending_replies) - server_port_flush(port); - - return 0; -} - -/* Free all storage held by RRs in req. */ -static void -server_request_free_answers(struct server_request *req) -{ - struct server_reply_item *victim, *next, **list; - int i; - for (i = 0; i < 3; ++i) { - if (i==0) - list = &req->answer; - else if (i==1) - list = &req->authority; - else - list = &req->additional; - - victim = *list; - while (victim) { - next = victim->next; - free(victim->name); - if (victim->data) - free(victim->data); - free(victim); - victim = next; - } - *list = NULL; - } -} - -/* Free all storage held by req, and remove links to it. */ -/* return true iff we just wound up freeing the server_port. */ -static int -server_request_free(struct server_request *req) -{ - int i, rc=1; - if (req->base.questions) { - for (i = 0; i < req->base.nquestions; ++i) - free(req->base.questions[i]); - free(req->base.questions); - } - - if (req->port) { - if (req->port->pending_replies == req) { - if (req->next_pending) - req->port->pending_replies = req->next_pending; - else - req->port->pending_replies = NULL; - } - rc = --req->port->refcnt; - } - - if (req->response) { - free(req->response); - } - - server_request_free_answers(req); - - if (req->next_pending && req->next_pending != req) { - req->next_pending->prev_pending = req->prev_pending; - req->prev_pending->next_pending = req->next_pending; - } - - if (rc == 0) { - server_port_free(req->port); - free(req); - return (1); - } - free(req); - return (0); -} - -/* Free all storage held by an evdns_server_port. Only called when */ -static void -server_port_free(struct evdns_server_port *port) -{ - assert(port); - assert(!port->refcnt); - assert(!port->pending_replies); - if (port->socket > 0) { - CLOSE_SOCKET(port->socket); - port->socket = -1; - } - (void) event_del(&port->event); - /* XXXX actually free the port? -NM */ -} - -/* exported function */ -int -evdns_server_request_drop(struct evdns_server_request *_req) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - server_request_free(req); - return 0; -} - -/* exported function */ -int -evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - if (addr_len < (int)req->addrlen) - return -1; - memcpy(sa, &(req->addr), req->addrlen); - return req->addrlen; -} - -#undef APPEND16 -#undef APPEND32 - -/* this is a libevent callback function which is called when a request */ -/* has timed out. */ -static void -evdns_request_timeout_callback(int fd, short events, void *arg) { - struct request *const req = (struct request *) arg; - (void) fd; - (void) events; - - log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg); - - req->ns->timedout++; - if (req->ns->timedout > global_max_nameserver_timeout) { - req->ns->timedout = 0; - nameserver_failed(req->ns, "request timed out."); - } - - (void) evtimer_del(&req->timeout_event); - if (req->tx_count >= global_max_retransmits) { - /* this request has failed */ - reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); - request_finished(req, &req_head); - } else { - /* retransmit it */ - evdns_request_transmit(req); - } -} - -/* try to send a request to a given server. */ -/* */ -/* return: */ -/* 0 ok */ -/* 1 temporary failure */ -/* 2 other failure */ -static int -evdns_request_transmit_to(struct request *req, struct nameserver *server) { - const int r = send(server->socket, req->request, req->request_len, 0); - if (r < 0) { - int err = last_error(server->socket); - if (error_is_eagain(err)) return 1; - nameserver_failed(req->ns, strerror(err)); - return 2; - } else if (r != (int)req->request_len) { - return 1; /* short write */ - } else { - return 0; - } -} - -/* try to send a request, updating the fields of the request */ -/* as needed */ -/* */ -/* return: */ -/* 0 ok */ -/* 1 failed */ -static int -evdns_request_transmit(struct request *req) { - int retcode = 0, r; - - /* if we fail to send this packet then this flag marks it */ - /* for evdns_transmit */ - req->transmit_me = 1; - if (req->trans_id == 0xffff) abort(); - - if (req->ns->choked) { - /* don't bother trying to write to a socket */ - /* which we have had EAGAIN from */ - return 1; - } - - r = evdns_request_transmit_to(req, req->ns); - switch (r) { - case 1: - /* temp failure */ - req->ns->choked = 1; - nameserver_write_waiting(req->ns, 1); - return 1; - case 2: - /* failed in some other way */ - retcode = 1; - /* fall through */ - default: - /* all ok */ - log(EVDNS_LOG_DEBUG, - "Setting timeout for request %lx", (unsigned long) req); - evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req); - if (evtimer_add(&req->timeout_event, &global_timeout) < 0) { - log(EVDNS_LOG_WARN, - "Error from libevent when adding timer for request %lx", - (unsigned long) req); - /* ???? Do more? */ - } - req->tx_count++; - req->transmit_me = 0; - return retcode; - } -} - -static void -nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) { - struct nameserver *const ns = (struct nameserver *) arg; - (void) type; - (void) count; - (void) ttl; - (void) addresses; - - if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) { - /* this is a good reply */ - nameserver_up(ns); - } else nameserver_probe_failed(ns); -} - -static void -nameserver_send_probe(struct nameserver *const ns) { - struct request *req; - /* here we need to send a probe to a given nameserver */ - /* in the hope that it is up now. */ - - log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address)); - - req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns); - if (!req) return; - /* we force this into the inflight queue no matter what */ - request_trans_id_set(req, transaction_id_pick()); - req->ns = ns; - request_submit(req); -} - -/* returns: */ -/* 0 didn't try to transmit anything */ -/* 1 tried to transmit something */ -static int -evdns_transmit(void) { - char did_try_to_transmit = 0; - - if (req_head) { - struct request *const started_at = req_head, *req = req_head; - /* first transmit all the requests which are currently waiting */ - do { - if (req->transmit_me) { - did_try_to_transmit = 1; - evdns_request_transmit(req); - } - - req = req->next; - } while (req != started_at); - } - - return did_try_to_transmit; -} - -/* exported function */ -int -evdns_count_nameservers(void) -{ - const struct nameserver *server = server_head; - int n = 0; - if (!server) - return 0; - do { - ++n; - server = server->next; - } while (server != server_head); - return n; -} - -/* exported function */ -int -evdns_clear_nameservers_and_suspend(void) -{ - struct nameserver *server = server_head, *started_at = server_head; - struct request *req = req_head, *req_started_at = req_head; - - if (!server) - return 0; - while (1) { - struct nameserver *next = server->next; - (void) event_del(&server->event); - (void) evtimer_del(&server->timeout_event); - if (server->socket >= 0) - CLOSE_SOCKET(server->socket); - free(server); - if (next == started_at) - break; - server = next; - } - server_head = NULL; - global_good_nameservers = 0; - - while (req) { - struct request *next = req->next; - req->tx_count = req->reissue_count = 0; - req->ns = NULL; - /* ???? What to do about searches? */ - (void) evtimer_del(&req->timeout_event); - req->trans_id = 0; - req->transmit_me = 0; - - global_requests_waiting++; - evdns_request_insert(req, &req_waiting_head); - /* We want to insert these suspended elements at the front of - * the waiting queue, since they were pending before any of - * the waiting entries were added. This is a circular list, - * so we can just shift the start back by one.*/ - req_waiting_head = req_waiting_head->prev; - - if (next == req_started_at) - break; - req = next; - } - req_head = NULL; - global_requests_inflight = 0; - - return 0; -} - - -/* exported function */ -int -evdns_resume(void) -{ - evdns_requests_pump_waiting_queue(); - return 0; -} - -static int -_evdns_nameserver_add_impl(unsigned long int address, int port) { - /* first check to see if we already have this nameserver */ - - const struct nameserver *server = server_head, *const started_at = server_head; - struct nameserver *ns; - struct sockaddr_in sin; - int err = 0; - if (server) { - do { - if (server->address == address) return 3; - server = server->next; - } while (server != started_at); - } - - ns = (struct nameserver *) malloc(sizeof(struct nameserver)); - if (!ns) return -1; - - memset(ns, 0, sizeof(struct nameserver)); - - ns->socket = socket(PF_INET, SOCK_DGRAM, 0); - if (ns->socket < 0) { err = 1; goto out1; } -#ifdef WIN32 - { - u_long nonblocking = 1; - ioctlsocket(ns->socket, FIONBIO, &nonblocking); - } -#else - fcntl(ns->socket, F_SETFL, O_NONBLOCK); -#endif - sin.sin_addr.s_addr = address; - sin.sin_port = htons(port); - sin.sin_family = AF_INET; - if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) { - err = 2; - goto out2; - } - - ns->address = address; - ns->state = 1; - event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns); - if (event_add(&ns->event, NULL) < 0) { - err = 2; - goto out2; - } - - log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address)); - - /* insert this nameserver into the list of them */ - if (!server_head) { - ns->next = ns->prev = ns; - server_head = ns; - } else { - ns->next = server_head->next; - ns->prev = server_head; - server_head->next = ns; - if (server_head->prev == server_head) { - server_head->prev = ns; - } - } - - global_good_nameservers++; - - return 0; - -out2: - CLOSE_SOCKET(ns->socket); -out1: - free(ns); - log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err); - return err; -} - -/* exported function */ -int -evdns_nameserver_add(unsigned long int address) { - return _evdns_nameserver_add_impl(address, 53); -} - -/* exported function */ -int -evdns_nameserver_ip_add(const char *ip_as_string) { - struct in_addr ina; - int port; - char buf[20]; - const char *cp; - cp = strchr(ip_as_string, ':'); - if (! cp) { - cp = ip_as_string; - port = 53; - } else { - port = strtoint(cp+1); - if (port < 0 || port > 65535) { - return 4; - } - if ((cp-ip_as_string) >= (int)sizeof(buf)) { - return 4; - } - memcpy(buf, ip_as_string, cp-ip_as_string); - buf[cp-ip_as_string] = '\0'; - cp = buf; - } - if (!inet_aton(cp, &ina)) { - return 4; - } - return _evdns_nameserver_add_impl(ina.s_addr, port); -} - -/* insert into the tail of the queue */ -static void -evdns_request_insert(struct request *req, struct request **head) { - if (!*head) { - *head = req; - req->next = req->prev = req; - return; - } - - req->prev = (*head)->prev; - req->prev->next = req; - req->next = *head; - (*head)->prev = req; -} - -static int -string_num_dots(const char *s) { - int count = 0; - while ((s = strchr(s, '.'))) { - s++; - count++; - } - return count; -} - -static struct request * -request_new(int type, const char *name, int flags, - evdns_callback_type callback, void *user_ptr) { - const char issuing_now = - (global_requests_inflight < global_max_requests_inflight) ? 1 : 0; - - const int name_len = strlen(name); - const int request_max_len = evdns_request_len(name_len); - const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff; - /* the request data is alloced in a single block with the header */ - struct request *const req = - (struct request *) malloc(sizeof(struct request) + request_max_len); - int rlen; - (void) flags; - - if (!req) return NULL; - memset(req, 0, sizeof(struct request)); - - /* request data lives just after the header */ - req->request = ((u8 *) req) + sizeof(struct request); - /* denotes that the request data shouldn't be free()ed */ - req->request_appended = 1; - rlen = evdns_request_data_build(name, name_len, trans_id, - type, CLASS_INET, req->request, request_max_len); - if (rlen < 0) - goto err1; - req->request_len = rlen; - req->trans_id = trans_id; - req->tx_count = 0; - req->request_type = type; - req->user_pointer = user_ptr; - req->user_callback = callback; - req->ns = issuing_now ? nameserver_pick() : NULL; - req->next = req->prev = NULL; - - return req; -err1: - free(req); - return NULL; -} - -static void -request_submit(struct request *const req) { - if (req->ns) { - /* if it has a nameserver assigned then this is going */ - /* straight into the inflight queue */ - evdns_request_insert(req, &req_head); - global_requests_inflight++; - evdns_request_transmit(req); - } else { - evdns_request_insert(req, &req_waiting_head); - global_requests_waiting++; - } -} - -/* exported function */ -int evdns_resolve_ipv4(const char *name, int flags, - evdns_callback_type callback, void *ptr) { - log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); - if (flags & DNS_QUERY_NO_SEARCH) { - struct request *const req = - request_new(TYPE_A, name, flags, callback, ptr); - if (req == NULL) - return (1); - request_submit(req); - return (0); - } else { - return (search_request_new(TYPE_A, name, flags, callback, ptr)); - } -} - -/* exported function */ -int evdns_resolve_ipv6(const char *name, int flags, - evdns_callback_type callback, void *ptr) { - log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); - if (flags & DNS_QUERY_NO_SEARCH) { - struct request *const req = - request_new(TYPE_AAAA, name, flags, callback, ptr); - if (req == NULL) - return (1); - request_submit(req); - return (0); - } else { - return (search_request_new(TYPE_AAAA, name, flags, callback, ptr)); - } -} - -int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { - char buf[32]; - struct request *req; - u32 a; - assert(in); - a = ntohl(in->s_addr); - snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", - (int)(u8)((a )&0xff), - (int)(u8)((a>>8 )&0xff), - (int)(u8)((a>>16)&0xff), - (int)(u8)((a>>24)&0xff)); - log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); - req = request_new(TYPE_PTR, buf, flags, callback, ptr); - if (!req) return 1; - request_submit(req); - return 0; -} - -int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { - /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */ - char buf[73]; - char *cp; - struct request *req; - int i; - assert(in); - cp = buf; - for (i=15; i >= 0; --i) { - u8 byte = in->s6_addr[i]; - *cp++ = "0123456789abcdef"[byte & 0x0f]; - *cp++ = '.'; - *cp++ = "0123456789abcdef"[byte >> 4]; - *cp++ = '.'; - } - assert(cp + strlen(".ip6.arpa") < buf+sizeof(buf)); - memcpy(cp, ".ip6.arpa", strlen(".ip6.arpa")+1); - log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); - req = request_new(TYPE_PTR, buf, flags, callback, ptr); - if (!req) return 1; - request_submit(req); - return 0; -} - -/*/////////////////////////////////////////////////////////////////// */ -/* Search support */ -/* */ -/* the libc resolver has support for searching a number of domains */ -/* to find a name. If nothing else then it takes the single domain */ -/* from the gethostname() call. */ -/* */ -/* It can also be configured via the domain and search options in a */ -/* resolv.conf. */ -/* */ -/* The ndots option controls how many dots it takes for the resolver */ -/* to decide that a name is non-local and so try a raw lookup first. */ - -struct search_domain { - int len; - struct search_domain *next; - /* the text string is appended to this structure */ -}; - -struct search_state { - int refcount; - int ndots; - int num_domains; - struct search_domain *head; -}; - -static struct search_state *global_search_state = NULL; - -static void -search_state_decref(struct search_state *const state) { - if (!state) return; - state->refcount--; - if (!state->refcount) { - struct search_domain *next, *dom; - for (dom = state->head; dom; dom = next) { - next = dom->next; - free(dom); - } - free(state); - } -} - -static struct search_state * -search_state_new(void) { - struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state)); - if (!state) return NULL; - memset(state, 0, sizeof(struct search_state)); - state->refcount = 1; - state->ndots = 1; - - return state; -} - -static void -search_postfix_clear(void) { - search_state_decref(global_search_state); - - global_search_state = search_state_new(); -} - -/* exported function */ -void -evdns_search_clear(void) { - search_postfix_clear(); -} - -static void -search_postfix_add(const char *domain) { - int domain_len; - struct search_domain *sdomain; - while (domain[0] == '.') domain++; - domain_len = strlen(domain); - - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return; - global_search_state->num_domains++; - - sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len); - if (!sdomain) return; - memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len); - sdomain->next = global_search_state->head; - sdomain->len = domain_len; - - global_search_state->head = sdomain; -} - -/* reverse the order of members in the postfix list. This is needed because, */ -/* when parsing resolv.conf we push elements in the wrong order */ -static void -search_reverse(void) { - struct search_domain *cur, *prev = NULL, *next; - cur = global_search_state->head; - while (cur) { - next = cur->next; - cur->next = prev; - prev = cur; - cur = next; - } - - global_search_state->head = prev; -} - -/* exported function */ -void -evdns_search_add(const char *domain) { - search_postfix_add(domain); -} - -/* exported function */ -void -evdns_search_ndots_set(const int ndots) { - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return; - global_search_state->ndots = ndots; -} - -static void -search_set_from_hostname(void) { - char hostname[HOST_NAME_MAX + 1], *domainname; - - search_postfix_clear(); - if (gethostname(hostname, sizeof(hostname))) return; - domainname = strchr(hostname, '.'); - if (!domainname) return; - search_postfix_add(domainname); -} - -/* warning: returns malloced string */ -static char * -search_make_new(const struct search_state *const state, int n, const char *const base_name) { - const int base_len = strlen(base_name); - const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1; - struct search_domain *dom; - - for (dom = state->head; dom; dom = dom->next) { - if (!n--) { - /* this is the postfix we want */ - /* the actual postfix string is kept at the end of the structure */ - const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain); - const int postfix_len = dom->len; - char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1); - if (!newname) return NULL; - memcpy(newname, base_name, base_len); - if (need_to_append_dot) newname[base_len] = '.'; - memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len); - newname[base_len + need_to_append_dot + postfix_len] = 0; - return newname; - } - } - - /* we ran off the end of the list and still didn't find the requested string */ - abort(); - return NULL; /* unreachable; stops warnings in some compilers. */ -} - -static int -search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) { - assert(type == TYPE_A || type == TYPE_AAAA); - if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) && - global_search_state && - global_search_state->num_domains) { - /* we have some domains to search */ - struct request *req; - if (string_num_dots(name) >= global_search_state->ndots) { - req = request_new(type, name, flags, user_callback, user_arg); - if (!req) return 1; - req->search_index = -1; - } else { - char *const new_name = search_make_new(global_search_state, 0, name); - if (!new_name) return 1; - req = request_new(type, new_name, flags, user_callback, user_arg); - free(new_name); - if (!req) return 1; - req->search_index = 0; - } - req->search_origname = strdup(name); - req->search_state = global_search_state; - req->search_flags = flags; - global_search_state->refcount++; - request_submit(req); - return 0; - } else { - struct request *const req = request_new(type, name, flags, user_callback, user_arg); - if (!req) return 1; - request_submit(req); - return 0; - } -} - -/* this is called when a request has failed to find a name. We need to check */ -/* if it is part of a search and, if so, try the next name in the list */ -/* returns: */ -/* 0 another request has been submitted */ -/* 1 no more requests needed */ -static int -search_try_next(struct request *const req) { - if (req->search_state) { - /* it is part of a search */ - char *new_name; - struct request *newreq; - req->search_index++; - if (req->search_index >= req->search_state->num_domains) { - /* no more postfixes to try, however we may need to try */ - /* this name without a postfix */ - if (string_num_dots(req->search_origname) < req->search_state->ndots) { - /* yep, we need to try it raw */ - struct request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer); - log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname); - if (newreq) { - request_submit(newreq); - return 0; - } - } - return 1; - } - - new_name = search_make_new(req->search_state, req->search_index, req->search_origname); - if (!new_name) return 1; - log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index); - newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer); - free(new_name); - if (!newreq) return 1; - newreq->search_origname = req->search_origname; - req->search_origname = NULL; - newreq->search_state = req->search_state; - newreq->search_flags = req->search_flags; - newreq->search_index = req->search_index; - newreq->search_state->refcount++; - request_submit(newreq); - return 0; - } - return 1; -} - -static void -search_request_finished(struct request *const req) { - if (req->search_state) { - search_state_decref(req->search_state); - req->search_state = NULL; - } - if (req->search_origname) { - free(req->search_origname); - req->search_origname = NULL; - } -} - -/*/////////////////////////////////////////////////////////////////// */ -/* Parsing resolv.conf files */ - -static void -evdns_resolv_set_defaults(int flags) { - /* if the file isn't found then we assume a local resolver */ - if (flags & DNS_OPTION_SEARCH) search_set_from_hostname(); - if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1"); -} - -#ifndef HAVE_STRTOK_R -static char * -strtok_r(char *s, const char *delim, char **state) { - return strtok(s, delim); -} -#endif - -/* helper version of atoi which returns -1 on error */ -static int -strtoint(const char *const str) { - char *endptr; - const int r = strtol(str, &endptr, 10); - if (*endptr) return -1; - return r; -} - -/* helper version of atoi that returns -1 on error and clips to bounds. */ -static int -strtoint_clipped(const char *const str, int min, int max) -{ - int r = strtoint(str); - if (r == -1) - return r; - else if (r<min) - return min; - else if (r>max) - return max; - else - return r; -} - -/* exported function */ -int -evdns_set_option(const char *option, const char *val, int flags) -{ - if (!strncmp(option, "ndots:", 6)) { - const int ndots = strtoint(val); - if (ndots == -1) return -1; - if (!(flags & DNS_OPTION_SEARCH)) return 0; - log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots); - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return -1; - global_search_state->ndots = ndots; - } else if (!strncmp(option, "timeout:", 8)) { - const int timeout = strtoint(val); - if (timeout == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout); - global_timeout.tv_sec = timeout; - } else if (!strncmp(option, "max-timeouts:", 12)) { - const int maxtimeout = strtoint_clipped(val, 1, 255); - if (maxtimeout == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d", - maxtimeout); - global_max_nameserver_timeout = maxtimeout; - } else if (!strncmp(option, "max-inflight:", 13)) { - const int maxinflight = strtoint_clipped(val, 1, 65000); - if (maxinflight == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", - maxinflight); - global_max_requests_inflight = maxinflight; - } else if (!strncmp(option, "attempts:", 9)) { - int retries = strtoint(val); - if (retries == -1) return -1; - if (retries > 255) retries = 255; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries); - global_max_retransmits = retries; - } - return 0; -} - -static void -resolv_conf_parse_line(char *const start, int flags) { - char *strtok_state; - static const char *const delims = " \t"; -#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state) - - char *const first_token = strtok_r(start, delims, &strtok_state); - if (!first_token) return; - - if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) { - const char *const nameserver = NEXT_TOKEN; - struct in_addr ina; - - if (inet_aton(nameserver, &ina)) { - /* address is valid */ - evdns_nameserver_add(ina.s_addr); - } - } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) { - const char *const domain = NEXT_TOKEN; - if (domain) { - search_postfix_clear(); - search_postfix_add(domain); - } - } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) { - const char *domain; - search_postfix_clear(); - - while ((domain = NEXT_TOKEN)) { - search_postfix_add(domain); - } - search_reverse(); - } else if (!strcmp(first_token, "options")) { - const char *option; - while ((option = NEXT_TOKEN)) { - const char *val = strchr(option, ':'); - evdns_set_option(option, val ? val+1 : "", flags); - } - } -#undef NEXT_TOKEN -} - -/* exported function */ -/* returns: */ -/* 0 no errors */ -/* 1 failed to open file */ -/* 2 failed to stat file */ -/* 3 file too large */ -/* 4 out of memory */ -/* 5 short read from file */ -int -evdns_resolv_conf_parse(int flags, const char *const filename) { - struct stat st; - int fd, n, r; - u8 *resolv; - char *start; - int err = 0; - - log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); - - fd = open(filename, O_RDONLY); - if (fd < 0) { - evdns_resolv_set_defaults(flags); - return 1; - } - - if (fstat(fd, &st)) { err = 2; goto out1; } - if (!st.st_size) { - evdns_resolv_set_defaults(flags); - err = (flags & DNS_OPTION_NAMESERVERS) ? 6 : 0; - goto out1; - } - if (st.st_size > 65535) { err = 3; goto out1; } /* no resolv.conf should be any bigger */ - - resolv = (u8 *) malloc((size_t)st.st_size + 1); - if (!resolv) { err = 4; goto out1; } - - n = 0; - while ((r = read(fd, resolv+n, (size_t)st.st_size-n)) > 0) { - n += r; - if (n == st.st_size) - break; - assert(n < st.st_size); - } - if (r < 0) { err = 5; goto out2; } - resolv[n] = 0; /* we malloced an extra byte; this should be fine. */ - - start = (char *) resolv; - for (;;) { - char *const newline = strchr(start, '\n'); - if (!newline) { - resolv_conf_parse_line(start, flags); - break; - } else { - *newline = 0; - resolv_conf_parse_line(start, flags); - start = newline + 1; - } - } - - if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) { - /* no nameservers were configured. */ - evdns_nameserver_ip_add("127.0.0.1"); - err = 6; - } - if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) { - search_set_from_hostname(); - } - -out2: - free(resolv); -out1: - close(fd); - return err; -} - -#ifdef WIN32 -/* Add multiple nameservers from a space-or-comma-separated list. */ -static int -evdns_nameserver_ip_add_line(const char *ips) { - const char *addr; - char *buf; - int r; - while (*ips) { - while (ISSPACE(*ips) || *ips == ',' || *ips == '\t') - ++ips; - addr = ips; - while (ISDIGIT(*ips) || *ips == '.' || *ips == ':') - ++ips; - buf = malloc(ips-addr+1); - if (!buf) return 4; - memcpy(buf, addr, ips-addr); - buf[ips-addr] = '\0'; - r = evdns_nameserver_ip_add(buf); - free(buf); - if (r) return r; - } - return 0; -} - -typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*); - -/* Use the windows GetNetworkParams interface in iphlpapi.dll to */ -/* figure out what our nameservers are. */ -static int -load_nameservers_with_getnetworkparams(void) -{ - /* Based on MSDN examples and inspection of c-ares code. */ - FIXED_INFO *fixed; - HMODULE handle = 0; - ULONG size = sizeof(FIXED_INFO); - void *buf = NULL; - int status = 0, r, added_any; - IP_ADDR_STRING *ns; - GetNetworkParams_fn_t fn; - - if (!(handle = LoadLibrary("iphlpapi.dll"))) { - log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll"); - status = -1; - goto done; - } - if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { - log(EVDNS_LOG_WARN, "Could not get address of function."); - status = -1; - goto done; - } - - buf = malloc(size); - if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) { - status = -1; - goto done; - } - if (r != ERROR_SUCCESS) { - free(buf); - buf = malloc(size); - if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG, "fn() failed."); - status = -1; - goto done; - } - } - - assert(fixed); - added_any = 0; - ns = &(fixed->DnsServerList); - while (ns) { - r = evdns_nameserver_ip_add_line(ns->IpAddress.String); - if (r) { - log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d", - (ns->IpAddress.String),(int)GetLastError()); - status = r; - goto done; - } else { - log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String); - } - - added_any++; - ns = ns->Next; - } - - if (!added_any) { - log(EVDNS_LOG_DEBUG, "No nameservers added."); - status = -1; - } - - done: - if (buf) - free(buf); - if (handle) - FreeLibrary(handle); - return status; -} - -static int -config_nameserver_from_reg_key(HKEY key, const char *subkey) -{ - char *buf; - DWORD bufsz = 0, type = 0; - int status = 0; - - if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz) - != ERROR_MORE_DATA) - return -1; - if (!(buf = malloc(bufsz))) - return -1; - - if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz) - == ERROR_SUCCESS && bufsz > 1) { - status = evdns_nameserver_ip_add_line(buf); - } - - free(buf); - return status; -} - -#define SERVICES_KEY "System\\CurrentControlSet\\Services\\" -#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP" -#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters" - -static int -load_nameservers_from_registry(void) -{ - int found = 0; - int r; -#define TRY(k, name) \ - if (!found && config_nameserver_from_reg_key(k,name) == 0) { \ - log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \ - found = 1; \ - } else if (!found) { \ - log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \ - #k,#name); \ - } - - if (((int)GetVersion()) > 0) { /* NT */ - HKEY nt_key = 0, interfaces_key = 0; - - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, - KEY_READ, &nt_key) != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError()); - return -1; - } - r = RegOpenKeyEx(nt_key, "Interfaces", 0, - KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, - &interfaces_key); - if (r != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError()); - return -1; - } - TRY(nt_key, "NameServer"); - TRY(nt_key, "DhcpNameServer"); - TRY(interfaces_key, "NameServer"); - TRY(interfaces_key, "DhcpNameServer"); - RegCloseKey(interfaces_key); - RegCloseKey(nt_key); - } else { - HKEY win_key = 0; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, - KEY_READ, &win_key) != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError()); - return -1; - } - TRY(win_key, "NameServer"); - RegCloseKey(win_key); - } - - if (found == 0) { - log(EVDNS_LOG_WARN,"Didn't find any nameservers."); - } - - return found ? 0 : -1; -#undef TRY -} - -int -evdns_config_windows_nameservers(void) -{ - if (load_nameservers_with_getnetworkparams() == 0) - return 0; - return load_nameservers_from_registry(); -} -#endif - -int -evdns_init(void) -{ - int res = 0; -#ifdef WIN32 - evdns_config_windows_nameservers(); -#else - res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); -#endif - - return (res); -} - -const char * -evdns_err_to_string(int err) -{ - switch (err) { - case DNS_ERR_NONE: return "no error"; - case DNS_ERR_FORMAT: return "misformatted query"; - case DNS_ERR_SERVERFAILED: return "server failed"; - case DNS_ERR_NOTEXIST: return "name does not exist"; - case DNS_ERR_NOTIMPL: return "query not implemented"; - case DNS_ERR_REFUSED: return "refused"; - - case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed"; - case DNS_ERR_UNKNOWN: return "unknown"; - case DNS_ERR_TIMEOUT: return "request timed out"; - case DNS_ERR_SHUTDOWN: return "dns subsystem shut down"; - default: return "[Unknown error code]"; - } -} - -void -evdns_shutdown(int fail_requests) -{ - struct nameserver *server, *server_next; - struct search_domain *dom, *dom_next; - - while (req_head) { - if (fail_requests) - reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_head, &req_head); - } - while (req_waiting_head) { - if (fail_requests) - reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_waiting_head, &req_waiting_head); - } - global_requests_inflight = global_requests_waiting = 0; - - for (server = server_head; server; server = server_next) { - server_next = server->next; - if (server->socket >= 0) - CLOSE_SOCKET(server->socket); - (void) event_del(&server->event); - if (server->state == 0) - (void) event_del(&server->timeout_event); - free(server); - if (server_next == server_head) - break; - } - server_head = NULL; - global_good_nameservers = 0; - - if (global_search_state) { - for (dom = global_search_state->head; dom; dom = dom_next) { - dom_next = dom->next; - free(dom); - } - free(global_search_state); - global_search_state = NULL; - } - evdns_log_fn = NULL; -} - -#ifdef EVDNS_MAIN -void -main_callback(int result, char type, int count, int ttl, - void *addrs, void *orig) { - char *n = (char*)orig; - int i; - for (i = 0; i < count; ++i) { - if (type == DNS_IPv4_A) { - printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i])); - } else if (type == DNS_PTR) { - printf("%s: %s\n", n, ((char**)addrs)[i]); - } - } - if (!count) { - printf("%s: No answer (%d)\n", n, result); - } - fflush(stdout); -} -void -evdns_server_callback(struct evdns_server_request *req, void *data) -{ - int i, r; - (void)data; - /* dummy; give 192.168.11.11 as an answer for all A questions, - * give foo.bar.example.com as an answer for all PTR questions. */ - for (i = 0; i < req->nquestions; ++i) { - u32 ans = htonl(0xc0a80b0bUL); - if (req->questions[i]->type == EVDNS_TYPE_A && - req->questions[i]->class == EVDNS_CLASS_INET) { - printf(" -- replying for %s (A)\n", req->questions[i]->name); - r = evdns_server_request_add_a_reply(req, req->questions[i]->name, - 1, &ans, 10); - if (r<0) - printf("eeep, didn't work.\n"); - } else if (req->questions[i]->type == EVDNS_TYPE_PTR && - req->questions[i]->class == EVDNS_CLASS_INET) { - printf(" -- replying for %s (PTR)\n", req->questions[i]->name); - r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name, - "foo.bar.example.com", 10); - } else { - printf(" -- skipping %s [%d %d]\n", req->questions[i]->name, - req->questions[i]->type, req->questions[i]->class); - } - } - - r = evdns_request_respond(req, 0); - if (r<0) - printf("eeek, couldn't send reply.\n"); -} - -void -logfn(int is_warn, const char *msg) { - (void) is_warn; - fprintf(stderr, "%s\n", msg); -} -int -main(int c, char **v) { - int idx; - int reverse = 0, verbose = 1, servertest = 0; - if (c<2) { - fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]); - fprintf(stderr, "syntax: %s [-servertest]\n", v[0]); - return 1; - } - idx = 1; - while (idx < c && v[idx][0] == '-') { - if (!strcmp(v[idx], "-x")) - reverse = 1; - else if (!strcmp(v[idx], "-v")) - verbose = 1; - else if (!strcmp(v[idx], "-servertest")) - servertest = 1; - else - fprintf(stderr, "Unknown option %s\n", v[idx]); - ++idx; - } - event_init(); - if (verbose) - evdns_set_log_fn(logfn); - evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf"); - if (servertest) { - int sock; - struct sockaddr_in my_addr; - sock = socket(PF_INET, SOCK_DGRAM, 0); - fcntl(sock, F_SETFL, O_NONBLOCK); - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons(10053); - my_addr.sin_addr.s_addr = INADDR_ANY; - if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) { - perror("bind"); - exit(1); - } - evdns_add_server_port(sock, 0, evdns_server_callback, NULL); - } - for (; idx < c; ++idx) { - if (reverse) { - struct in_addr addr; - if (!inet_aton(v[idx], &addr)) { - fprintf(stderr, "Skipping non-IP %s\n", v[idx]); - continue; - } - fprintf(stderr, "resolving %s...\n",v[idx]); - evdns_resolve_reverse(&addr, 0, main_callback, v[idx]); - } else { - fprintf(stderr, "resolving (fwd) %s...\n",v[idx]); - evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]); - } - } - fflush(stdout); - event_dispatch(); - return 0; -} -#endif diff --git a/lib/libevent/evdns.h b/lib/libevent/evdns.h deleted file mode 100644 index 83f09dddd02..00000000000 --- a/lib/libevent/evdns.h +++ /dev/null @@ -1,369 +0,0 @@ -/* $OpenBSD: evdns.h,v 1.3 2008/05/02 15:55:58 brad 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 - -/* - * Structures and functions used to implement a DNS server. - */ - -struct evdns_server_request { - int flags; - int nquestions; - struct evdns_server_question **questions; -}; -struct evdns_server_question { - int type; - int class; - char name[1]; -}; -typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); -#define EVDNS_ANSWER_SECTION 0 -#define EVDNS_AUTHORITY_SECTION 1 -#define EVDNS_ADDITIONAL_SECTION 2 - -#define EVDNS_TYPE_A 1 -#define EVDNS_TYPE_NS 2 -#define EVDNS_TYPE_CNAME 5 -#define EVDNS_TYPE_SOA 6 -#define EVDNS_TYPE_PTR 12 -#define EVDNS_TYPE_MX 15 -#define EVDNS_TYPE_TXT 16 -#define EVDNS_TYPE_AAAA 28 - -#define EVDNS_QTYPE_AXFR 252 -#define EVDNS_QTYPE_ALL 255 - -#define EVDNS_CLASS_INET 1 - -struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data); -void evdns_close_server_port(struct evdns_server_port *port); - -int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data); -int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); -int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); -int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); -int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); - -int evdns_server_request_respond(struct evdns_server_request *req, int err); -int evdns_server_request_drop(struct evdns_server_request *req); -struct sockaddr; -int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len); - -#ifdef __cplusplus -} -#endif - -#endif /* !EVENTDNS_H */ diff --git a/lib/libevent/event-config.h b/lib/libevent/event-config.h new file mode 100644 index 00000000000..9fee7ce9a17 --- /dev/null +++ b/lib/libevent/event-config.h @@ -0,0 +1,269 @@ +/* $OpenBSD: event-config.h,v 1.1 2010/04/21 20:02:40 nicm Exp $ */ + +#ifndef _EVENT_CONFIG_H_ +#define _EVENT_CONFIG_H_ + +/* Define if clock_gettime is available in libc */ +#define _EVENT_DNS_USE_CPU_CLOCK_FOR_ID 1 + +/* Define is no secure id variant is available */ +/* #undef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#define _EVENT_HAVE_CLOCK_GETTIME 1 + +/* Define if /dev/poll is available */ +/* #undef _EVENT_HAVE_DEVPOLL */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define _EVENT_HAVE_DLFCN_H 1 + +/* Define if your system supports the epoll system calls */ +/* #undef _EVENT_HAVE_EPOLL */ + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef _EVENT_HAVE_EPOLL_CTL */ + +/* Define if your system supports event ports */ +/* #undef _EVENT_HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +#define _EVENT_HAVE_FCNTL 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define _EVENT_HAVE_FCNTL_H 1 + +/* Define to 1 if the system has the type `fd_mask'. */ +#define _EVENT_HAVE_FD_MASK 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define _EVENT_HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getegid' function. */ +#define _EVENT_HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define _EVENT_HAVE_GETEUID 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define _EVENT_HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define _EVENT_HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define _EVENT_HAVE_INET_NTOP 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define _EVENT_HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `issetugid' function. */ +#define _EVENT_HAVE_ISSETUGID 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define _EVENT_HAVE_KQUEUE 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +/* #undef _EVENT_HAVE_LIBNSL */ + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +/* #undef _EVENT_HAVE_LIBRESOLV */ + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef _EVENT_HAVE_LIBRT */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef _EVENT_HAVE_LIBSOCKET */ + +/* Define to 1 if you have the <memory.h> header file. */ +#define _EVENT_HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <netinet/in6.h> header file. */ +/* #undef _EVENT_HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the `poll' function. */ +#define _EVENT_HAVE_POLL 1 + +/* Define to 1 if you have the <poll.h> header file. */ +#define _EVENT_HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef _EVENT_HAVE_PORT_CREATE */ + +/* Define to 1 if you have the <port.h> header file. */ +/* #undef _EVENT_HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +#define _EVENT_HAVE_SELECT 1 + +/* Define if F_SETFD is defined in <fcntl.h> */ +#define _EVENT_HAVE_SETFD 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define _EVENT_HAVE_SIGACTION 1 + +/* Define to 1 if you have the `signal' function. */ +#define _EVENT_HAVE_SIGNAL 1 + +/* Define to 1 if you have the <signal.h> header file. */ +#define _EVENT_HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define _EVENT_HAVE_STDARG_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define _EVENT_HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define _EVENT_HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define _EVENT_HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define _EVENT_HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define _EVENT_HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strsep' function. */ +#define _EVENT_HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtok_r' function. */ +#define _EVENT_HAVE_STRTOK_R 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define _EVENT_HAVE_STRTOLL 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define _EVENT_HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if you have the <sys/devpoll.h> header file. */ +/* #undef _EVENT_HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the <sys/epoll.h> header file. */ +/* #undef _EVENT_HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the <sys/event.h> header file. */ +#define _EVENT_HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define _EVENT_HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define _EVENT_HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/queue.h> header file. */ +#define _EVENT_HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the <sys/select.h> header file. */ +#define _EVENT_HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define _EVENT_HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define _EVENT_HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define _EVENT_HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define _EVENT_HAVE_SYS_TYPES_H 1 + +/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */ +#define _EVENT_HAVE_TAILQFOREACH 1 + +/* Define if timeradd is defined in <sys/time.h> */ +#define _EVENT_HAVE_TIMERADD 1 + +/* Define if timerclear is defined in <sys/time.h> */ +#define _EVENT_HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in <sys/time.h> */ +#define _EVENT_HAVE_TIMERCMP 1 + +/* Define if timerisset is defined in <sys/time.h> */ +#define _EVENT_HAVE_TIMERISSET 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define _EVENT_HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `uint32_t'. */ +#define _EVENT_HAVE_UINT32_T 1 + +/* Define to 1 if the system has the type `uint64_t'. */ +#define _EVENT_HAVE_UINT64_T 1 + +/* Define to 1 if the system has the type `uint8_t'. */ +#define _EVENT_HAVE_UINT8_T 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define _EVENT_HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define _EVENT_HAVE_VASPRINTF 1 + +/* Define if kqueue works correctly with pipes */ +#define _EVENT_HAVE_WORKING_KQUEUE 1 + +/* Name of package */ +#define _EVENT_PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define _EVENT_PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define _EVENT_PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define _EVENT_PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define _EVENT_PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define _EVENT_PACKAGE_VERSION "" + +/* The size of `int', as computed by sizeof. */ +#define _EVENT_SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define _EVENT_SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define _EVENT_SIZEOF_LONG_LONG 8 + +/* The size of `short', as computed by sizeof. */ +#define _EVENT_SIZEOF_SHORT 2 + +/* Define to 1 if you have the ANSI C header files. */ +#define _EVENT_STDC_HEADERS 1 + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#define _EVENT_TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define _EVENT_VERSION "1.4.13-stable" + +/* Define to appropriate substitue if compiler doesnt have __func__ */ +/* #undef _EVENT___func__ */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef _EVENT_const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef _EVENT___cplusplus +/* #undef _EVENT_inline */ +#endif + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef _EVENT_pid_t */ + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef _EVENT_size_t */ + +/* Define to unsigned int if you dont have it */ +/* #undef _EVENT_socklen_t */ +#endif diff --git a/lib/libevent/event-internal.h b/lib/libevent/event-internal.h index 0a44a5ea68e..9dc84353d7f 100644 --- a/lib/libevent/event-internal.h +++ b/lib/libevent/event-internal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: event-internal.h,v 1.5 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: event-internal.h,v 1.6 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu> @@ -33,8 +33,20 @@ extern "C" { #endif +#include "min_heap.h" #include "evsignal.h" +struct eventop { + const char *name; + void *(*init)(struct event_base *); + int (*add)(void *, struct event *); + int (*del)(void *, struct event *); + int (*dispatch)(struct event_base *, void *, struct timeval *); + void (*dealloc)(struct event_base *, void *); + /* set if we need to reinitialize the event base */ + int need_reinit; +}; + struct event_base { const struct eventop *evsel; void *evbase; @@ -42,6 +54,7 @@ struct event_base { int event_count_active; /* counts number of active events */ int event_gotterm; /* Set to terminate loop */ + int event_break; /* Set to terminate loop immediately */ /* active event management */ struct event_list **activequeues; @@ -53,9 +66,35 @@ struct event_base { struct event_list eventqueue; struct timeval event_tv; - RB_HEAD(event_tree, event) timetree; + struct min_heap timeheap; + + struct timeval tv_cache; }; +/* Internal use only: Functions that might be missing from <sys/queue.h> */ +#ifndef HAVE_TAILQFOREACH +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) +#endif /* TAILQ_FOREACH */ + +int _evsignal_set_handler(struct event_base *base, int evsignal, + void (*fn)(int)); +int _evsignal_restore_handler(struct event_base *base, int evsignal); + +/* defined in evutil.c */ +const char *evutil_getenv(const char *varname); + #ifdef __cplusplus } #endif diff --git a/lib/libevent/event.3 b/lib/libevent/event.3 index 45a555cfd67..85f89765e62 100644 --- a/lib/libevent/event.3 +++ b/lib/libevent/event.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: event.3,v 1.39 2008/05/02 18:26:42 brad Exp $ +.\" $OpenBSD: event.3,v 1.40 2010/04/21 20:02:40 nicm Exp $ .\" .\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org> .\" All rights reserved. @@ -23,7 +23,7 @@ .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: May 2 2008 $ +.Dd $Mdocdate: April 21 2010 $ .Dt EVENT 3 .Os .Sh NAME @@ -31,25 +31,22 @@ .Nm event_dispatch , .Nm event_loop , .Nm event_loopexit , +.Nm event_loopbreak , .Nm event_set , -.\".Nm event_set_log_callback , .Nm event_base_dispatch , .Nm event_base_loop , .Nm event_base_loopexit , +.Nm event_base_loopbreak , .Nm event_base_set , .Nm event_base_free , .Nm event_add , .Nm event_del , .Nm event_once , .Nm event_base_once , -.\".Nm event_active , .Nm event_pending , .Nm event_initialized , .Nm event_priority_init , .Nm event_priority_set , -.\".Nm event_base_priority_init , -.\".Nm event_get_version , -.\".Nm event_get_method , .Nm evtimer_set , .Nm evtimer_add , .Nm evtimer_del , @@ -69,66 +66,17 @@ .Nm bufferevent_disable , .Nm bufferevent_settimeout , .Nm bufferevent_base_set , -.\".Nm bufferevent_priority_set , .Nm evbuffer_new , .Nm evbuffer_free , .Nm evbuffer_add , .Nm evbuffer_add_buffer , .Nm evbuffer_add_printf , .Nm evbuffer_add_vprintf , -.\".Nm evbuffer_expand , -.\".Nm evbuffer_remove , -.\".Nm evbuffer_setcb , .Nm evbuffer_drain , .Nm evbuffer_write , .Nm evbuffer_read , .Nm evbuffer_find , .Nm evbuffer_readline -.\".Nm evhttp_start , -.\".Nm evhttp_free -.\".Nm evtag_init , -.\".Nm evtag_marshal , -.\".Nm encode_int , -.\".Nm evtag_marshal_int , -.\".Nm evtag_marshal_string , -.\".Nm evtag_marshal_timeval , -.\".Nm evtag_test , -.\".Nm evtag_unmarshal , -.\".Nm evtag_peek , -.\".Nm evtag_peek_length , -.\".Nm evtag_payload_length , -.\".Nm evtag_consume , -.\".Nm evtag_unmarshal_int , -.\".Nm evtag_unmarshal_fixed , -.\".Nm evtag_unmarshal_string , -.\".Nm evtag_unmarshal_timeval , -.\".Nm evhttp_start , -.\".Nm evhttp_free , -.\".Nm evhttp_set_cb , -.\".Nm evhttp_set_gencb , -.\".Nm evhttp_set_timeout , -.\".Nm evhttp_send_error , -.\".Nm evhttp_send_reply , -.\".Nm evhttp_send_reply_start , -.\".Nm evhttp_send_reply_chunk , -.\".Nm evhttp_send_reply_end , -.\".Nm evhttp_request_new , -.\".Nm evhttp_request_set_chunked_cb , -.\".Nm evhttp_connection_new , -.\".Nm evhttp_connection_free , -.\".Nm evhttp_connection_set_timeout , -.\".Nm evhttp_connection_set_retries , -.\".Nm evhttp_connection_set_closecb , -.\".Nm evhttp_connection_get_peer , -.\".Nm evhttp_make_request , -.\".Nm evhttp_request_uri , -.\".Nm evhttp_remove_header , -.\".Nm evhttp_add_header , -.\".Nm evhttp_clear_headers , -.\".Nm evhttp_encode_uri , -.\".Nm evhttp_decode_uri , -.\".Nm evhttp_parse_query , -.\".Nm evhttp_htmlescape , .Nd execute a function when a specific event occurs .Sh SYNOPSIS .Fd #include <sys/time.h> @@ -141,6 +89,8 @@ .Fn "event_loop" "int flags" .Ft int .Fn "event_loopexit" "struct timeval *tv" +.Ft int +.Fn "event_loopbreak" "void" .Ft void .Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" .Ft int @@ -150,6 +100,8 @@ .Ft int .Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv" .Ft int +.Fn "event_base_loopbreak" "struct event_base *base" +.Ft int .Fn "event_base_set" "struct event_base *base" "struct event *" .Ft void .Fn "event_base_free" "struct event_base *base" @@ -229,14 +181,6 @@ .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 -.Fa event_gotsig ; .Sh DESCRIPTION The .Nm event @@ -254,51 +198,6 @@ In order to process events, an application needs to call This function only returns on error, and should replace the event core of the application program. .Pp -In order to avoid races in signal handlers, the -.Nm event -API provides two variables: -.Va event_sigcb -and -.Va event_gotsig . -A signal handler -sets -.Va event_gotsig -to indicate that a signal has been received. -The application sets -.Va event_sigcb -to a callback function. -After the signal handler sets -.Va event_gotsig , -.Nm event_dispatch -will execute the callback function to process received signals. -The callback returns 1 when no events are registered any more. -It can return \-1 to indicate an error to the -.Nm event -library, causing -.Fn event_dispatch -to terminate with -.Va errno -set to -.Er EINTR . -.Pp -The -.Nm event_loop -function provides an interface for single pass execution of pending -events. -The flags -.Va EVLOOP_ONCE -and -.Va EVLOOP_NONBLOCK -are recognized. -The -.Nm event_loopexit -function allows the loop to be terminated after some amount of time -has passed. -The parameter indicates the time after which the loop should terminate. -.Pp -It is the responsibility of the caller to provide these functions with -pre-allocated event structures. -.Pp The function .Fn event_set prepares the event structure @@ -337,6 +236,11 @@ the type of event which will be either .Va EV_READ , or .Va EV_WRITE . +Additionally, an event which has registered interest in more than one of the +preceding events, via bitwise-OR to +.Fn event_set , +can provide its callback function with a bitwise-OR of more than one triggered +event. The additional flag .Va EV_PERSIST makes an @@ -402,6 +306,32 @@ will cancel the event in the argument If the event has already executed or has never been added the call will have no effect. .Pp +The functions +.Fn evtimer_set , +.Fn evtimer_add , +.Fn evtimer_del , +.Fn evtimer_initialized , +and +.Fn evtimer_pending +are abbreviations for common situations where only a timeout is required. +The file descriptor passed will be \-1, and the event type will be +.Va EV_TIMEOUT . +.Pp +The functions +.Fn signal_set , +.Fn signal_add , +.Fn signal_del , +.Fn signal_initialized , +and +.Fn signal_pending +are abbreviations. +The event type will be a persistent +.Va EV_SIGNAL . +That means +.Fn signal_set +adds +.Va EV_PERSIST . +.Pp The function .Fn event_once is similar to @@ -434,45 +364,41 @@ The .Fn event_initialized macro can be used to check if an event has been initialized. .Pp -The functions -.Fn evtimer_set , -.Fn evtimer_add , -.Fn evtimer_del , -.Fn evtimer_initialized , +The +.Nm event_loop +function provides an interface for single pass execution of pending +events. +The flags +.Va EVLOOP_ONCE and -.Fn evtimer_pending -are abbreviations for common situations where only a timeout is required. -The file descriptor passed will be \-1, and the event type will be -.Va EV_TIMEOUT . +.Va EVLOOP_NONBLOCK +are recognized. +The +.Nm event_loopexit +function exits from the event loop. +The next +.Fn event_loop +iteration after the +given timer expires will complete normally (handling all queued events) then +exit without blocking for events again. +Subsequent invocations of +.Fn event_loop +will proceed normally. +The +.Nm event_loopbreak +function exits from the event loop immediately. +.Fn event_loop +will abort after the next event is completed; +.Fn event_loopbreak +is typically invoked from this event's callback. +This behavior is analogous to the "break;" statement. +Subsequent invocations of +.Fn event_loop +will proceed normally. .Pp -The functions -.Fn signal_set , -.Fn signal_add , -.Fn signal_del , -.Fn signal_initialized , -and -.Fn signal_pending -are abbreviations. -The event type will be a persistent -.Va EV_SIGNAL . -That means -.Fn signal_set -adds -.Va EV_PERSIST . +It is the responsibility of the caller to provide these functions with +pre-allocated event structures. .Pp -It is possible to disable support for -.Va kqueue , poll , -or -.Va select -by setting the environment variables -.Va EVENT_NOKQUEUE , EVENT_NOPOLL , -or -.Va EVENT_NOSELECT , -respectively. -By setting the environment variable -.Va EVENT_SHOW_METHOD , -.Nm libevent -displays the kernel notification method that it uses. .Sh EVENT PRIORITIES By default .Nm libevent @@ -583,28 +509,20 @@ 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 ADDITIONAL NOTES +It is possible to disable support for +.Va kqueue , poll +or +.Va select +by setting the environment variable +.Va EVENT_NOKQUEUE , EVENT_NOPOLL +or +.Va EVENT_NOSELECT , +respectively. +By setting the environment variable +.Va EVENT_SHOW_METHOD , +.Nm libevent +displays the kernel notification method that it uses. .Sh RETURN VALUES Upon successful completion .Fn event_add @@ -617,7 +535,6 @@ set to indicate the error. .Xr kqueue 2 , .Xr poll 2 , .Xr select 2 , -.Xr evdns 3 , .Xr timeout 9 .Sh HISTORY The @@ -625,6 +542,9 @@ The API manpage is based on the .Xr timeout 9 manpage by Artur Grabowski. +The port of +.Nm libevent +to Windows is due to Michael A. Davis. Support for real-time signals is due to Taral. .Sh AUTHORS The diff --git a/lib/libevent/event.c b/lib/libevent/event.c index 33b8f2f29b3..133ffb5f7e7 100644 --- a/lib/libevent/event.c +++ b/lib/libevent/event.c @@ -1,4 +1,4 @@ -/* $OpenBSD: event.c,v 1.21 2009/11/12 05:44:29 deraadt Exp $ */ +/* $OpenBSD: event.c,v 1.22 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu> @@ -34,14 +34,12 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> #undef WIN32_LEAN_AND_MEAN -#include "misc.h" #endif #include <sys/types.h> -#include <sys/tree.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> -#else -#include <sys/_time.h> +#else +#include <sys/_libevent_time.h> #endif #include <sys/queue.h> #include <stdio.h> @@ -57,6 +55,7 @@ #include "event.h" #include "event-internal.h" +#include "evutil.h" #include "log.h" #ifdef HAVE_EVENT_PORTS @@ -68,9 +67,6 @@ extern const struct eventop selectops; #ifdef HAVE_POLL extern const struct eventop pollops; #endif -#ifdef HAVE_RTSIG -extern const struct eventop rtsigops; -#endif #ifdef HAVE_EPOLL extern const struct eventop epollops; #endif @@ -85,7 +81,7 @@ extern const struct eventop win32ops; #endif /* In order of preference */ -const struct eventop *eventops[] = { +static const struct eventop *eventops[] = { #ifdef HAVE_EVENT_PORTS &evportops, #endif @@ -98,9 +94,6 @@ const struct eventop *eventops[] = { #ifdef HAVE_DEVPOLL &devpollops, #endif -#ifdef HAVE_RTSIG - &rtsigops, -#endif #ifdef HAVE_POLL &pollops, #endif @@ -118,10 +111,6 @@ struct event_base *current_base = NULL; extern struct event_base *evsignal_base; static int use_monotonic; -/* Handle signals - This is a deprecated interface */ -int (*event_sigcb)(void); /* Signal callback when gotsig is set */ -volatile sig_atomic_t event_gotsig; /* Set in signal handler */ - /* Prototypes */ static void event_queue_insert(struct event_base *, struct event *, int); static void event_queue_remove(struct event_base *, struct event *, int); @@ -133,20 +122,6 @@ static int timeout_next(struct event_base *, struct timeval **); static void timeout_process(struct event_base *); static void timeout_correct(struct event_base *, struct timeval *); -static int -compare(struct event *a, struct event *b) -{ - if (timercmp(&a->ev_timeout, &b->ev_timeout, <)) - return (-1); - else if (timercmp(&a->ev_timeout, &b->ev_timeout, >)) - return (1); - if (a < b) - return (-1); - else if (a > b) - return (1); - return (0); -} - static void detect_monotonic(void) { @@ -159,12 +134,17 @@ detect_monotonic(void) } static int -gettime(struct timeval *tp) +gettime(struct event_base *base, struct timeval *tp) { -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; + if (base->tv_cache.tv_sec) { + *tp = base->tv_cache; + return (0); + } +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) if (use_monotonic) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return (-1); @@ -174,35 +154,37 @@ gettime(struct timeval *tp) } #endif - return (gettimeofday(tp, NULL)); + return (evutil_gettimeofday(tp, NULL)); } -RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare); +struct event_base * +event_init(void) +{ + struct event_base *base = event_base_new(); -RB_GENERATE(event_tree, event, ev_timeout_node, compare); + if (base != NULL) + current_base = base; + return (base); +} struct event_base * -event_init(void) +event_base_new(void) { int i; struct event_base *base; if ((base = calloc(1, sizeof(struct event_base))) == NULL) - event_err(1, "%s: calloc"); - - event_sigcb = NULL; - event_gotsig = 0; + event_err(1, "%s: calloc", __func__); detect_monotonic(); - gettime(&base->event_tv); - - RB_INIT(&base->timetree); + gettime(base, &base->event_tv); + + min_heap_ctor(&base->timeheap); TAILQ_INIT(&base->eventqueue); - TAILQ_INIT(&base->sig.signalqueue); base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; - + base->evbase = NULL; for (i = 0; eventops[i] && !base->evbase; i++) { base->evsel = eventops[i]; @@ -213,34 +195,66 @@ event_init(void) if (base->evbase == NULL) event_errx(1, "%s: no event mechanism available", __func__); - if (getenv("EVENT_SHOW_METHOD")) + if (evutil_getenv("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s\n", base->evsel->name); /* allocate a single active event queue */ event_base_priority_init(base, 1); - current_base = base; return (base); } void event_base_free(struct event_base *base) { - int i; + int i, n_deleted=0; + struct event *ev; if (base == NULL && current_base) base = current_base; - if (base == current_base) + if (base == current_base) current_base = NULL; + /* XXX(niels) - check for internal events first */ assert(base); + /* Delete all non-internal events. */ + for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) { + struct event *next = TAILQ_NEXT(ev, ev_next); + if (!(ev->ev_flags & EVLIST_INTERNAL)) { + event_del(ev); + ++n_deleted; + } + ev = next; + } + while ((ev = min_heap_top(&base->timeheap)) != NULL) { + event_del(ev); + ++n_deleted; + } + + for (i = 0; i < base->nactivequeues; ++i) { + for (ev = TAILQ_FIRST(base->activequeues[i]); ev; ) { + struct event *next = TAILQ_NEXT(ev, ev_active_next); + if (!(ev->ev_flags & EVLIST_INTERNAL)) { + event_del(ev); + ++n_deleted; + } + ev = next; + } + } + + if (n_deleted) + event_debug(("%s: %d events were still set in base", + __func__, n_deleted)); + if (base->evsel->dealloc != NULL) base->evsel->dealloc(base, base->evbase); - for (i=0; i < base->nactivequeues; ++i) + + for (i = 0; i < base->nactivequeues; ++i) assert(TAILQ_EMPTY(base->activequeues[i])); - assert(RB_EMPTY(&base->timetree)); + assert(min_heap_empty(&base->timeheap)); + min_heap_dtor(&base->timeheap); for (i = 0; i < base->nactivequeues; ++i) free(base->activequeues[i]); @@ -251,6 +265,46 @@ event_base_free(struct event_base *base) free(base); } +/* reinitialized the event base after a fork */ +int +event_reinit(struct event_base *base) +{ + const struct eventop *evsel = base->evsel; + void *evbase = base->evbase; + int res = 0; + struct event *ev; + + /* check if this event mechanism requires reinit */ + if (!evsel->need_reinit) + return (0); + + /* prevent internal delete */ + if (base->sig.ev_signal_added) { + /* we cannot call event_del here because the base has + * not been reinitialized yet. */ + event_queue_remove(base, &base->sig.ev_signal, + EVLIST_INSERTED); + if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE) + event_queue_remove(base, &base->sig.ev_signal, + EVLIST_ACTIVE); + base->sig.ev_signal_added = 0; + } + + if (base->evsel->dealloc != NULL) + base->evsel->dealloc(base, base->evbase); + evbase = base->evbase = evsel->init(base); + if (base->evbase == NULL) + event_errx(1, "%s: could not reinitialize event mechanism", + __func__); + + TAILQ_FOREACH(ev, &base->eventqueue, ev_next) { + if (evsel->add(evbase, ev) == -1) + res = -1; + } + + return (res); +} + int event_priority_init(int npriorities) { @@ -274,8 +328,8 @@ event_base_priority_init(struct event_base *base, int npriorities) /* Allocate our priority queues */ base->nactivequeues = npriorities; - base->activequeues = (struct event_list **)calloc(base->nactivequeues, - npriorities * sizeof(struct event_list *)); + base->activequeues = (struct event_list **) + calloc(base->nactivequeues, sizeof(struct event_list *)); if (base->activequeues == NULL) event_err(1, "%s: calloc", __func__); @@ -309,9 +363,6 @@ event_process_active(struct event_base *base) int i; short ncalls; - if (!base->event_count_active) - return; - for (i = 0; i < base->nactivequeues; ++i) { if (TAILQ_FIRST(base->activequeues[i]) != NULL) { activeq = base->activequeues[i]; @@ -322,8 +373,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); - + if (ev->ev_events & EV_PERSIST) + event_queue_remove(base, ev, EVLIST_ACTIVE); + else + event_del(ev); + /* Allows deletes to work */ ncalls = ev->ev_ncalls; ev->ev_pncalls = &ncalls; @@ -331,7 +385,7 @@ event_process_active(struct event_base *base) ncalls--; ev->ev_ncalls = ncalls; (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); - if (event_gotsig) + if (base->event_break) return; } } @@ -353,6 +407,13 @@ event_base_dispatch(struct event_base *event_base) return (event_base_loop(event_base, 0)); } +const char * +event_base_get_method(struct event_base *base) +{ + assert(base); + return (base->evsel->name); +} + static void event_loopexit_cb(int fd, short what, void *arg) { @@ -362,20 +423,39 @@ event_loopexit_cb(int fd, short what, void *arg) /* not thread safe */ int -event_loopexit(struct timeval *tv) +event_loopexit(const struct timeval *tv) { return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, current_base, tv)); } int -event_base_loopexit(struct event_base *event_base, struct timeval *tv) +event_base_loopexit(struct event_base *event_base, const struct timeval *tv) { return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb, event_base, tv)); } /* not thread safe */ +int +event_loopbreak(void) +{ + return (event_base_loopbreak(current_base)); +} + +int +event_base_loopbreak(struct event_base *event_base) +{ + if (event_base == NULL) + return (-1); + + event_base->event_break = 1; + return (0); +} + + + +/* not thread safe */ int event_loop(int flags) @@ -392,32 +472,22 @@ event_base_loop(struct event_base *base, int flags) struct timeval *tv_p; int res, done; -#ifndef WIN32 - if(!TAILQ_EMPTY(&base->sig.signalqueue)) + /* clear time cache */ + base->tv_cache.tv_sec = 0; + + if (base->sig.ev_signal_added) evsignal_base = base; -#endif done = 0; while (!done) { - /* Calculate the initial events that we are waiting for */ - if (evsel->recalc(base, evbase, 0) == -1) - return (-1); - /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { base->event_gotterm = 0; break; } - /* You cannot use this interface for multi-threaded apps */ - while (event_gotsig) { - event_gotsig = 0; - if (event_sigcb) { - res = (*event_sigcb)(); - if (res == -1) { - errno = EINTR; - return (-1); - } - } + if (base->event_break) { + base->event_break = 0; + break; } timeout_correct(base, &tv); @@ -430,19 +500,26 @@ event_base_loop(struct event_base *base, int flags) * if we have active events, we just poll new events * without waiting. */ - timerclear(&tv); + evutil_timerclear(&tv); } - + /* If we have no events, we just exit */ if (!event_haveevents(base)) { event_debug(("%s: no events registered.", __func__)); return (1); } + /* update last old time */ + gettime(base, &base->event_tv); + + /* clear time cache */ + base->tv_cache.tv_sec = 0; + res = evsel->dispatch(base, evbase, tv_p); if (res == -1) return (-1); + gettime(base, &base->tv_cache); timeout_process(base); @@ -454,6 +531,9 @@ event_base_loop(struct event_base *base, int flags) done = 1; } + /* clear time cache */ + base->tv_cache.tv_sec = 0; + event_debug(("%s: asked to terminate loop.", __func__)); return (0); } @@ -481,7 +561,7 @@ event_once_cb(int fd, short events, void *arg) /* not threadsafe, event scheduled once. */ int event_once(int fd, short events, - void (*callback)(int, short, void *), void *arg, struct timeval *tv) + void (*callback)(int, short, void *), void *arg, const struct timeval *tv) { return event_base_once(current_base, fd, events, callback, arg, tv); } @@ -489,7 +569,7 @@ event_once(int fd, short events, /* Schedules an event once */ int event_base_once(struct event_base *base, int fd, short events, - void (*callback)(int, short, void *), void *arg, struct timeval *tv) + void (*callback)(int, short, void *), void *arg, const struct timeval *tv) { struct event_once *eonce; struct timeval etv; @@ -507,7 +587,7 @@ event_base_once(struct event_base *base, int fd, short events, if (events == EV_TIMEOUT) { if (tv == NULL) { - timerclear(&etv); + evutil_timerclear(&etv); tv = &etv; } @@ -549,6 +629,8 @@ event_set(struct event *ev, int fd, short events, ev->ev_ncalls = 0; ev->ev_pncalls = NULL; + min_heap_elem_init(ev); + /* by default, we put new events into the middle priority */ if(current_base) ev->ev_pri = current_base->nactivequeues/2; @@ -596,34 +678,33 @@ event_pending(struct event *ev, short event, struct timeval *tv) int flags = 0; if (ev->ev_flags & EVLIST_INSERTED) - flags |= (ev->ev_events & (EV_READ|EV_WRITE)); + flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)); if (ev->ev_flags & EVLIST_ACTIVE) flags |= ev->ev_res; if (ev->ev_flags & EVLIST_TIMEOUT) flags |= EV_TIMEOUT; - if (ev->ev_flags & EVLIST_SIGNAL) - flags |= EV_SIGNAL; event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL); /* See if there is a timeout that we should report */ if (tv != NULL && (flags & event & EV_TIMEOUT)) { - gettime(&now); - timersub(&ev->ev_timeout, &now, &res); + gettime(ev->ev_base, &now); + evutil_timersub(&ev->ev_timeout, &now, &res); /* correctly remap to real time */ - gettimeofday(&now, NULL); - timeradd(&now, &res, tv); + evutil_gettimeofday(&now, NULL); + evutil_timeradd(&now, &res, tv); } return (flags & event); } int -event_add(struct event *ev, struct timeval *tv) +event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; const struct eventop *evsel = base->evsel; void *evbase = base->evbase; + int res = 0; event_debug(( "event_add: event: %p, %s%s%scall %p", @@ -635,9 +716,34 @@ event_add(struct event *ev, struct timeval *tv) assert(!(ev->ev_flags & ~EVLIST_ALL)); - if (tv != NULL) { + /* + * prepare for timeout insertion further below, if we get a + * failure on any step, we should not change any state. + */ + if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { + if (min_heap_reserve(&base->timeheap, + 1 + min_heap_size(&base->timeheap)) == -1) + return (-1); /* ENOMEM == errno */ + } + + if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && + !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { + res = evsel->add(evbase, ev); + if (res != -1) + event_queue_insert(base, ev, EVLIST_INSERTED); + } + + /* + * we should change the timout state only if the previous event + * addition succeeded. + */ + if (res != -1 && tv != NULL) { struct timeval now; + /* + * we already reserved memory above for the case where we + * are not replacing an exisiting timeout. + */ if (ev->ev_flags & EVLIST_TIMEOUT) event_queue_remove(base, ev, EVLIST_TIMEOUT); @@ -653,33 +759,21 @@ event_add(struct event *ev, struct timeval *tv) /* Abort loop */ *ev->ev_pncalls = 0; } - + event_queue_remove(base, ev, EVLIST_ACTIVE); } - gettime(&now); - timeradd(&now, tv, &ev->ev_timeout); + gettime(base, &now); + evutil_timeradd(&now, tv, &ev->ev_timeout); event_debug(( - "event_add: timeout in %d seconds, call %p", + "event_add: timeout in %ld seconds, call %p", tv->tv_sec, ev->ev_callback)); event_queue_insert(base, ev, EVLIST_TIMEOUT); } - if ((ev->ev_events & (EV_READ|EV_WRITE)) && - !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { - event_queue_insert(base, ev, EVLIST_INSERTED); - - return (evsel->add(evbase, ev)); - } else if ((ev->ev_events & EV_SIGNAL) && - !(ev->ev_flags & EVLIST_SIGNAL)) { - event_queue_insert(base, ev, EVLIST_SIGNAL); - - return (evsel->add(evbase, ev)); - } - - return (0); + return (res); } int @@ -717,9 +811,6 @@ event_del(struct event *ev) if (ev->ev_flags & EVLIST_INSERTED) { event_queue_remove(base, ev, EVLIST_INSERTED); return (evsel->del(evbase, ev)); - } else if (ev->ev_flags & EVLIST_SIGNAL) { - event_queue_remove(base, ev, EVLIST_SIGNAL); - return (evsel->del(evbase, ev)); } return (0); @@ -747,26 +838,26 @@ timeout_next(struct event_base *base, struct timeval **tv_p) struct event *ev; struct timeval *tv = *tv_p; - if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) { + if ((ev = min_heap_top(&base->timeheap)) == NULL) { /* if no time-based events are active wait for I/O */ *tv_p = NULL; return (0); } - if (gettime(&now) == -1) + if (gettime(base, &now) == -1) return (-1); - if (timercmp(&ev->ev_timeout, &now, <=)) { - timerclear(tv); + if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { + evutil_timerclear(tv); return (0); } - timersub(&ev->ev_timeout, &now, tv); + evutil_timersub(&ev->ev_timeout, &now, tv); assert(tv->tv_sec >= 0); assert(tv->tv_usec >= 0); - event_debug(("timeout_next: in %d seconds", tv->tv_sec)); + event_debug(("timeout_next: in %ld seconds", tv->tv_sec)); return (0); } @@ -779,45 +870,52 @@ timeout_next(struct event_base *base, struct timeval **tv_p) static void timeout_correct(struct event_base *base, struct timeval *tv) { - struct event *ev; + struct event **pev; + unsigned int size; struct timeval off; if (use_monotonic) return; /* Check if time is running backwards */ - gettime(tv); - if (timercmp(tv, &base->event_tv, >=)) { + gettime(base, tv); + if (evutil_timercmp(tv, &base->event_tv, >=)) { base->event_tv = *tv; return; } event_debug(("%s: time is running backwards, corrected", __func__)); - timersub(&base->event_tv, tv, &off); + evutil_timersub(&base->event_tv, tv, &off); /* * We can modify the key element of the node without destroying * the key, beause we apply it to all in the right order. */ - RB_FOREACH(ev, event_tree, &base->timetree) - timersub(&ev->ev_timeout, &off, &ev->ev_timeout); + pev = base->timeheap.p; + size = base->timeheap.n; + for (; size-- > 0; ++pev) { + struct timeval *ev_tv = &(**pev).ev_timeout; + evutil_timersub(ev_tv, &off, ev_tv); + } + /* Now remember what the new time turned out to be. */ + base->event_tv = *tv; } void timeout_process(struct event_base *base) { struct timeval now; - struct event *ev, *next; + struct event *ev; - gettime(&now); + if (min_heap_empty(&base->timeheap)) + return; - for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) { - if (timercmp(&ev->ev_timeout, &now, >)) - break; - next = RB_NEXT(event_tree, &base->timetree, ev); + gettime(base, &now); - event_queue_remove(base, ev, EVLIST_TIMEOUT); + while ((ev = min_heap_top(&base->timeheap))) { + if (evutil_timercmp(&ev->ev_timeout, &now, >)) + break; /* delete this event from the I/O queues */ event_del(ev); @@ -831,34 +929,25 @@ timeout_process(struct event_base *base) void event_queue_remove(struct event_base *base, struct event *ev, int queue) { - int docount = 1; - if (!(ev->ev_flags & queue)) event_errx(1, "%s: %p(fd %d) not on queue %x", __func__, ev, ev->ev_fd, queue); - if (ev->ev_flags & EVLIST_INTERNAL) - docount = 0; - - if (docount) + if (~ev->ev_flags & EVLIST_INTERNAL) base->event_count--; ev->ev_flags &= ~queue; switch (queue) { + case EVLIST_INSERTED: + TAILQ_REMOVE(&base->eventqueue, ev, ev_next); + break; case EVLIST_ACTIVE: - if (docount) - base->event_count_active--; + base->event_count_active--; TAILQ_REMOVE(base->activequeues[ev->ev_pri], ev, ev_active_next); break; - case EVLIST_SIGNAL: - TAILQ_REMOVE(&base->sig.signalqueue, ev, ev_signal_next); - break; case EVLIST_TIMEOUT: - RB_REMOVE(event_tree, &base->timetree, ev); - break; - case EVLIST_INSERTED: - TAILQ_REMOVE(&base->eventqueue, ev, ev_next); + min_heap_erase(&base->timeheap, ev); break; default: event_errx(1, "%s: unknown queue %x", __func__, queue); @@ -868,8 +957,6 @@ event_queue_remove(struct event_base *base, struct event *ev, int queue) void event_queue_insert(struct event_base *base, struct event *ev, int queue) { - int docount = 1; - if (ev->ev_flags & queue) { /* Double insertion is possible for active events */ if (queue & EVLIST_ACTIVE) @@ -879,31 +966,23 @@ event_queue_insert(struct event_base *base, struct event *ev, int queue) ev, ev->ev_fd, queue); } - if (ev->ev_flags & EVLIST_INTERNAL) - docount = 0; - - if (docount) + if (~ev->ev_flags & EVLIST_INTERNAL) base->event_count++; ev->ev_flags |= queue; switch (queue) { + case EVLIST_INSERTED: + TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); + break; case EVLIST_ACTIVE: - if (docount) - base->event_count_active++; + base->event_count_active++; TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], ev,ev_active_next); break; - case EVLIST_SIGNAL: - TAILQ_INSERT_TAIL(&base->sig.signalqueue, ev, ev_signal_next); - break; case EVLIST_TIMEOUT: { - struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev); - assert(tmp == NULL); + min_heap_push(&base->timeheap, ev); break; } - case EVLIST_INSERTED: - TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); - break; default: event_errx(1, "%s: unknown queue %x", __func__, queue); } @@ -914,10 +993,10 @@ event_queue_insert(struct event_base *base, struct event *ev, int queue) const char * event_get_version(void) { - return (LIBEVENT_VERSION); + return (_EVENT_VERSION); } -/* +/* * No thread-safe interface needed - the information should be the same * for all threads. */ diff --git a/lib/libevent/event.h b/lib/libevent/event.h index 24125d40b98..9e1e233fecd 100644 --- a/lib/libevent/event.h +++ b/lib/libevent/event.h @@ -1,7 +1,7 @@ -/* $OpenBSD: event.h,v 1.20 2008/05/02 18:26:42 brad Exp $ */ +/* $OpenBSD: event.h,v 1.21 2010/04/21 20:02:40 nicm Exp $ */ /* - * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,14 +29,153 @@ #ifndef _EVENT_H_ #define _EVENT_H_ +/** @mainpage + + @section intro Introduction + + libevent is an event notification library for developing scalable network + servers. The libevent API provides a mechanism to execute a callback + function when a specific event occurs on a file descriptor or after a + timeout has been reached. Furthermore, libevent also support callbacks due + to signals or regular timeouts. + + libevent is meant to replace the event loop found in event driven network + servers. An application just needs to call event_dispatch() and then add or + remove events dynamically without having to change the event loop. + + Currently, libevent supports /dev/poll, kqueue(2), select(2), poll(2) and + epoll(4). It also has experimental support for real-time signals. The + internal event mechanism is completely independent of the exposed event API, + and a simple update of libevent can provide new functionality without having + to redesign the applications. As a result, Libevent allows for portable + application development and provides the most scalable event notification + mechanism available on an operating system. Libevent can also be used for + multi-threaded aplications; see Steven Grimm's explanation. Libevent should + compile on Linux, *BSD, Mac OS X, Solaris and Windows. + + @section usage Standard usage + + Every program that uses libevent must include the <event.h> header, and pass + the -levent flag to the linker. Before using any of the functions in the + library, you must call event_init() or event_base_new() to perform one-time + initialization of the libevent library. + + @section event Event notification + + For each file descriptor that you wish to monitor, you must declare an event + structure and call event_set() to initialize the members of the structure. + To enable notification, you add the structure to the list of monitored + events by calling event_add(). The event structure must remain allocated as + long as it is active, so it should be allocated on the heap. Finally, you + call event_dispatch() to loop and dispatch events. + + @section bufferevent I/O Buffers + + libevent provides an abstraction on top of the regular event callbacks. This + abstraction is called a buffered event. A buffered event provides input and + output buffers that get filled and drained automatically. The user of a + buffered event no longer deals directly with the I/O, but instead is reading + from input and writing to output buffers. + + Once initialized via bufferevent_new(), the bufferevent structure can be + used repeatedly with bufferevent_enable() and bufferevent_disable(). + Instead of reading and writing directly to a socket, you would call + bufferevent_read() and bufferevent_write(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + @section timers Timers + + libevent can also be used to create timers that invoke a callback after a + certain amount of time has expired. The evtimer_set() function prepares an + event struct to be used as a timer. To activate the timer, call + evtimer_add(). Timers can be deactivated by calling evtimer_del(). + + @section timeouts Timeouts + + In addition to simple timers, libevent can assign timeout events to file + descriptors that are triggered whenever a certain amount of time has passed + with no activity on a file descriptor. The timeout_set() function + initializes an event struct for use as a timeout. Once initialized, the + event must be activated by using timeout_add(). To cancel the timeout, call + timeout_del(). + + @section evdns Asynchronous DNS resolution + + libevent provides an asynchronous DNS resolver that should be used instead + of the standard DNS resolver functions. These functions can be imported by + including the <evdns.h> header in your program. Before using any of the + resolver functions, you must call evdns_init() to initialize the library. To + convert a hostname to an IP address, you call the evdns_resolve_ipv4() + function. To perform a reverse lookup, you would call the + evdns_resolve_reverse() function. All of these functions use callbacks to + avoid blocking while the lookup is performed. + + @section evhttp Event-driven HTTP servers + + libevent provides a very simple event-driven HTTP server that can be + embedded in your program and used to service HTTP requests. + + To use this capability, you need to include the <evhttp.h> header in your + program. You create the server by calling evhttp_new(). Add addresses and + ports to listen on with evhttp_bind_socket(). You then register one or more + callbacks to handle incoming requests. Each URI can be assigned a callback + via the evhttp_set_cb() function. A generic callback function can also be + registered via evhttp_set_gencb(); this callback will be invoked if no other + callbacks have been registered for a given URI. + + @section evrpc A framework for RPC servers and clients + + libevents provides a framework for creating RPC servers and clients. It + takes care of marshaling and unmarshaling all data structures. + + @section api API Reference + + To browse the complete documentation of the libevent API, click on any of + the following links. + + event.h + The primary libevent header + + evdns.h + Asynchronous DNS resolution + + evhttp.h + An embedded libevent-based HTTP server + + evrpc.h + A framework for creating RPC servers and clients + + */ + +/** @file event.h + + A library for writing event-driven network servers + + */ + #ifdef __cplusplus extern "C" { #endif +#include <event-config.h> +#ifdef _EVENT_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef _EVENT_HAVE_SYS_TIME_H #include <sys/time.h> +#endif +#ifdef _EVENT_HAVE_STDINT_H #include <stdint.h> +#endif #include <stdarg.h> +/* For int types. */ +#include <evutil.h> + #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> @@ -45,8 +184,6 @@ typedef unsigned char u_char; typedef unsigned short u_short; #endif -#define LIBEVENT_VERSION "1.3e" - #define EVLIST_TIMEOUT 0x01 #define EVLIST_INSERTED 0x02 #define EVLIST_SIGNAL 0x04 @@ -72,25 +209,17 @@ struct { \ struct type **tqe_prev; /* address of previous next element */ \ } #endif /* !TAILQ_ENTRY */ -#ifndef RB_ENTRY -#define _EVENT_DEFINED_RBENTRY -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} -#endif /* !RB_ENTRY */ struct event_base; +#ifndef EVENT_NO_STRUCT struct event { TAILQ_ENTRY (event) ev_next; TAILQ_ENTRY (event) ev_active_next; TAILQ_ENTRY (event) ev_signal_next; - RB_ENTRY (event) ev_timeout_node; + unsigned int min_heap_idx; /* for managing timeouts */ struct event_base *ev_base; + int ev_fd; short ev_events; short ev_ncalls; @@ -106,6 +235,9 @@ struct event { int ev_res; /* result passed to event callback */ int ev_flags; }; +#else +struct event; +#endif #define EVENT_SIGNAL(ev) (int)(ev)->ev_fd #define EVENT_FD(ev) (int)(ev)->ev_fd @@ -130,49 +262,256 @@ struct evkeyvalq; TAILQ_HEAD (event_list, event); TAILQ_HEAD (evkeyvalq, evkeyval); #endif /* _EVENT_DEFINED_TQENTRY */ -#ifdef _EVENT_DEFINED_RBENTRY -#undef RB_ENTRY -#undef _EVENT_DEFINED_RBENTRY -#endif /* _EVENT_DEFINED_RBENTRY */ - -struct eventop { - char *name; - void *(*init)(struct event_base *); - int (*add)(void *, struct event *); - int (*del)(void *, struct event *); - int (*recalc)(struct event_base *, void *, int); - int (*dispatch)(struct event_base *, void *, struct timeval *); - void (*dealloc)(struct event_base *, void *); -}; +/** + Initialize the event API. + + Use event_base_new() to initialize a new event base, but does not set + the current_base global. If using only event_base_new(), each event + added must have an event base set with event_base_set() + + @see event_base_set(), event_base_free(), event_init() + */ +struct event_base *event_base_new(void); + +/** + Initialize the event API. + + The event API needs to be initialized with event_init() before it can be + used. Sets the current_base global representing the default base for + events that have no base associated with them. + + @see event_base_set(), event_base_new() + */ struct event_base *event_init(void); + +/** + Reinitialized the event base after a fork + + Some event mechanisms do not survive across fork. The event base needs + to be reinitialized with the event_reinit() function. + + @param base the event base that needs to be re-initialized + @return 0 if successful, or -1 if some events could not be re-added. + @see event_base_new(), event_init() +*/ +int event_reinit(struct event_base *base); + +/** + Loop to process events. + + In order to process events, an application needs to call + event_dispatch(). This function only returns on error, and should + replace the event core of the application program. + + @see event_base_dispatch() + */ int event_dispatch(void); + + +/** + Threadsafe event dispatching loop. + + @param eb the event_base structure returned by event_init() + @see event_init(), event_dispatch() + */ int event_base_dispatch(struct event_base *); + + +/** + Get the kernel event notification mechanism used by libevent. + + @param eb the event_base structure returned by event_base_new() + @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) + */ +const char *event_base_get_method(struct event_base *); + + +/** + Deallocate all memory associated with an event_base, and free the base. + + Note that this function will not close any fds or free any memory passed + to event_set as the argument to callback. + + @param eb an event_base to be freed + */ void event_base_free(struct event_base *); + #define _EVENT_LOG_DEBUG 0 #define _EVENT_LOG_MSG 1 #define _EVENT_LOG_WARN 2 #define _EVENT_LOG_ERR 3 typedef void (*event_log_cb)(int severity, const char *msg); +/** + Redirect libevent's log messages. + + @param cb a function taking two arguments: an integer severity between + _EVENT_LOG_DEBUG and _EVENT_LOG_ERR, and a string. If cb is NULL, + then the default log is used. + */ void event_set_log_callback(event_log_cb cb); -/* Associate a different event base with an event */ +/** + Associate a different event base with an event. + + @param eb the event base + @param ev the event + */ int event_base_set(struct event_base *, struct event *); -#define EVLOOP_ONCE 0x01 -#define EVLOOP_NONBLOCK 0x02 +/** + event_loop() flags + */ +/*@{*/ +#define EVLOOP_ONCE 0x01 /**< Block at most once. */ +#define EVLOOP_NONBLOCK 0x02 /**< Do not block. */ +/*@}*/ + +/** + Handle events. + + This is a more flexible version of event_dispatch(). + + @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK + @return 0 if successful, -1 if an error occurred, or 1 if no events were + registered. + @see event_loopexit(), event_base_loop() +*/ int event_loop(int); + +/** + Handle events (threadsafe version). + + This is a more flexible version of event_base_dispatch(). + + @param eb the event_base structure returned by event_init() + @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK + @return 0 if successful, -1 if an error occurred, or 1 if no events were + registered. + @see event_loopexit(), event_base_loop() + */ int event_base_loop(struct event_base *, int); -int event_loopexit(struct timeval *); /* Causes the loop to exit */ -int event_base_loopexit(struct event_base *, struct timeval *); +/** + Exit the event loop after the specified time. + + The next event_loop() iteration after the given timer expires will + complete normally (handling all queued events) then exit without + blocking for events again. + + Subsequent invocations of event_loop() will proceed normally. + + @param tv the amount of time after which the loop should terminate. + @return 0 if successful, or -1 if an error occurred + @see event_loop(), event_base_loop(), event_base_loopexit() + */ +int event_loopexit(const struct timeval *); + + +/** + Exit the event loop after the specified time (threadsafe variant). + + The next event_base_loop() iteration after the given timer expires will + complete normally (handling all queued events) then exit without + blocking for events again. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @param tv the amount of time after which the loop should terminate. + @return 0 if successful, or -1 if an error occurred + @see event_loopexit() + */ +int event_base_loopexit(struct event_base *, const struct timeval *); + +/** + Abort the active event_loop() immediately. + + event_loop() will abort the loop after the next event is completed; + event_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_loop() will proceed normally. + + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak(), event_loopexit() + */ +int event_loopbreak(void); + +/** + Abort the active event_base_loop() immediately. + + event_base_loop() will abort the loop after the next event is completed; + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopexit + */ +int event_base_loopbreak(struct event_base *); + + +/** + Add a timer event. + + @param ev the event struct + @param tv timeval struct + */ #define evtimer_add(ev, tv) event_add(ev, tv) + + +/** + Define a timer event. + + @param ev event struct to be modified + @param cb callback function + @param arg argument that will be passed to the callback function + */ #define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) + + +/** + * Delete a timer event. + * + * @param ev the event struct to be disabled + */ #define evtimer_del(ev) event_del(ev) #define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv) #define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) +/** + * Add a timeout event. + * + * @param ev the event struct to be disabled + * @param tv the timeout value, in seconds + */ +#define timeout_add(ev, tv) event_add(ev, tv) + + +/** + * Define a timeout event. + * + * @param ev the event struct to be defined + * @param cb the callback to be invoked when the timeout expires + * @param arg the argument to be passed to the callback + */ +#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) + + +/** + * Disable a timeout event. + * + * @param ev the timeout event to be disabled + */ +#define timeout_del(ev) event_del(ev) + +#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv) +#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) + #define signal_add(ev, tv) event_add(ev, tv) #define signal_set(ev, x, cb, arg) \ event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) @@ -180,32 +519,210 @@ int event_base_loopexit(struct event_base *, struct timeval *); #define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv) #define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) +/** + Prepare an event structure to be added. + + The function event_set() prepares the event structure ev to be used in + future calls to event_add() and event_del(). The event will be prepared to + call the function specified by the fn argument with an int argument + indicating the file descriptor, a short argument indicating the type of + event, and a void * argument given in the arg argument. The fd indicates + the file descriptor that should be monitored for events. The events can be + either EV_READ, EV_WRITE, or both. Indicating that an application can read + or write from the file descriptor respectively without blocking. + + The function fn will be called with the file descriptor that triggered the + event and the type of event which will be either EV_TIMEOUT, EV_SIGNAL, + EV_READ, or EV_WRITE. The additional flag EV_PERSIST makes an event_add() + persistent until event_del() has been called. + + @param ev an event struct to be modified + @param fd the file descriptor to be monitored + @param event desired events to monitor; can be EV_READ and/or EV_WRITE + @param fn callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + + @see event_add(), event_del(), event_once() + + */ void event_set(struct event *, int, short, void (*)(int, short, void *), void *); -int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *); -int event_base_once(struct event_base *, int, short, void (*)(int, short, void *), void *, struct timeval *); -int event_add(struct event *, struct timeval *); +/** + Schedule a one-time event to occur. + + The function event_once() is similar to event_set(). However, it schedules + a callback to be called exactly once and does not require the caller to + prepare an event structure. + + @param fd a file descriptor to monitor + @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ | + EV_WRITE + @param callback callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_set() + + */ +int event_once(int, short, void (*)(int, short, void *), void *, + const struct timeval *); + + +/** + Schedule a one-time event (threadsafe variant) + + The function event_base_once() is similar to event_set(). However, it + schedules a callback to be called exactly once and does not require the + caller to prepare an event structure. + + @param base an event_base returned by event_init() + @param fd a file descriptor to monitor + @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ | + EV_WRITE + @param callback callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_once() + */ +int event_base_once(struct event_base *base, int fd, short events, + void (*callback)(int, short, void *), void *arg, + const struct timeval *timeout); + + +/** + Add an event to the set of monitored events. + + The function event_add() schedules the execution of the ev event when the + event specified in event_set() occurs or in at least the time specified in + the tv. If tv is NULL, no timeout occurs and the function will only be + called if a matching event occurs on the file descriptor. The event in the + ev argument must be already initialized by event_set() and may not be used + in calls to event_set() until it has timed out or been removed with + event_del(). If the event in the ev argument already has a scheduled + timeout, the old timeout will be replaced by the new one. + + @param ev an event struct initialized via event_set() + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_del(), event_set() + */ +int event_add(struct event *ev, const struct timeval *timeout); + + +/** + Remove an event from the set of monitored events. + + The function event_del() will cancel the event in the argument ev. If the + event has already executed or has never been added the call will have no + effect. + + @param ev an event struct to be removed from the working set + @return 0 if successful, or -1 if an error occurred + @see event_add() + */ int event_del(struct event *); + void event_active(struct event *, int, short); -int event_pending(struct event *, short, struct timeval *); +/** + Checks if a specific event is pending or scheduled. + + @param ev an event struct previously passed to event_add() + @param event the requested event type; any of EV_TIMEOUT|EV_READ| + EV_WRITE|EV_SIGNAL + @param tv an alternate timeout (FIXME - is this true?) + + @return 1 if the event is pending, or 0 if the event has not occurred + + */ +int event_pending(struct event *ev, short event, struct timeval *tv); + + +/** + Test if an event structure has been initialized. + + The event_initialized() macro can be used to check if an event has been + initialized. + + @param ev an event structure to be tested + @return 1 if the structure has been initialized, or 0 if it has not been + initialized + */ #ifdef WIN32 #define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != (int)INVALID_HANDLE_VALUE) #else #define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) #endif -/* Some simple debugging functions */ + +/** + Get the libevent version number. + + @return a string containing the version number of libevent + */ const char *event_get_version(void); + + +/** + Get the kernel event notification mechanism used by libevent. + + @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) + */ const char *event_get_method(void); -/* These functions deal with event priorities */ +/** + Set the number of different event priorities. + + By default libevent schedules all active events with the same priority. + However, some time it is desirable to process some events with a higher + priority than others. For that reason, libevent supports strict priority + queues. Active events with a lower priority are always processed before + events with a higher priority. + + The number of different priorities can be set initially with the + event_priority_init() function. This function should be called before the + first call to event_dispatch(). The event_priority_set() function can be + used to assign a priority to an event. By default, libevent assigns the + middle priority to all events unless their priority is explicitly set. + + @param npriorities the maximum number of priorities + @return 0 if successful, or -1 if an error occurred + @see event_base_priority_init(), event_priority_set() + + */ int event_priority_init(int); + + +/** + Set the number of different event priorities (threadsafe variant). + + See the description of event_priority_init() for more information. + + @param eb the event_base structure returned by event_init() + @param npriorities the maximum number of priorities + @return 0 if successful, or -1 if an error occurred + @see event_priority_init(), event_priority_set() + */ int event_base_priority_init(struct event_base *, int); + + +/** + Assign a priority to an event. + + @param ev an event struct + @param priority the new priority to be assigned + @return 0 if successful, or -1 if an error occurred + @see event_priority_init() + */ int event_priority_set(struct event *, int); + /* These functions deal with buffering input and output */ struct evbuffer { @@ -236,7 +753,10 @@ struct event_watermark { size_t high; }; +#ifndef EVENT_NO_STRUCT struct bufferevent { + struct event_base *ev_base; + struct event ev_read; struct event ev_write; @@ -256,39 +776,384 @@ struct bufferevent { short enabled; /* events that are currently enabled */ }; +#endif +/** + Create a new bufferevent. + + libevent provides an abstraction on top of the regular event callbacks. + This abstraction is called a buffered event. A buffered event provides + input and output buffers that get filled and drained automatically. The + user of a buffered event no longer deals directly with the I/O, but + instead is reading from input and writing to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly with + bufferevent_enable() and bufferevent_disable(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + If multiple bases are in use, bufferevent_base_set() must be called before + enabling the bufferevent for the first time. + + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param errorcb callback to invoke when there is an error on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_base_set(), bufferevent_free() + */ struct bufferevent *bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + + +/** + Assign a bufferevent to a specific event_base. + + @param base an event_base returned by event_init() + @param bufev a bufferevent struct returned by bufferevent_new() + @return 0 if successful, or -1 if an error occurred + @see bufferevent_new() + */ int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); + + +/** + Assign a priority to a bufferevent. + + @param bufev a bufferevent struct + @param pri the priority to be assigned + @return 0 if successful, or -1 if an error occurred + */ int bufferevent_priority_set(struct bufferevent *bufev, int pri); + + +/** + Deallocate the storage associated with a bufferevent structure. + + @param bufev the bufferevent structure to be freed. + */ void bufferevent_free(struct bufferevent *bufev); + + +/** + Changes the callbacks for a bufferevent. + + @param bufev the bufferevent object for which to change callbacks + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param errorcb callback to invoke when there is an error on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @see bufferevent_new() + */ +void bufferevent_setcb(struct bufferevent *bufev, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + +/** + Changes the file descriptor on which the bufferevent operates. + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +void bufferevent_setfd(struct bufferevent *bufev, int fd); + +/** + Write data to a bufferevent buffer. + + The bufferevent_write() function can be used to write data to the file + descriptor. The data is appended to the output buffer and written to the + descriptor automatically as it becomes available for writing. + + @param bufev the bufferevent to be written to + @param data a pointer to the data to be written + @param size the length of the data, in bytes + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write_buffer() + */ int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size); + + +/** + Write data from an evbuffer to a bufferevent buffer. The evbuffer is + being drained as a result. + + @param bufev the bufferevent to be written to + @param buf the evbuffer to be written + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write() + */ int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); + + +/** + Read data from a bufferevent buffer. + + The bufferevent_read() function is used to read data from the input buffer. + + @param bufev the bufferevent to be read from + @param data pointer to a buffer that will store the data + @param size the size of the data buffer, in bytes + @return the amount of data read, in bytes. + */ size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); + +/** + Enable a bufferevent. + + @param bufev the bufferevent to be enabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_disable() + */ int bufferevent_enable(struct bufferevent *bufev, short event); + + +/** + Disable a bufferevent. + + @param bufev the bufferevent to be disabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_enable() + */ int bufferevent_disable(struct bufferevent *bufev, short event); + + +/** + Set the read and write timeout for a buffered event. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout + @param timeout_write the write timeout + */ void bufferevent_settimeout(struct bufferevent *bufev, int timeout_read, int timeout_write); + +/** + Sets the watermarks for read and write events. + + On input, a bufferevent does not invoke the user read callback unless + there is at least low watermark data in the buffer. If the read buffer + is beyond the high watermark, the buffevent stops reading from the network. + + On output, the user write callback is invoked whenever the buffered data + falls below the low watermark. + + @param bufev the bufferevent to be modified + @param events EV_READ, EV_WRITE or both + @param lowmark the lower watermark to set + @param highmark the high watermark to set +*/ + +void bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark); + #define EVBUFFER_LENGTH(x) (x)->off #define EVBUFFER_DATA(x) (x)->buffer #define EVBUFFER_INPUT(x) (x)->input #define EVBUFFER_OUTPUT(x) (x)->output + +/** + Allocate storage for a new evbuffer. + + @return a pointer to a newly allocated evbuffer struct, or NULL if an error + occurred + */ struct evbuffer *evbuffer_new(void); + + +/** + Deallocate storage for an evbuffer. + + @param pointer to the evbuffer to be freed + */ void evbuffer_free(struct evbuffer *); + + +/** + Expands the available space in an event buffer. + + Expands the available space in the event buffer to at least datlen + + @param buf the event buffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ int evbuffer_expand(struct evbuffer *, size_t); + + +/** + Append data to the end of an evbuffer. + + @param buf the event buffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + */ int evbuffer_add(struct evbuffer *, const void *, size_t); + + + +/** + Read data from an event buffer and drain the bytes read. + + @param buf the event buffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read + */ int evbuffer_remove(struct evbuffer *, void *, size_t); + + +/** + * Read a single line from an event buffer. + * + * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. + * The returned buffer needs to be freed by the caller. + * + * @param buffer the evbuffer to read from + * @return pointer to a single line, or NULL if an error occurred + */ char *evbuffer_readline(struct evbuffer *); + + +/** Used to tell evbuffer_readln what kind of line-ending to look for. + */ +enum evbuffer_eol_style { + /** Any sequence of CR and LF characters is acceptable as an EOL. */ + EVBUFFER_EOL_ANY, + /** An EOL is an LF, optionally preceded by a CR. This style is + * most useful for implementing text-based internet protocols. */ + EVBUFFER_EOL_CRLF, + /** An EOL is a CR followed by an LF. */ + EVBUFFER_EOL_CRLF_STRICT, + /** An EOL is a LF. */ + EVBUFFER_EOL_LF +}; + +/** + * Read a single line from an event buffer. + * + * Reads a line terminated by an EOL as determined by the evbuffer_eol_style + * argument. Returns a newly allocated nul-terminated string; the caller must + * free the returned value. The EOL is not included in the returned string. + * + * @param buffer the evbuffer to read from + * @param n_read_out if non-NULL, points to a size_t that is set to the + * number of characters in the returned string. This is useful for + * strings that can contain NUL characters. + * @param eol_style the style of line-ending to use. + * @return pointer to a single line, or NULL if an error occurred + */ +char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style); + + +/** + Move data from one evbuffer into another evbuffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. The destination buffer is expanded as needed. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *); -int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...); + + +/** + Append a formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + + +/** + Append a va_list formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap); + + +/** + Remove a specified number of bytes data from the beginning of an evbuffer. + + @param buf the evbuffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + */ void evbuffer_drain(struct evbuffer *, size_t); + + +/** + Write the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ int evbuffer_write(struct evbuffer *, int); + + +/** + Read from a file descriptor and store the result in an evbuffer. + + @param buf the evbuffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read + @return the number of bytes read, or -1 if an error occurred + @see evbuffer_write() + */ int evbuffer_read(struct evbuffer *, int, int); + + +/** + Find a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @return a pointer to the beginning of the search string, or NULL if the search failed. + */ u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t); + +/** + Set a callback to invoke when the evbuffer is modified. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified + @param cbarg an argument to be provided to the callback function + */ void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *); /* @@ -299,37 +1164,47 @@ void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_ void evtag_init(void); -void evtag_marshal(struct evbuffer *evbuf, uint8_t tag, const void *data, - uint32_t len); +void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, + ev_uint32_t len); -void encode_int(struct evbuffer *evbuf, uint32_t number); +/** + Encode an integer and store it in an evbuffer. -void evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer); + 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 evtag_marshal_string(struct evbuffer *buf, uint8_t tag, + @param evbuf evbuffer to store the encoded number + @param number a 32-bit integer + */ +void encode_int(struct evbuffer *evbuf, ev_uint32_t number); + +void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint32_t integer); + +void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string); -void evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag, +void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv); -void evtag_test(void); - -int evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst); -int evtag_peek(struct evbuffer *evbuf, uint8_t *ptag); -int evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength); -int evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength); +int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, + struct evbuffer *dst); +int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag); +int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength); +int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength); int evtag_consume(struct evbuffer *evbuf); -int evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag, - uint32_t *pinteger); +int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger); -int evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data, - size_t len); +int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, + void *data, size_t len); -int evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag, +int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, char **pstring); -int evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag, +int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, struct timeval *ptv); #ifdef __cplusplus diff --git a/lib/libevent/event_tagging.c b/lib/libevent/event_tagging.c index 74ff08118df..9090557af34 100644 --- a/lib/libevent/event_tagging.c +++ b/lib/libevent/event_tagging.c @@ -1,4 +1,4 @@ -/* $OpenBSD: event_tagging.c,v 1.2 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: event_tagging.c,v 1.3 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu> @@ -27,22 +27,26 @@ * 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 HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + #ifdef WIN32 #define WIN32_LEAN_AND_MEAN +#include <winsock2.h> #include <windows.h> #undef WIN32_LEAN_AND_MEAN #else #include <sys/ioctl.h> #endif -#include <sys/tree.h> #include <sys/queue.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> @@ -55,17 +59,22 @@ #ifndef WIN32 #include <syslog.h> #endif +#ifdef HAVE_UNISTD_H #include <unistd.h> +#endif #include "event.h" +#include "evutil.h" #include "log.h" -int decode_int(uint32_t *pnumber, struct evbuffer *evbuf); +int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf); +int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag); +int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf); static struct evbuffer *_buf; /* not thread safe */ void -evtag_init() +evtag_init(void) { if (_buf != NULL) return; @@ -81,12 +90,12 @@ evtag_init() */ void -encode_int(struct evbuffer *evbuf, uint32_t number) +encode_int(struct evbuffer *evbuf, ev_uint32_t number) { int off = 1, nibbles = 0; - uint8_t data[5]; + ev_uint8_t data[5]; - memset(data, 0, sizeof(data)); + memset(data, 0, sizeof(ev_uint32_t)+1); while (number) { if (off & 0x1) data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f); @@ -107,40 +116,105 @@ encode_int(struct evbuffer *evbuf, uint32_t number) } /* + * Support variable length encoding of tags; we use the high bit in each + * octet as a continuation signal. + */ + +int +evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag) +{ + int bytes = 0; + ev_uint8_t data[5]; + + memset(data, 0, sizeof(data)); + do { + ev_uint8_t lower = tag & 0x7f; + tag >>= 7; + + if (tag) + lower |= 0x80; + + data[bytes++] = lower; + } while (tag); + + if (evbuf != NULL) + evbuffer_add(evbuf, data, bytes); + + return (bytes); +} + +static int +decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain) +{ + ev_uint32_t number = 0; + ev_uint8_t *data = EVBUFFER_DATA(evbuf); + int len = EVBUFFER_LENGTH(evbuf); + int count = 0, shift = 0, done = 0; + + while (count++ < len) { + ev_uint8_t lower = *data++; + number |= (lower & 0x7f) << shift; + shift += 7; + + if (!(lower & 0x80)) { + done = 1; + break; + } + } + + if (!done) + return (-1); + + if (dodrain) + evbuffer_drain(evbuf, count); + + if (ptag != NULL) + *ptag = number; + + return (count); +} + +int +evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf) +{ + return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */)); +} + +/* * Marshal a data type, the general format is as follows: * * tag number: one byte; length: var bytes; payload: var bytes */ void -evtag_marshal(struct evbuffer *evbuf, uint8_t tag, - const void *data, uint32_t len) +evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, + const void *data, ev_uint32_t len) { - evbuffer_add(evbuf, &tag, sizeof(tag)); + evtag_encode_tag(evbuf, tag); encode_int(evbuf, len); evbuffer_add(evbuf, (void *)data, len); } /* Marshaling for integers */ void -evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer) +evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer) { evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); encode_int(_buf, integer); - evbuffer_add(evbuf, &tag, sizeof(tag)); + evtag_encode_tag(evbuf, tag); encode_int(evbuf, EVBUFFER_LENGTH(_buf)); evbuffer_add_buffer(evbuf, _buf); } void -evtag_marshal_string(struct evbuffer *buf, uint8_t tag, const char *string) +evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string) { evtag_marshal(buf, tag, string, strlen(string)); } void -evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag, struct timeval *tv) +evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv) { evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); @@ -152,31 +226,30 @@ evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag, struct timeval *tv) } static int -decode_int_internal(uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) +decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) { - uint32_t number = 0; - uint8_t *data = EVBUFFER_DATA(evbuf); + ev_uint32_t number = 0; + ev_uint8_t *data = EVBUFFER_DATA(evbuf); int len = EVBUFFER_LENGTH(evbuf); - int nibbles = 0, off; + int nibbles = 0; if (!len) return (-1); nibbles = ((data[0] & 0xf0) >> 4) + 1; - if (nibbles > 8 || (nibbles >> 1) > len - 1) + if (nibbles > 8 || (nibbles >> 1) + 1 > len) return (-1); + len = (nibbles >> 1) + 1; - off = nibbles; - while (off > 0) { + while (nibbles > 0) { number <<= 4; - if (off & 0x1) - number |= data[off >> 1] & 0x0f; + if (nibbles & 0x1) + number |= data[nibbles >> 1] & 0x0f; else - number |= (data[off >> 1] & 0xf0) >> 4; - off--; + number |= (data[nibbles >> 1] & 0xf0) >> 4; + nibbles--; } - len = (nibbles >> 1) + 1; if (dodrain) evbuffer_drain(evbuf, len); @@ -186,55 +259,53 @@ decode_int_internal(uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) } int -decode_int(uint32_t *pnumber, struct evbuffer *evbuf) +evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf) { return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0); } int -evtag_peek(struct evbuffer *evbuf, uint8_t *ptag) +evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag) { - if (EVBUFFER_LENGTH(evbuf) < 2) - return (-1); - *ptag = EVBUFFER_DATA(evbuf)[0]; - - return (0); + return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */)); } int -evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength) +evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength) { struct evbuffer tmp; - int res; + int res, len; - if (EVBUFFER_LENGTH(evbuf) < 2) + len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); + if (len == -1) return (-1); tmp = *evbuf; - tmp.buffer += 1; - tmp.off -= 1; + tmp.buffer += len; + tmp.off -= len; res = decode_int_internal(plength, &tmp, 0); if (res == -1) return (-1); - *plength += res + 1; + *plength += res + len; return (0); } int -evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength) +evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength) { struct evbuffer tmp; - int res; + int res, len; - if (EVBUFFER_LENGTH(evbuf) < 2) + len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); + if (len == -1) return (-1); tmp = *evbuf; - tmp.buffer += 1; - tmp.off -= 1; + tmp.buffer += len; + tmp.off -= len; res = decode_int_internal(plength, &tmp, 0); if (res == -1) @@ -246,9 +317,10 @@ evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength) int evtag_consume(struct evbuffer *evbuf) { - uint32_t len; - evbuffer_drain(evbuf, 1); - if (decode_int(&len, evbuf) == -1) + ev_uint32_t len; + if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1) + return (-1); + if (evtag_decode_int(&len, evbuf) == -1) return (-1); evbuffer_drain(evbuf, len); @@ -258,44 +330,43 @@ evtag_consume(struct evbuffer *evbuf) /* Reads the data type from an event buffer */ int -evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst) +evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst) { - uint8_t tag; - uint32_t len; - uint32_t integer; + ev_uint32_t len; + ev_uint32_t integer; - if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag)) + if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1) return (-1); - if (decode_int(&integer, src) == -1) + if (evtag_decode_int(&integer, src) == -1) return (-1); len = integer; if (EVBUFFER_LENGTH(src) < len) return (-1); - + if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) return (-1); evbuffer_drain(src, len); - *ptag = tag; return (len); } /* Marshaling for integers */ int -evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag, - uint32_t *pinteger) +evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger) { - uint8_t tag; - uint32_t len; - uint32_t integer; + ev_uint32_t tag; + ev_uint32_t len; + ev_uint32_t integer; - if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) || - tag != need_tag) + if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1) + return (-1); + if (need_tag != tag) return (-1); - if (decode_int(&integer, evbuf) == -1) + if (evtag_decode_int(&integer, evbuf) == -1) return (-1); len = integer; @@ -308,16 +379,16 @@ evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag, evbuffer_drain(evbuf, len); - return (decode_int(pinteger, _buf)); + return (evtag_decode_int(pinteger, _buf)); } /* Unmarshal a fixed length tag */ int -evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data, +evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data, size_t len) { - uint8_t tag; + ev_uint32_t tag; /* Initialize this event buffer so that we can read into it */ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); @@ -334,10 +405,10 @@ evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data, } int -evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag, +evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, char **pstring) { - uint8_t tag; + ev_uint32_t tag; evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); @@ -353,20 +424,20 @@ evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag, } int -evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag, +evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, struct timeval *ptv) { - uint8_t tag; - uint32_t integer; + ev_uint32_t tag; + ev_uint32_t integer; evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) return (-1); - if (decode_int(&integer, _buf) == -1) + if (evtag_decode_int(&integer, _buf) == -1) return (-1); ptv->tv_sec = integer; - if (decode_int(&integer, _buf) == -1) + if (evtag_decode_int(&integer, _buf) == -1) return (-1); ptv->tv_usec = integer; diff --git a/lib/libevent/evsignal.h b/lib/libevent/evsignal.h index 7949411de08..4b92c148860 100644 --- a/lib/libevent/evsignal.h +++ b/lib/libevent/evsignal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: evsignal.h,v 1.3 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: evsignal.h,v 1.4 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> @@ -29,15 +29,23 @@ #ifndef _EVSIGNAL_H_ #define _EVSIGNAL_H_ +typedef void (*ev_sighandler_t)(int); + struct evsignal_info { - struct event_list signalqueue; struct event ev_signal; int ev_signal_pair[2]; int ev_signal_added; volatile sig_atomic_t evsignal_caught; + struct event_list evsigevents[NSIG]; sig_atomic_t evsigcaught[NSIG]; +#ifdef HAVE_SIGACTION + struct sigaction **sh_old; +#else + ev_sighandler_t **sh_old; +#endif + int sh_old_max; }; -void evsignal_init(struct event_base *); +int evsignal_init(struct event_base *); void evsignal_process(struct event_base *); int evsignal_add(struct event *); int evsignal_del(struct event *); diff --git a/lib/libevent/evutil.c b/lib/libevent/evutil.c new file mode 100644 index 00000000000..62286bb812e --- /dev/null +++ b/lib/libevent/evutil.c @@ -0,0 +1,279 @@ +/* $OpenBSD: evutil.c,v 1.1 2010/04/21 20:02:40 nicm Exp $ */ + +/* + * Copyright (c) 2007 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WIN32 +#include <winsock2.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#endif + +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <errno.h> +#if defined WIN32 && !defined(HAVE_GETTIMEOFDAY_H) +#include <sys/timeb.h> +#endif +#include <stdio.h> +#include <signal.h> + +#include <sys/queue.h> +#include "event.h" +#include "event-internal.h" +#include "evutil.h" +#include "log.h" + +int +evutil_socketpair(int family, int type, int protocol, int fd[2]) +{ +#ifndef WIN32 + return socketpair(family, type, protocol, fd); +#else + /* This code is originally from Tor. Used with permission. */ + + /* This socketpair does not work when localhost is down. So + * it's really not the same thing at all. But it's close enough + * for now, and really, when localhost is down sometimes, we + * have other problems too. + */ + int listener = -1; + int connector = -1; + int acceptor = -1; + struct sockaddr_in listen_addr; + struct sockaddr_in connect_addr; + int size; + int saved_errno = -1; + + if (protocol +#ifdef AF_UNIX + || family != AF_UNIX +#endif + ) { + EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT); + return -1; + } + if (!fd) { + EVUTIL_SET_SOCKET_ERROR(WSAEINVAL); + return -1; + } + + listener = socket(AF_INET, type, 0); + if (listener < 0) + return -1; + memset(&listen_addr, 0, sizeof(listen_addr)); + listen_addr.sin_family = AF_INET; + listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + listen_addr.sin_port = 0; /* kernel chooses port. */ + if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr)) + == -1) + goto tidy_up_and_fail; + if (listen(listener, 1) == -1) + goto tidy_up_and_fail; + + connector = socket(AF_INET, type, 0); + if (connector < 0) + goto tidy_up_and_fail; + /* We want to find out the port number to connect to. */ + size = sizeof(connect_addr); + if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1) + goto tidy_up_and_fail; + if (size != sizeof (connect_addr)) + goto abort_tidy_up_and_fail; + if (connect(connector, (struct sockaddr *) &connect_addr, + sizeof(connect_addr)) == -1) + goto tidy_up_and_fail; + + size = sizeof(listen_addr); + acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size); + if (acceptor < 0) + goto tidy_up_and_fail; + if (size != sizeof(listen_addr)) + goto abort_tidy_up_and_fail; + EVUTIL_CLOSESOCKET(listener); + /* Now check we are talking to ourself by matching port and host on the + two sockets. */ + if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1) + goto tidy_up_and_fail; + if (size != sizeof (connect_addr) + || listen_addr.sin_family != connect_addr.sin_family + || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr + || listen_addr.sin_port != connect_addr.sin_port) + goto abort_tidy_up_and_fail; + fd[0] = connector; + fd[1] = acceptor; + + return 0; + + abort_tidy_up_and_fail: + saved_errno = WSAECONNABORTED; + tidy_up_and_fail: + if (saved_errno < 0) + saved_errno = WSAGetLastError(); + if (listener != -1) + EVUTIL_CLOSESOCKET(listener); + if (connector != -1) + EVUTIL_CLOSESOCKET(connector); + if (acceptor != -1) + EVUTIL_CLOSESOCKET(acceptor); + + EVUTIL_SET_SOCKET_ERROR(saved_errno); + return -1; +#endif +} + +int +evutil_make_socket_nonblocking(int fd) +{ +#ifdef WIN32 + { + unsigned long nonblocking = 1; + ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking); + } +#else + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + event_warn("fcntl(O_NONBLOCK)"); + return -1; +} +#endif + return 0; +} + +ev_int64_t +evutil_strtoll(const char *s, char **endptr, int base) +{ +#ifdef HAVE_STRTOLL + return (ev_int64_t)strtoll(s, endptr, base); +#elif SIZEOF_LONG == 8 + return (ev_int64_t)strtol(s, endptr, base); +#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300 + /* XXXX on old versions of MS APIs, we only support base + * 10. */ + ev_int64_t r; + if (base != 10) + return 0; + r = (ev_int64_t) _atoi64(s); + while (isspace(*s)) + ++s; + while (isdigit(*s)) + ++s; + if (endptr) + *endptr = (char*) s; + return r; +#elif defined(WIN32) + return (ev_int64_t) _strtoi64(s, endptr, base); +#else +#error "I don't know how to parse 64-bit integers." +#endif +} + +#ifndef _EVENT_HAVE_GETTIMEOFDAY +int +evutil_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct _timeb tb; + + if(tv == NULL) + return -1; + + _ftime(&tb); + tv->tv_sec = (long) tb.time; + tv->tv_usec = ((int) tb.millitm) * 1000; + return 0; +} +#endif + +int +evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +{ + int r; + va_list ap; + va_start(ap, format); + r = evutil_vsnprintf(buf, buflen, format, ap); + va_end(ap); + return r; +} + +int +evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +{ +#ifdef _MSC_VER + int r = _vsnprintf(buf, buflen, format, ap); + buf[buflen-1] = '\0'; + if (r >= 0) + return r; + else + return _vscprintf(format, ap); +#else + int r = vsnprintf(buf, buflen, format, ap); + buf[buflen-1] = '\0'; + return r; +#endif +} + +static int +evutil_issetugid(void) +{ +#ifdef _EVENT_HAVE_ISSETUGID + return issetugid(); +#else + +#ifdef _EVENT_HAVE_GETEUID + if (getuid() != geteuid()) + return 1; +#endif +#ifdef _EVENT_HAVE_GETEGID + if (getgid() != getegid()) + return 1; +#endif + return 0; +#endif +} + +const char * +evutil_getenv(const char *varname) +{ + if (evutil_issetugid()) + return NULL; + + return getenv(varname); +} diff --git a/lib/libevent/evutil.h b/lib/libevent/evutil.h new file mode 100644 index 00000000000..c0e1a5d292d --- /dev/null +++ b/lib/libevent/evutil.h @@ -0,0 +1,188 @@ +/* $OpenBSD: evutil.h,v 1.1 2010/04/21 20:02:40 nicm Exp $ */ + +/* + * Copyright (c) 2007 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _EVUTIL_H_ +#define _EVUTIL_H_ + +/** @file evutil.h + + Common convenience functions for cross-platform portability and + related socket manipulations. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event-config.h> +#ifdef _EVENT_HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef _EVENT_HAVE_STDINT_H +#include <stdint.h> +#elif defined(_EVENT_HAVE_INTTYPES_H) +#include <inttypes.h> +#endif +#ifdef _EVENT_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <stdarg.h> + +#ifdef _EVENT_HAVE_UINT64_T +#define ev_uint64_t uint64_t +#define ev_int64_t int64_t +#elif defined(WIN32) +#define ev_uint64_t unsigned __int64 +#define ev_int64_t signed __int64 +#elif _EVENT_SIZEOF_LONG_LONG == 8 +#define ev_uint64_t unsigned long long +#define ev_int64_t long long +#elif _EVENT_SIZEOF_LONG == 8 +#define ev_uint64_t unsigned long +#define ev_int64_t long +#else +#error "No way to define ev_uint64_t" +#endif + +#ifdef _EVENT_HAVE_UINT32_T +#define ev_uint32_t uint32_t +#elif defined(WIN32) +#define ev_uint32_t unsigned int +#elif _EVENT_SIZEOF_LONG == 4 +#define ev_uint32_t unsigned long +#elif _EVENT_SIZEOF_INT == 4 +#define ev_uint32_t unsigned int +#else +#error "No way to define ev_uint32_t" +#endif + +#ifdef _EVENT_HAVE_UINT16_T +#define ev_uint16_t uint16_t +#elif defined(WIN32) +#define ev_uint16_t unsigned short +#elif _EVENT_SIZEOF_INT == 2 +#define ev_uint16_t unsigned int +#elif _EVENT_SIZEOF_SHORT == 2 +#define ev_uint16_t unsigned short +#else +#error "No way to define ev_uint16_t" +#endif + +#ifdef _EVENT_HAVE_UINT8_T +#define ev_uint8_t uint8_t +#else +#define ev_uint8_t unsigned char +#endif + +int evutil_socketpair(int d, int type, int protocol, int sv[2]); +int evutil_make_socket_nonblocking(int sock); +#ifdef WIN32 +#define EVUTIL_CLOSESOCKET(s) closesocket(s) +#else +#define EVUTIL_CLOSESOCKET(s) close(s) +#endif + +#ifdef WIN32 +#define EVUTIL_SOCKET_ERROR() WSAGetLastError() +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { WSASetLastError(errcode); } while (0) +#else +#define EVUTIL_SOCKET_ERROR() (errno) +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { errno = (errcode); } while (0) +#endif + +/* + * Manipulation functions for struct timeval + */ +#ifdef _EVENT_HAVE_TIMERADD +#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) +#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) +#else +#define evutil_timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define evutil_timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* !_EVENT_HAVE_HAVE_TIMERADD */ + +#ifdef _EVENT_HAVE_TIMERCLEAR +#define evutil_timerclear(tvp) timerclear(tvp) +#else +#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif + +#define evutil_timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) + +#ifdef _EVENT_HAVE_TIMERISSET +#define evutil_timerisset(tvp) timerisset(tvp) +#else +#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif + + +/* big-int related functions */ +ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); + + +#ifdef _EVENT_HAVE_GETTIMEOFDAY +#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#else +struct timezone; +int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif + ; +int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap); + +#ifdef __cplusplus +} +#endif + +#endif /* _EVUTIL_H_ */ diff --git a/lib/libevent/kqueue.c b/lib/libevent/kqueue.c index 560d6b120c5..33aa46d1c29 100644 --- a/lib/libevent/kqueue.c +++ b/lib/libevent/kqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kqueue.c,v 1.24 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: kqueue.c,v 1.25 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> @@ -30,11 +30,13 @@ #include "config.h" #endif +#define _GNU_SOURCE 1 + #include <sys/types.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else -#include <sys/_time.h> +#include <sys/_libevent_time.h> #endif #include <sys/queue.h> #include <sys/event.h> @@ -44,20 +46,22 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <assert.h> #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif /* Some platforms apparently define the udata field of struct kevent as - * ntptr_t, whereas others define it as void*. There doesn't seem to be an + * intptr_t, whereas others define it as void*. There doesn't seem to be an * easy way to tell them apart via autoconf, so we need to use OS macros. */ #if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) -#define PTR_TO_UDATA(x) ((intptr_t)(x)) +#define PTR_TO_UDATA(x) ((intptr_t)(x)) #else -#define PTR_TO_UDATA(x) (x) +#define PTR_TO_UDATA(x) (x) #endif #include "event.h" +#include "event-internal.h" #include "log.h" #define EVLIST_X_KQINKERNEL 0x1000 @@ -68,43 +72,44 @@ struct kqop { struct kevent *changes; int nchanges; struct kevent *events; + struct event_list evsigevents[NSIG]; int nevents; int kq; + pid_t pid; }; -void *kq_init (struct event_base *); -int kq_add (void *, struct event *); -int kq_del (void *, struct event *); -int kq_recalc (struct event_base *, void *, int); -int kq_dispatch (struct event_base *, void *, struct timeval *); -int kq_insert (struct kqop *, struct kevent *); -void kq_dealloc (struct event_base *, void *); +static void *kq_init (struct event_base *); +static int kq_add (void *, struct event *); +static int kq_del (void *, struct event *); +static int kq_dispatch (struct event_base *, void *, struct timeval *); +static int kq_insert (struct kqop *, struct kevent *); +static void kq_dealloc (struct event_base *, void *); const struct eventop kqops = { "kqueue", kq_init, kq_add, kq_del, - kq_recalc, kq_dispatch, - kq_dealloc + kq_dealloc, + 1 /* need reinit */ }; -void * +static void * kq_init(struct event_base *base) { - int kq; + int i, kq; struct kqop *kqueueop; /* Disable kqueue when this environment variable is set */ - if (!issetugid() && getenv("EVENT_NOKQUEUE")) + if (evutil_getenv("EVENT_NOKQUEUE")) return (NULL); if (!(kqueueop = calloc(1, sizeof(struct kqop)))) return (NULL); /* Initalize the kernel queue */ - + if ((kq = kqueue()) == -1) { event_warn("kqueue"); free (kqueueop); @@ -113,6 +118,8 @@ kq_init(struct event_base *base) kqueueop->kq = kq; + kqueueop->pid = getpid(); + /* Initalize fields */ kqueueop->changes = calloc(NEVENT, sizeof(struct kevent)); if (kqueueop->changes == NULL) { @@ -127,11 +134,16 @@ kq_init(struct event_base *base) } kqueueop->nevents = NEVENT; + /* we need to keep track of multiple events per signal */ + for (i = 0; i < NSIG; ++i) { + TAILQ_INIT(&kqueueop->evsigevents[i]); + } + /* Check for Mac OS X kqueue bug. */ kqueueop->changes[0].ident = -1; kqueueop->changes[0].filter = EVFILT_READ; kqueueop->changes[0].flags = EV_ADD; - /* + /* * If kqueue works, then kevent will succeed, and it will * stick an error in events[0]. If kqueue is broken, then * kevent will fail. @@ -151,13 +163,7 @@ kq_init(struct event_base *base) return (kqueueop); } -int -kq_recalc(struct event_base *base, void *arg, int max) -{ - return (0); -} - -int +static int kq_insert(struct kqop *kqop, struct kevent *kev) { int nevents = kqop->nevents; @@ -195,9 +201,9 @@ kq_insert(struct kqop *kqop, struct kevent *kev) memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent)); event_debug(("%s: fd %d %s%s", - __func__, kev->ident, - kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", - kev->flags == EV_DELETE ? " (del)" : "")); + __func__, (int)kev->ident, + kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", + kev->flags == EV_DELETE ? " (del)" : "")); return (0); } @@ -208,7 +214,7 @@ kq_sighandler(int sig) /* Do nothing here */ } -int +static int kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) { struct kqop *kqop = arg; @@ -241,7 +247,7 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) int which = 0; if (events[i].flags & EV_ERROR) { - /* + /* * Error messages that can happen, when a delete fails. * EBADF happens when the file discriptor has been * closed, @@ -261,8 +267,6 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) return (-1); } - ev = (struct event *)events[i].udata; - if (events[i].filter == EVFILT_READ) { which |= EV_READ; } else if (events[i].filter == EVFILT_WRITE) { @@ -274,18 +278,27 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) if (!which) continue; + if (events[i].filter == EVFILT_SIGNAL) { + struct event_list *head = + (struct event_list *)events[i].udata; + TAILQ_FOREACH(ev, head, ev_signal_next) { + event_active(ev, which, events[i].data); + } + } else { + ev = (struct event *)events[i].udata; + if (!(ev->ev_events & EV_PERSIST)) - event_del(ev); + ev->ev_flags &= ~EVLIST_X_KQINKERNEL; - event_active(ev, which, - ev->ev_events & EV_SIGNAL ? events[i].data : 1); + event_active(ev, which, 1); + } } return (0); } -int +static int kq_add(void *arg, struct event *ev) { struct kqop *kqop = arg; @@ -294,20 +307,29 @@ kq_add(void *arg, struct event *ev) if (ev->ev_events & EV_SIGNAL) { int nsignal = EVENT_SIGNAL(ev); - memset(&kev, 0, sizeof(kev)); - kev.ident = nsignal; - kev.filter = EVFILT_SIGNAL; - kev.flags = EV_ADD; - if (!(ev->ev_events & EV_PERSIST)) - kev.flags |= EV_ONESHOT; - kev.udata = PTR_TO_UDATA(ev); - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - if (signal(nsignal, kq_sighandler) == SIG_ERR) - return (-1); + assert(nsignal >= 0 && nsignal < NSIG); + if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { + struct timespec timeout = { 0, 0 }; + + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_ADD; + kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]); + + /* Be ready for the signal if it is sent any + * time between now and the next call to + * kq_dispatch. */ + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) + return (-1); + + if (_evsignal_set_handler(ev->ev_base, nsignal, + kq_sighandler) == -1) + return (-1); + } + TAILQ_INSERT_TAIL(&kqop->evsigevents[nsignal], ev, + ev_signal_next); ev->ev_flags |= EVLIST_X_KQINKERNEL; return (0); } @@ -324,7 +346,7 @@ kq_add(void *arg, struct event *ev) if (!(ev->ev_events & EV_PERSIST)) kev.flags |= EV_ONESHOT; kev.udata = PTR_TO_UDATA(ev); - + if (kq_insert(kqop, &kev) == -1) return (-1); @@ -339,7 +361,7 @@ kq_add(void *arg, struct event *ev) if (!(ev->ev_events & EV_PERSIST)) kev.flags |= EV_ONESHOT; kev.udata = PTR_TO_UDATA(ev); - + if (kq_insert(kqop, &kev) == -1) return (-1); @@ -349,7 +371,7 @@ kq_add(void *arg, struct event *ev) return (0); } -int +static int kq_del(void *arg, struct event *ev) { struct kqop *kqop = arg; @@ -360,17 +382,26 @@ kq_del(void *arg, struct event *ev) if (ev->ev_events & EV_SIGNAL) { int nsignal = EVENT_SIGNAL(ev); - - memset(&kev, 0, sizeof(kev)); - kev.ident = nsignal; - kev.filter = EVFILT_SIGNAL; - kev.flags = EV_DELETE; - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - if (signal(nsignal, SIG_DFL) == SIG_ERR) - return (-1); + struct timespec timeout = { 0, 0 }; + + assert(nsignal >= 0 && nsignal < NSIG); + TAILQ_REMOVE(&kqop->evsigevents[nsignal], ev, ev_signal_next); + if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_DELETE; + + /* Because we insert signal events + * immediately, we need to delete them + * immediately, too */ + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) + return (-1); + + if (_evsignal_restore_handler(ev->ev_base, + nsignal) == -1) + return (-1); + } ev->ev_flags &= ~EVLIST_X_KQINKERNEL; return (0); @@ -381,7 +412,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); @@ -393,7 +424,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); @@ -403,7 +434,7 @@ kq_del(void *arg, struct event *ev) return (0); } -void +static void kq_dealloc(struct event_base *base, void *arg) { struct kqop *kqop = arg; @@ -412,7 +443,7 @@ kq_dealloc(struct event_base *base, void *arg) free(kqop->changes); if (kqop->events) free(kqop->events); - if (kqop->kq) + if (kqop->kq >= 0 && kqop->pid == getpid()) close(kqop->kq); memset(kqop, 0, sizeof(struct kqop)); free(kqop); diff --git a/lib/libevent/log.c b/lib/libevent/log.c index 239c47842c9..07b65e85ba6 100644 --- a/lib/libevent/log.c +++ b/lib/libevent/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.5 2007/03/19 15:12:49 millert Exp $ */ +/* $OpenBSD: log.c,v 1.6 2010/04/21 20:02:40 nicm Exp $ */ /* * log.c @@ -45,14 +45,12 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> #undef WIN32_LEAN_AND_MEAN -#include "misc.h" #endif #include <sys/types.h> -#include <sys/tree.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else -#include <sys/_time.h> +#include <sys/_libevent_time.h> #endif #include <stdio.h> #include <stdlib.h> @@ -62,47 +60,17 @@ #include "event.h" #include "log.h" +#include "evutil.h" static void _warn_helper(int severity, int log_errno, const char *fmt, va_list ap); static void event_log(int severity, const char *msg); -static int -event_vsnprintf(char *str, size_t size, const char *format, va_list args) -{ - int r; - if (size == 0) - return -1; -#ifdef WIN32 - r = _vsnprintf(str, size, format, args); -#else - r = vsnprintf(str, size, format, args); -#endif - str[size-1] = '\0'; - if (r < 0 || ((size_t)r) >= size) { - /* different platforms behave differently on overflow; - * handle both kinds. */ - return -1; - } - return r; -} - -static int -event_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int r; - va_start(ap, format); - r = event_vsnprintf(str, size, format, ap); - va_end(ap); - return r; -} - void event_err(int eval, const char *fmt, ...) { va_list ap; - + va_start(ap, fmt); _warn_helper(_EVENT_LOG_ERR, errno, fmt, ap); va_end(ap); @@ -113,7 +81,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 +91,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 +102,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 +112,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 +122,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); @@ -167,14 +135,14 @@ _warn_helper(int severity, int log_errno, const char *fmt, va_list ap) size_t len; if (fmt != NULL) - event_vsnprintf(buf, sizeof(buf), fmt, ap); + evutil_vsnprintf(buf, sizeof(buf), fmt, ap); else buf[0] = '\0'; if (log_errno >= 0) { len = strlen(buf); if (len < sizeof(buf) - 3) { - event_snprintf(buf + len, sizeof(buf) - len, ": %s", + evutil_snprintf(buf + len, sizeof(buf) - len, ": %s", strerror(log_errno)); } } diff --git a/lib/libevent/log.h b/lib/libevent/log.h index d39e0c2b541..2f350940947 100644 --- a/lib/libevent/log.h +++ b/lib/libevent/log.h @@ -1,4 +1,4 @@ -/* $OpenBSD: log.h,v 1.5 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: log.h,v 1.6 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu> @@ -29,12 +29,18 @@ #ifndef _LOG_H_ #define _LOG_H_ -void event_err(int eval, const char *fmt, ...); -void event_warn(const char *fmt, ...); -void event_errx(int eval, const char *fmt, ...); -void event_warnx(const char *fmt, ...); -void event_msgx(const char *fmt, ...); -void _event_debugx(const char *fmt, ...); +#ifdef __GNUC__ +#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b))) +#else +#define EV_CHECK_FMT(a,b) +#endif + +void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3); +void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2); +void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3); +void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2); +void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2); +void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2); #ifdef USE_DEBUG #define event_debug(x) _event_debugx x @@ -42,4 +48,6 @@ void _event_debugx(const char *fmt, ...); #define event_debug(x) do {;} while (0) #endif +#undef EV_CHECK_FMT + #endif diff --git a/lib/libevent/min_heap.h b/lib/libevent/min_heap.h new file mode 100644 index 00000000000..d1023545fa1 --- /dev/null +++ b/lib/libevent/min_heap.h @@ -0,0 +1,151 @@ +/* $OpenBSD: min_heap.h,v 1.1 2010/04/21 20:02:40 nicm Exp $ */ + +/* + * Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MIN_HEAP_H_ +#define _MIN_HEAP_H_ + +#include "event.h" +#include "evutil.h" + +typedef struct min_heap +{ + struct event** p; + unsigned n, a; +} min_heap_t; + +static inline void min_heap_ctor(min_heap_t* s); +static inline void min_heap_dtor(min_heap_t* s); +static inline void min_heap_elem_init(struct event* e); +static inline int min_heap_elem_greater(struct event *a, struct event *b); +static inline int min_heap_empty(min_heap_t* s); +static inline unsigned min_heap_size(min_heap_t* s); +static inline struct event* min_heap_top(min_heap_t* s); +static inline int min_heap_reserve(min_heap_t* s, unsigned n); +static inline int min_heap_push(min_heap_t* s, struct event* e); +static inline struct event* min_heap_pop(min_heap_t* s); +static inline int min_heap_erase(min_heap_t* s, struct event* e); +static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e); +static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e); + +int min_heap_elem_greater(struct event *a, struct event *b) +{ + return evutil_timercmp(&a->ev_timeout, &b->ev_timeout, >); +} + +void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; } +void min_heap_dtor(min_heap_t* s) { free(s->p); } +void min_heap_elem_init(struct event* e) { e->min_heap_idx = -1; } +int min_heap_empty(min_heap_t* s) { return 0u == s->n; } +unsigned min_heap_size(min_heap_t* s) { return s->n; } +struct event* min_heap_top(min_heap_t* s) { return s->n ? *s->p : 0; } + +int min_heap_push(min_heap_t* s, struct event* e) +{ + if(min_heap_reserve(s, s->n + 1)) + return -1; + min_heap_shift_up_(s, s->n++, e); + return 0; +} + +struct event* min_heap_pop(min_heap_t* s) +{ + if(s->n) + { + struct event* e = *s->p; + min_heap_shift_down_(s, 0u, s->p[--s->n]); + e->min_heap_idx = -1; + return e; + } + return 0; +} + +int min_heap_erase(min_heap_t* s, struct event* e) +{ + if(((unsigned int)-1) != e->min_heap_idx) + { + struct event *last = s->p[--s->n]; + unsigned parent = (e->min_heap_idx - 1) / 2; + /* we replace e with the last element in the heap. We might need to + shift it upward if it is less than its parent, or downward if it is + greater than one or both its children. Since the children are known + to be less than the parent, it can't need to shift both up and + down. */ + if (e->min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last)) + min_heap_shift_up_(s, e->min_heap_idx, last); + else + min_heap_shift_down_(s, e->min_heap_idx, last); + e->min_heap_idx = -1; + return 0; + } + return -1; +} + +int min_heap_reserve(min_heap_t* s, unsigned n) +{ + if(s->a < n) + { + struct event** p; + unsigned a = s->a ? s->a * 2 : 8; + if(a < n) + a = n; + if(!(p = (struct event**)realloc(s->p, a * sizeof *p))) + return -1; + s->p = p; + s->a = a; + } + return 0; +} + +void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e) +{ + unsigned parent = (hole_index - 1) / 2; + while(hole_index && min_heap_elem_greater(s->p[parent], e)) + { + (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index; + hole_index = parent; + parent = (hole_index - 1) / 2; + } + (s->p[hole_index] = e)->min_heap_idx = hole_index; +} + +void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e) +{ + unsigned min_child = 2 * (hole_index + 1); + while(min_child <= s->n) + { + min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]); + if(!(min_heap_elem_greater(e, s->p[min_child]))) + break; + (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index; + hole_index = min_child; + min_child = 2 * (hole_index + 1); + } + min_heap_shift_up_(s, hole_index, e); +} + +#endif /* _MIN_HEAP_H_ */ diff --git a/lib/libevent/poll.c b/lib/libevent/poll.c index 612b36370a3..3ce103681b6 100644 --- a/lib/libevent/poll.c +++ b/lib/libevent/poll.c @@ -1,4 +1,4 @@ -/* $OpenBSD: poll.c,v 1.14 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: poll.c,v 1.15 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu> @@ -34,10 +34,9 @@ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else -#include <sys/_time.h> +#include <sys/_libevent_time.h> #endif #include <sys/queue.h> -#include <sys/tree.h> #include <poll.h> #include <signal.h> #include <stdio.h> @@ -66,30 +65,29 @@ struct pollop { * "no entry." */ }; -void *poll_init (struct event_base *); -int poll_add (void *, struct event *); -int poll_del (void *, struct event *); -int poll_recalc (struct event_base *, void *, int); -int poll_dispatch (struct event_base *, void *, struct timeval *); -void poll_dealloc (struct event_base *, void *); +static void *poll_init (struct event_base *); +static int poll_add (void *, struct event *); +static int poll_del (void *, struct event *); +static int poll_dispatch (struct event_base *, void *, struct timeval *); +static void poll_dealloc (struct event_base *, void *); const struct eventop pollops = { "poll", poll_init, poll_add, poll_del, - poll_recalc, poll_dispatch, - poll_dealloc + poll_dealloc, + 0 }; -void * +static void * poll_init(struct event_base *base) { struct pollop *pollop; /* Disable poll when this environment variable is set */ - if (!issetugid() && getenv("EVENT_NOPOLL")) + if (evutil_getenv("EVENT_NOPOLL")) return (NULL); if (!(pollop = calloc(1, sizeof(struct pollop)))) @@ -100,17 +98,6 @@ poll_init(struct event_base *base) return (pollop); } -/* - * Called with the highest fd that we know about. If it is 0, completely - * recalculate everything. - */ - -int -poll_recalc(struct event_base *base, void *arg, int max) -{ - return (0); -} - #ifdef CHECK_INVARIANTS static void poll_check_ok(struct pollop *pop) @@ -145,10 +132,10 @@ poll_check_ok(struct pollop *pop) #define poll_check_ok(pop) #endif -int +static int poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) { - int res, i, msec = -1, nfds; + int res, i, j, msec = -1, nfds; struct pollop *pop = arg; poll_check_ok(pop); @@ -173,12 +160,16 @@ poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) event_debug(("%s: poll reports %d", __func__, res)); - if (res == 0) + if (res == 0 || nfds == 0) return (0); - for (i = 0; i < nfds; i++) { - int what = pop->event_set[i].revents; + i = random() % nfds; + for (j = 0; j < nfds; j++) { struct event *r_ev = NULL, *w_ev = NULL; + int what; + if (++i == nfds) + i = 0; + what = pop->event_set[i].revents; if (!what) continue; @@ -200,13 +191,9 @@ poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) continue; if (r_ev && (res & r_ev->ev_events)) { - if (!(r_ev->ev_events & EV_PERSIST)) - event_del(r_ev); event_active(r_ev, res & r_ev->ev_events, 1); } if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { - if (!(w_ev->ev_events & EV_PERSIST)) - event_del(w_ev); event_active(w_ev, res & w_ev->ev_events, 1); } } @@ -214,7 +201,7 @@ poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) return (0); } -int +static int poll_add(void *arg, struct event *ev) { struct pollop *pop = arg; @@ -319,7 +306,7 @@ poll_add(void *arg, struct event *ev) * Nothing to be done here. */ -int +static int poll_del(void *arg, struct event *ev) { struct pollop *pop = arg; @@ -357,7 +344,7 @@ poll_del(void *arg, struct event *ev) --pop->nfds; if (i != pop->nfds) { - /* + /* * Shift the last pollfd down into the now-unoccupied * position. */ @@ -372,7 +359,7 @@ poll_del(void *arg, struct event *ev) return (0); } -void +static void poll_dealloc(struct event_base *base, void *arg) { struct pollop *pop = arg; diff --git a/lib/libevent/select.c b/lib/libevent/select.c index de55ae057fe..709c794387f 100644 --- a/lib/libevent/select.c +++ b/lib/libevent/select.c @@ -1,4 +1,4 @@ -/* $OpenBSD: select.c,v 1.14 2008/05/02 06:09:11 brad Exp $ */ +/* $OpenBSD: select.c,v 1.15 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> @@ -34,13 +34,12 @@ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else -#include <sys/_time.h> +#include <sys/_libevent_time.h> #endif #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif #include <sys/queue.h> -#include <sys/tree.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -52,6 +51,7 @@ #endif #include "event.h" +#include "evutil.h" #include "event-internal.h" #include "evsignal.h" #include "log.h" @@ -60,6 +60,13 @@ #define howmany(x, y) (((x)+((y)-1))/(y)) #endif +#ifndef _EVENT_HAVE_FD_MASK +/* This type is mandatory, but Android doesn't define it. */ +#undef NFDBITS +#define NFDBITS (sizeof(long)*8) +typedef unsigned long fd_mask; +#endif + struct selectop { int event_fds; /* Highest fd in fd set */ int event_fdsz; @@ -71,32 +78,31 @@ struct selectop { struct event **event_w_by_fd; }; -void *select_init (struct event_base *); -int select_add (void *, struct event *); -int select_del (void *, struct event *); -int select_recalc (struct event_base *, void *, int); -int select_dispatch (struct event_base *, void *, struct timeval *); -void select_dealloc (struct event_base *, void *); +static void *select_init (struct event_base *); +static int select_add (void *, struct event *); +static int select_del (void *, struct event *); +static int select_dispatch (struct event_base *, void *, struct timeval *); +static void select_dealloc (struct event_base *, void *); const struct eventop selectops = { "select", select_init, select_add, select_del, - select_recalc, select_dispatch, - select_dealloc + select_dealloc, + 0 }; static int select_resize(struct selectop *sop, int fdsz); -void * +static void * select_init(struct event_base *base) { struct selectop *sop; /* Disable select when this environment variable is set */ - if (!issetugid() && getenv("EVENT_NOSELECT")) + if (evutil_getenv("EVENT_NOSELECT")) return (NULL); if (!(sop = calloc(1, sizeof(struct selectop)))) @@ -136,25 +142,10 @@ check_selectop(struct selectop *sop) #define check_selectop(sop) do { (void) sop; } while (0) #endif -/* - * Called with the highest fd that we know about. If it is 0, completely - * recalculate everything. - */ - -int -select_recalc(struct event_base *base, void *arg, int max) -{ - struct selectop *sop = arg; - - check_selectop(sop); - - return (0); -} - -int +static int select_dispatch(struct event_base *base, void *arg, struct timeval *tv) { - int res, i; + int res, i, j; struct selectop *sop = arg; check_selectop(sop); @@ -184,8 +175,12 @@ select_dispatch(struct event_base *base, void *arg, struct timeval *tv) event_debug(("%s: select reports %d", __func__, res)); check_selectop(sop); - for (i = 0; i <= sop->event_fds; ++i) { + i = random() % (sop->event_fds+1); + for (j = 0; j <= sop->event_fds; ++j) { struct event *r_ev = NULL, *w_ev = NULL; + if (++i >= sop->event_fds+1) + i = 0; + res = 0; if (FD_ISSET(i, sop->event_readset_out)) { r_ev = sop->event_r_by_fd[i]; @@ -196,13 +191,9 @@ select_dispatch(struct event_base *base, void *arg, struct timeval *tv) res |= EV_WRITE; } if (r_ev && (res & r_ev->ev_events)) { - if (!(r_ev->ev_events & EV_PERSIST)) - event_del(r_ev); event_active(r_ev, res & r_ev->ev_events, 1); } if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { - if (!(w_ev->ev_events & EV_PERSIST)) - event_del(w_ev); event_active(w_ev, res & w_ev->ev_events, 1); } } @@ -271,7 +262,7 @@ select_resize(struct selectop *sop, int fdsz) } -int +static int select_add(void *arg, struct event *ev) { struct selectop *sop = arg; @@ -321,7 +312,7 @@ select_add(void *arg, struct event *ev) * Nothing to be done here. */ -int +static int select_del(void *arg, struct event *ev) { struct selectop *sop = arg; @@ -349,7 +340,7 @@ select_del(void *arg, struct event *ev) return (0); } -void +static void select_dealloc(struct event_base *base, void *arg) { struct selectop *sop = arg; diff --git a/lib/libevent/shlib_version b/lib/libevent/shlib_version index b52599a164f..012c14171d3 100644 --- a/lib/libevent/shlib_version +++ b/lib/libevent/shlib_version @@ -1,2 +1,2 @@ -major=2 +major=3 minor=0 diff --git a/lib/libevent/signal.c b/lib/libevent/signal.c index ee9c57552fd..c5ce41596dd 100644 --- a/lib/libevent/signal.c +++ b/lib/libevent/signal.c @@ -1,4 +1,4 @@ -/* $OpenBSD: signal.c,v 1.13 2010/01/10 00:36:35 guenther Exp $ */ +/* $OpenBSD: signal.c,v 1.14 2010/04/21 20:02:40 nicm Exp $ */ /* * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> @@ -30,20 +30,27 @@ #include "config.h" #endif +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include <winsock2.h> +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#endif #include <sys/types.h> -#include <sys/tree.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> -#else -#include <sys/_time.h> #endif #include <sys/queue.h> +#ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> +#endif #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_UNISTD_H #include <unistd.h> +#endif #include <errno.h> #ifdef HAVE_FCNTL_H #include <fcntl.h> @@ -53,6 +60,7 @@ #include "event.h" #include "event-internal.h" #include "evsignal.h" +#include "evutil.h" #include "log.h" struct event_base *evsignal_base = NULL; @@ -63,14 +71,16 @@ static void evsignal_handler(int sig); static void evsignal_cb(int fd, short what, void *arg) { - static char signals[100]; - struct event *ev = arg; + static char signals[1]; +#ifdef WIN32 + SSIZE_T n; +#else ssize_t n; +#endif - n = read(fd, signals, sizeof(signals)); + n = recv(fd, signals, sizeof(signals), 0); if (n == -1) event_err(1, "%s: read", __func__); - event_add(ev, NULL); } #ifdef HAVE_SETFD @@ -82,68 +92,195 @@ evsignal_cb(int fd, short what, void *arg) #define FD_CLOSEONEXEC(x) #endif -void +int evsignal_init(struct event_base *base) { - /* + int i; + + /* * Our signal handler is going to write to one end of the socket * pair to wake up our event loop. The event loop then scans for * signals that got delivered. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) + if (evutil_socketpair( + AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) { +#ifdef WIN32 + /* Make this nonfatal on win32, where sometimes people + have localhost firewalled. */ + event_warn("%s: socketpair", __func__); +#else event_err(1, "%s: socketpair", __func__); +#endif + return -1; + } FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]); FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]); + base->sig.sh_old = NULL; + base->sig.sh_old_max = 0; base->sig.evsignal_caught = 0; memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG); + /* initialize the queues for all events */ + for (i = 0; i < NSIG; ++i) + TAILQ_INIT(&base->sig.evsigevents[i]); - fcntl(base->sig.ev_signal_pair[0], F_SETFL, O_NONBLOCK); + evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]); - event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], EV_READ, - evsignal_cb, &base->sig.ev_signal); + event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], + EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal); base->sig.ev_signal.ev_base = base; base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL; + + return 0; +} + +/* Helper: set the signal handler for evsignal to handler in base, so that + * we can restore the original handler when we clear the current one. */ +int +_evsignal_set_handler(struct event_base *base, + int evsignal, void (*handler)(int)) +{ +#ifdef HAVE_SIGACTION + struct sigaction sa; +#else + ev_sighandler_t sh; +#endif + struct evsignal_info *sig = &base->sig; + void *p; + + /* + * resize saved signal handler array up to the highest signal number. + * a dynamic array is used to keep footprint on the low side. + */ + if (evsignal >= sig->sh_old_max) { + int new_max = evsignal + 1; + event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing", + __func__, evsignal, sig->sh_old_max)); + p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old)); + if (p == NULL) { + event_warn("realloc"); + return (-1); + } + + memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old), + 0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old)); + + sig->sh_old_max = new_max; + sig->sh_old = p; + } + + /* allocate space for previous handler out of dynamic array */ + sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]); + if (sig->sh_old[evsignal] == NULL) { + event_warn("malloc"); + return (-1); + } + + /* save previous handler and setup new handler */ +#ifdef HAVE_SIGACTION + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sa.sa_flags |= SA_RESTART; + sigfillset(&sa.sa_mask); + + if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) { + event_warn("sigaction"); + free(sig->sh_old[evsignal]); + return (-1); + } +#else + if ((sh = signal(evsignal, handler)) == SIG_ERR) { + event_warn("signal"); + free(sig->sh_old[evsignal]); + return (-1); + } + *sig->sh_old[evsignal] = sh; +#endif + + return (0); } int evsignal_add(struct event *ev) { int evsignal; - struct sigaction sa; struct event_base *base = ev->ev_base; + struct evsignal_info *sig = &ev->ev_base->sig; if (ev->ev_events & (EV_READ|EV_WRITE)) event_errx(1, "%s: EV_SIGNAL incompatible use", __func__); evsignal = EVENT_SIGNAL(ev); + assert(evsignal >= 0 && evsignal < NSIG); + if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { + event_debug(("%s: %p: changing signal handler", __func__, ev)); + if (_evsignal_set_handler( + base, evsignal, evsignal_handler) == -1) + return (-1); - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = evsignal_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags |= SA_RESTART; - /* catch signals if they happen quickly */ - evsignal_base = base; - - if (sigaction(evsignal, &sa, NULL) == -1) - return (-1); + /* catch signals if they happen quickly */ + evsignal_base = base; - if (!base->sig.ev_signal_added) { - base->sig.ev_signal_added = 1; - event_add(&base->sig.ev_signal, NULL); + if (!sig->ev_signal_added) { + if (event_add(&sig->ev_signal, NULL)) + return (-1); + sig->ev_signal_added = 1; + } } + /* multiple events may listen to the same signal */ + TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next); + return (0); } int +_evsignal_restore_handler(struct event_base *base, int evsignal) +{ + int ret = 0; + struct evsignal_info *sig = &base->sig; +#ifdef HAVE_SIGACTION + struct sigaction *sh; +#else + ev_sighandler_t *sh; +#endif + + /* restore previous handler */ + sh = sig->sh_old[evsignal]; + sig->sh_old[evsignal] = NULL; +#ifdef HAVE_SIGACTION + if (sigaction(evsignal, sh, NULL) == -1) { + event_warn("sigaction"); + ret = -1; + } +#else + if (signal(evsignal, *sh) == SIG_ERR) { + event_warn("signal"); + ret = -1; + } +#endif + free(sh); + + return ret; +} + +int evsignal_del(struct event *ev) { - struct sigaction sa; + struct event_base *base = ev->ev_base; + struct evsignal_info *sig = &base->sig; + int evsignal = EVENT_SIGNAL(ev); - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_DFL; - return (sigaction(EVENT_SIGNAL(ev), &sa, NULL)); + assert(evsignal >= 0 && evsignal < NSIG); + + /* multiple events may listen to the same signal */ + TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next); + + if (!TAILQ_EMPTY(&sig->evsigevents[evsignal])) + return (0); + + event_debug(("%s: %p: restoring signal handler", __func__, ev)); + + return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev))); } static void @@ -151,9 +288,9 @@ evsignal_handler(int sig) { int save_errno = errno; - if(evsignal_base == NULL) { + if (evsignal_base == NULL) { event_warn( - "%s: received signal %s, but have no base configured", + "%s: received signal %d, but have no base configured", __func__, sig); return; } @@ -161,40 +298,60 @@ evsignal_handler(int sig) evsignal_base->sig.evsigcaught[sig]++; evsignal_base->sig.evsignal_caught = 1; +#ifndef HAVE_SIGACTION + signal(sig, evsignal_handler); +#endif + /* Wake up our notification mechanism */ - write(evsignal_base->sig.ev_signal_pair[0], "a", 1); + send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); errno = save_errno; } void evsignal_process(struct event_base *base) { - struct event *ev; + struct evsignal_info *sig = &base->sig; + struct event *ev, *next_ev; sig_atomic_t ncalls; - + int i; + base->sig.evsignal_caught = 0; - TAILQ_FOREACH(ev, &base->sig.signalqueue, ev_signal_next) { - ncalls = base->sig.evsigcaught[EVENT_SIGNAL(ev)]; - if (ncalls) { + for (i = 1; i < NSIG; ++i) { + ncalls = sig->evsigcaught[i]; + if (ncalls == 0) + continue; + sig->evsigcaught[i] -= ncalls; + + for (ev = TAILQ_FIRST(&sig->evsigevents[i]); + ev != NULL; ev = next_ev) { + next_ev = TAILQ_NEXT(ev, ev_signal_next); if (!(ev->ev_events & EV_PERSIST)) event_del(ev); event_active(ev, EV_SIGNAL, ncalls); - base->sig.evsigcaught[EVENT_SIGNAL(ev)] = 0; } + } } void evsignal_dealloc(struct event_base *base) { - if(base->sig.ev_signal_added) { + int i = 0; + if (base->sig.ev_signal_added) { event_del(&base->sig.ev_signal); base->sig.ev_signal_added = 0; } - assert(TAILQ_EMPTY(&base->sig.signalqueue)); + for (i = 0; i < NSIG; ++i) { + if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL) + _evsignal_restore_handler(base, i); + } - close(base->sig.ev_signal_pair[0]); + EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]); base->sig.ev_signal_pair[0] = -1; - close(base->sig.ev_signal_pair[1]); + EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]); base->sig.ev_signal_pair[1] = -1; + base->sig.sh_old_max = 0; + + /* per index frees are handled in evsignal_del() */ + free(base->sig.sh_old); } |