Bump to libevent 2.1.8
[platform/upstream/libevent.git] / evdns.c
diff --git a/evdns.c b/evdns.c
index 60b1048..e9dbc35 100644 (file)
--- a/evdns.c
+++ b/evdns.c
  * Version: 0.1b
  */
 
-#include <sys/types.h>
 #include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
 
 #ifndef _FORTIFY_SOURCE
 #define _FORTIFY_SOURCE 3
 
 #include <string.h>
 #include <fcntl.h>
-#ifdef _EVENT_HAVE_SYS_TIME_H
+#ifdef EVENT__HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#ifdef _EVENT_HAVE_STDINT_H
+#ifdef EVENT__HAVE_STDINT_H
 #include <stdint.h>
 #endif
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#ifdef _EVENT_HAVE_UNISTD_H
+#ifdef EVENT__HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <limits.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <stdarg.h>
-#ifdef WIN32
+#ifdef _WIN32
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #ifndef _WIN32_IE
 #include "event2/event_struct.h"
 #include "event2/thread.h"
 
-#include "event2/bufferevent.h"
-#include "event2/bufferevent_struct.h"
-#include "bufferevent-internal.h"
-
 #include "defer-internal.h"
 #include "log-internal.h"
 #include "mm-internal.h"
 #include "ipv6-internal.h"
 #include "util-internal.h"
 #include "evthread-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
 #include <ctype.h>
 #include <winsock2.h>
 #include <windows.h>
 #include <arpa/inet.h>
 #endif
 
-#ifdef _EVENT_HAVE_NETINET_IN6_H
+#ifdef EVENT__HAVE_NETINET_IN6_H
 #include <netinet/in6.h>
 #endif
 
-#define EVDNS_LOG_DEBUG 0
-#define EVDNS_LOG_WARN 1
-#define EVDNS_LOG_MSG 2
+#define EVDNS_LOG_DEBUG EVENT_LOG_DEBUG
+#define EVDNS_LOG_WARN EVENT_LOG_WARN
+#define EVDNS_LOG_MSG EVENT_LOG_MSG
 
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 255
@@ -234,6 +232,10 @@ struct nameserver {
        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 */
        struct evdns_base *base;
+
+       /* Number of currently inflight requests: used
+        * to track when we should add/del the event. */
+       int requests_inflight;
 };
 
 
@@ -251,7 +253,7 @@ struct evdns_server_port {
        struct server_request *pending_replies;
        struct event_base *event_base;
 
-#ifndef _EVENT_DISABLE_THREAD_SUPPORT
+#ifndef EVENT__DISABLE_THREAD_SUPPORT
        void *lock;
 #endif
 };
@@ -353,9 +355,11 @@ struct evdns_base {
 
        TAILQ_HEAD(hosts_list, hosts_entry) hostsdb;
 
-#ifndef _EVENT_DISABLE_THREAD_SUPPORT
+#ifndef EVENT__DISABLE_THREAD_SUPPORT
        void *lock;
 #endif
+
+       int disable_when_inactive;
 };
 
 struct hosts_entry {
@@ -408,13 +412,14 @@ static int evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags,
 static int evdns_base_set_option_impl(struct evdns_base *base,
     const char *option, const char *val, int flags);
 static void evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests);
+static void evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg);
 
 static int strtoint(const char *const str);
 
-#ifdef _EVENT_DISABLE_THREAD_SUPPORT
-#define EVDNS_LOCK(base)  _EVUTIL_NIL_STMT
-#define EVDNS_UNLOCK(base) _EVUTIL_NIL_STMT
-#define ASSERT_LOCKED(base) _EVUTIL_NIL_STMT
+#ifdef EVENT__DISABLE_THREAD_SUPPORT
+#define EVDNS_LOCK(base)  EVUTIL_NIL_STMT_
+#define EVDNS_UNLOCK(base) EVUTIL_NIL_STMT_
+#define ASSERT_LOCKED(base) EVUTIL_NIL_STMT_
 #else
 #define EVDNS_LOCK(base)                       \
        EVLOCK_LOCK((base)->lock, 0)
@@ -424,17 +429,6 @@ static int strtoint(const char *const str);
        EVLOCK_ASSERT_LOCKED((base)->lock)
 #endif
 
-static void
-default_evdns_log_fn(int warning, const char *buf)
-{
-       if (warning == EVDNS_LOG_WARN)
-               event_warnx("[evdns] %s", buf);
-       else if (warning == EVDNS_LOG_MSG)
-               event_msgx("[evdns] %s", buf);
-       else
-               event_debug(("[evdns] %s", buf));
-}
-
 static evdns_debug_log_fn_type evdns_log_fn = NULL;
 
 void
@@ -449,28 +443,24 @@ evdns_set_log_fn(evdns_debug_log_fn_type fn)
 #define EVDNS_LOG_CHECK
 #endif
 
-static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK;
+static void evdns_log_(int severity, const char *fmt, ...) EVDNS_LOG_CHECK;
 static void
-_evdns_log(int warn, const char *fmt, ...)
+evdns_log_(int severity, const char *fmt, ...)
 {
        va_list args;
-       char buf[512];
-       if (!evdns_log_fn)
-               return;
        va_start(args,fmt);
-       evutil_vsnprintf(buf, sizeof(buf), fmt, args);
-       va_end(args);
        if (evdns_log_fn) {
-               if (warn == EVDNS_LOG_MSG)
-                       warn = EVDNS_LOG_WARN;
-               evdns_log_fn(warn, buf);
+               char buf[512];
+               int is_warn = (severity == EVDNS_LOG_WARN);
+               evutil_vsnprintf(buf, sizeof(buf), fmt, args);
+               evdns_log_fn(is_warn, buf);
        } else {
-               default_evdns_log_fn(warn, buf);
+               event_logv_(severity, NULL, fmt, args);
        }
-
+       va_end(args);
 }
 
-#define log _evdns_log
+#define log evdns_log_
 
 /* This walks the list of inflight requests to find the */
 /* one with a matching transaction id. Returns NULL on */
@@ -545,12 +535,23 @@ nameserver_probe_failed(struct nameserver *const ns) {
                char addrbuf[128];
                log(EVDNS_LOG_WARN,
                    "Error from libevent when adding timer event for %s",
-                   evutil_format_sockaddr_port(
+                   evutil_format_sockaddr_port_(
                            (struct sockaddr *)&ns->address,
                            addrbuf, sizeof(addrbuf)));
        }
 }
 
+static void
+request_swap_ns(struct request *req, struct nameserver *ns) {
+       if (ns && req->ns != ns) {
+               EVUTIL_ASSERT(req->ns->requests_inflight > 0);
+               req->ns->requests_inflight--;
+               ns->requests_inflight++;
+
+               req->ns = ns;
+       }
+}
+
 /* called when a nameserver has been deemed to have failed. For example, too */
 /* many packets have timed out etc */
 static void
@@ -566,7 +567,7 @@ nameserver_failed(struct nameserver *const ns, const char *msg) {
        if (!ns->state) return;
 
        log(EVDNS_LOG_MSG, "Nameserver %s has failed: %s",
-           evutil_format_sockaddr_port(
+           evutil_format_sockaddr_port_(
                    (struct sockaddr *)&ns->address,
                    addrbuf, sizeof(addrbuf)),
            msg);
@@ -584,7 +585,7 @@ nameserver_failed(struct nameserver *const ns, const char *msg) {
                &base->global_nameserver_probe_initial_timeout) < 0) {
                log(EVDNS_LOG_WARN,
                    "Error from libevent when adding timer event for %s",
-                   evutil_format_sockaddr_port(
+                   evutil_format_sockaddr_port_(
                            (struct sockaddr *)&ns->address,
                            addrbuf, sizeof(addrbuf)));
                /* ???? Do more? */
@@ -605,7 +606,7 @@ nameserver_failed(struct nameserver *const ns, const char *msg) {
                                if (req->tx_count == 0 && req->ns == ns) {
                                        /* still waiting to go out, can be moved */
                                        /* to another server */
-                                       req->ns = nameserver_pick(base);
+                                       request_swap_ns(req, nameserver_pick(base));
                                }
                                req = req->next;
                        } while (req != started_at);
@@ -620,7 +621,7 @@ nameserver_up(struct nameserver *const ns)
        ASSERT_LOCKED(ns->base);
        if (ns->state) return;
        log(EVDNS_LOG_MSG, "Nameserver %s is back up",
-           evutil_format_sockaddr_port(
+           evutil_format_sockaddr_port_(
                    (struct sockaddr *)&ns->address,
                    addrbuf, sizeof(addrbuf)));
        evtimer_del(&ns->timeout_event);
@@ -658,12 +659,20 @@ request_finished(struct request *const req, struct request **head, int free_hand
        if (was_inflight) {
                evtimer_del(&req->timeout_event);
                base->global_requests_inflight--;
+               req->ns->requests_inflight--;
        } else {
                base->global_requests_waiting--;
        }
        /* it was initialized during request_new / evtimer_assign */
        event_debug_unassign(&req->timeout_event);
 
+       if (req->ns &&
+           req->ns->requests_inflight == 0 &&
+           req->base->disable_when_inactive) {
+               event_del(&req->ns->event);
+               evtimer_del(&req->ns->timeout_event);
+       }
+
        if (!req->request_appended) {
                /* need to free the request data on it's own */
                mm_free(req->request);
@@ -710,7 +719,7 @@ request_reissue(struct request *req) {
        /* 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(req->base);
+       request_swap_ns(req, nameserver_pick(req->base));
        if (req->ns == last_ns) {
                /* ... but pick did return it */
                /* not a lot of point in trying again with the */
@@ -745,6 +754,8 @@ evdns_requests_pump_waiting_queue(struct evdns_base *base) {
                        return;
 
                /* move a request from the waiting queue to the inflight queue */
+               req->ns->requests_inflight++;
+
                evdns_request_remove(req, &base->req_waiting_head);
 
                base->global_requests_waiting--;
@@ -760,7 +771,7 @@ evdns_requests_pump_waiting_queue(struct evdns_base *base) {
 
 /* TODO(nickm) document */
 struct deferred_reply_callback {
-       struct deferred_cb deferred;
+       struct event_callback deferred;
        struct evdns_request *handle;
        u8 request_type;
        u8 have_reply;
@@ -771,7 +782,7 @@ struct deferred_reply_callback {
 };
 
 static void
-reply_run_callback(struct deferred_cb *d, void *user_pointer)
+reply_run_callback(struct event_callback *d, void *user_pointer)
 {
        struct deferred_reply_callback *cb =
            EVUTIL_UPCAST(d, struct deferred_reply_callback, deferred);
@@ -842,10 +853,13 @@ reply_schedule_callback(struct request *const req, u32 ttl, u32 err, struct repl
                d->handle = req->handle;
        }
 
-       event_deferred_cb_init(&d->deferred, reply_run_callback,
+       event_deferred_cb_init_(
+           &d->deferred,
+           event_get_priority(&req->timeout_event),
+           reply_run_callback,
            req->user_pointer);
-       event_deferred_cb_schedule(
-               event_base_get_deferred_cb_queue(req->base->event_base),
+       event_deferred_cb_schedule_(
+               req->base->event_base,
                &d->deferred);
 }
 
@@ -899,10 +913,12 @@ reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply)
                         */
                        log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver"
                                "at %s; will allow the request to time out.",
-                           evutil_format_sockaddr_port(
+                           evutil_format_sockaddr_port_(
                                    (struct sockaddr *)&req->ns->address,
                                    addrbuf, sizeof(addrbuf)));
-                       break;
+                       /* Call the timeout function */
+                       evdns_request_timeout_callback(0, 0, req);
+                       return;
                default:
                        /* we got a good reply from the nameserver: it is up. */
                        if (req->handle == req->ns->probe_request) {
@@ -945,8 +961,8 @@ 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 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;
@@ -960,7 +976,6 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
 
        for (;;) {
                u8 label_len;
-               if (j >= length) return -1;
                GET8(label_len);
                if (!label_len) break;
                if (label_len & 0xc0) {
@@ -981,6 +996,7 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
                        *cp++ = '.';
                }
                if (cp + label_len >= end) return -1;
+               if (j + label_len > length) return -1;
                memcpy(cp, packet + j, label_len);
                cp += label_len;
                j += label_len;
@@ -1000,8 +1016,8 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
 static int
 reply_parse(struct evdns_base *base, u8 *packet, int length) {
        int j = 0, k = 0;  /* index into packet */
-       u16 _t;  /* used by the macros */
-       u32 _t32;  /* used by the macros */
+       u16 t_;  /* used by the macros */
+       u32 t32_;  /* used by the macros */
        char tmp_name[256], cmp_name[256]; /* used by the macros */
        int name_matches = 0;
 
@@ -1044,24 +1060,6 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
                        sizeof(tmp_name))<0)                    \
                        goto err;                               \
        } while (0)
-#define TEST_NAME                                                      \
-       do { tmp_name[0] = '\0';                                        \
-               cmp_name[0] = '\0';                                     \
-               k = j;                                                  \
-               if (name_parse(packet, length, &j, tmp_name,            \
-                       sizeof(tmp_name))<0)                            \
-                       goto err;                                       \
-               if (name_parse(req->request, req->request_len, &k,      \
-                       cmp_name, sizeof(cmp_name))<0)                  \
-                       goto err;                                       \
-               if (base->global_randomize_case) {                      \
-                       if (strcmp(tmp_name, cmp_name) == 0)            \
-                               name_matches = 1;                       \
-               } else {                                                \
-                       if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) \
-                               name_matches = 1;                       \
-               }                                                       \
-       } while (0)
 
        reply.type = req->request_type;
 
@@ -1070,9 +1068,25 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
                /* the question looks like
                 *   <label:name><u16:type><u16:class>
                 */
-               TEST_NAME;
+               tmp_name[0] = '\0';
+               cmp_name[0] = '\0';
+               k = j;
+               if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name)) < 0)
+                       goto err;
+               if (name_parse(req->request, req->request_len, &k,
+                       cmp_name, sizeof(cmp_name))<0)
+                       goto err;
+               if (!base->global_randomize_case) {
+                       if (strcmp(tmp_name, cmp_name) == 0)
+                               name_matches = 1;
+               } else {
+                       if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0)
+                               name_matches = 1;
+               }
+
                j += 4;
-               if (j > length) goto err;
+               if (j > length)
+                       goto err;
        }
 
        if (!name_matches)
@@ -1202,7 +1216,7 @@ static int
 request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, ev_socklen_t addrlen)
 {
        int j = 0;      /* index into packet */
-       u16 _t;  /* used by the macros */
+       u16 t_;  /* used by the macros */
        char tmp_name[256]; /* used by the macros */
 
        int i;
@@ -1377,7 +1391,7 @@ nameserver_read(struct nameserver *ns) {
                        (struct sockaddr*)&ns->address, 0)) {
                        log(EVDNS_LOG_WARN, "Address mismatch on received "
                            "DNS packet.  Apparent source was %s",
-                           evutil_format_sockaddr_port(
+                           evutil_format_sockaddr_port_(
                                    (struct sockaddr *)&ss,
                                    addrbuf, sizeof(addrbuf)));
                        return;
@@ -1467,7 +1481,7 @@ nameserver_write_waiting(struct nameserver *ns, char waiting) {
        if (event_add(&ns->event, NULL) < 0) {
                char addrbuf[128];
                log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s",
-                   evutil_format_sockaddr_port(
+                   evutil_format_sockaddr_port_(
                            (struct sockaddr *)&ns->address,
                            addrbuf, sizeof(addrbuf)));
                /* ???? Do more? */
@@ -1588,20 +1602,20 @@ dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j,
                                  struct dnslabel_table *table) {
        const char *end = name + name_len;
        int ref = 0;
-       u16 _t;
+       u16 t_;
 
 #define APPEND16(x) do {                                               \
                if (j + 2 > (off_t)buf_len)                             \
                        goto overflow;                                  \
-               _t = htons(x);                                          \
-               memcpy(buf + j, &_t, 2);                                \
+               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);                              \
+               t32_ = htonl(x);                                        \
+               memcpy(buf + j, &t32_, 4);                              \
                j += 4;                                                 \
        } while (0)
 
@@ -1667,7 +1681,7 @@ evdns_request_data_build(const char *const name, const size_t 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 */
+       u16 t_;  /* used by the macros */
 
        APPEND16(trans_id);
        APPEND16(0x0100);  /* standard query, recusion needed */
@@ -1742,9 +1756,9 @@ evdns_close_server_port(struct evdns_server_port *port)
 
 /* 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)
+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_request *req = TO_SERVER_REQUEST(req_);
        struct server_reply_item **itemp, *item;
        int *countp;
        int result = -1;
@@ -1879,8 +1893,8 @@ 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;
+       u16 t_;
+       u32 t32_;
        int i;
        u16 flags;
        struct dnslabel_table table;
@@ -1938,8 +1952,8 @@ evdns_server_request_format_response(struct server_request *req, int err)
                                if (r < 0)
                                        goto overflow;
                                j = r;
-                               _t = htons( (short) (j-name_start) );
-                               memcpy(buf+len_idx, &_t, 2);
+                               t_ = htons( (short) (j-name_start) );
+                               memcpy(buf+len_idx, &t_, 2);
                        } else {
                                APPEND16(item->datalen);
                                if (j+item->datalen > (off_t)buf_len)
@@ -1972,9 +1986,9 @@ overflow:
 
 /* exported function */
 int
-evdns_server_request_respond(struct evdns_server_request *_req, int err)
+evdns_server_request_respond(struct evdns_server_request *req_, int err)
 {
-       struct server_request *req = TO_SERVER_REQUEST(_req);
+       struct server_request *req = TO_SERVER_REQUEST(req_);
        struct evdns_server_port *port = req->port;
        int r = -1;
 
@@ -2120,18 +2134,18 @@ server_port_free(struct evdns_server_port *port)
 
 /* exported function */
 int
-evdns_server_request_drop(struct evdns_server_request *_req)
+evdns_server_request_drop(struct evdns_server_request *req_)
 {
-       struct server_request *req = TO_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)
+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);
+       struct server_request *req = TO_SERVER_REQUEST(req_);
        if (addr_len < (int)req->addrlen)
                return -1;
        memcpy(sa, &(req->addr), req->addrlen);
@@ -2154,29 +2168,30 @@ evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) {
        log(EVDNS_LOG_DEBUG, "Request %p timed out", arg);
        EVDNS_LOCK(base);
 
-       req->ns->timedout++;
-       if (req->ns->timedout > req->base->global_max_nameserver_timeout) {
-               req->ns->timedout = 0;
-               nameserver_failed(req->ns, "request timed out.");
-       }
-
        if (req->tx_count >= req->base->global_max_retransmits) {
+               struct nameserver *ns = req->ns;
                /* this request has failed */
                log(EVDNS_LOG_DEBUG, "Giving up on request %p; tx_count==%d",
                    arg, req->tx_count);
                reply_schedule_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
+
                request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1);
+               nameserver_failed(ns, "request timed out.");
        } else {
                /* retransmit it */
-               struct nameserver *new_ns;
                log(EVDNS_LOG_DEBUG, "Retransmitting request %p; tx_count==%d",
                    arg, req->tx_count);
                (void) evtimer_del(&req->timeout_event);
-               new_ns = nameserver_pick(base);
-               if (new_ns)
-                       req->ns = new_ns;
+               request_swap_ns(req, nameserver_pick(base));
                evdns_request_transmit(req);
+
+               req->ns->timedout++;
+               if (req->ns->timedout > req->base->global_max_nameserver_timeout) {
+                       req->ns->timedout = 0;
+                       nameserver_failed(req->ns, "request timed out.");
+               }
        }
+
        EVDNS_UNLOCK(base);
 }
 
@@ -2191,6 +2206,13 @@ evdns_request_transmit_to(struct request *req, struct nameserver *server) {
        int r;
        ASSERT_LOCKED(req->base);
        ASSERT_VALID_REQUEST(req);
+
+       if (server->requests_inflight == 1 &&
+               req->base->disable_when_inactive &&
+               event_add(&server->event, NULL) < 0) {
+               return 1;
+       }
+
        r = sendto(server->socket, (void*)req->request, req->request_len, 0,
            (struct sockaddr *)&server->address, server->addrlen);
        if (r < 0) {
@@ -2299,7 +2321,7 @@ nameserver_send_probe(struct nameserver *const ns) {
 
        ASSERT_LOCKED(ns->base);
        log(EVDNS_LOG_DEBUG, "Sending probe to %s",
-           evutil_format_sockaddr_port(
+           evutil_format_sockaddr_port_(
                    (struct sockaddr *)&ns->address,
                    addrbuf, sizeof(addrbuf)));
        handle = mm_calloc(1, sizeof(*handle));
@@ -2460,7 +2482,7 @@ evdns_resume(void)
 }
 
 static int
-_evdns_nameserver_add_impl(struct evdns_base *base, const struct sockaddr *address, int addrlen) {
+evdns_nameserver_add_impl_(struct evdns_base *base, const struct sockaddr *address, int addrlen) {
        /* first check to see if we already have this nameserver */
 
        const struct nameserver *server = base->server_head, *const started_at = base->server_head;
@@ -2488,13 +2510,12 @@ _evdns_nameserver_add_impl(struct evdns_base *base, const struct sockaddr *addre
 
        evtimer_assign(&ns->timeout_event, ns->base->event_base, nameserver_prod_callback, ns);
 
-       ns->socket = socket(address->sa_family, SOCK_DGRAM, 0);
+       ns->socket = evutil_socket_(address->sa_family,
+           SOCK_DGRAM|EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC, 0);
        if (ns->socket < 0) { err = 1; goto out1; }
-       evutil_make_socket_closeonexec(ns->socket);
-       evutil_make_socket_nonblocking(ns->socket);
 
        if (base->global_outgoing_addrlen &&
-           !evutil_sockaddr_is_loopback(address)) {
+           !evutil_sockaddr_is_loopback_(address)) {
                if (bind(ns->socket,
                        (struct sockaddr*)&base->global_outgoing_address,
                        base->global_outgoing_addrlen) < 0) {
@@ -2507,14 +2528,15 @@ _evdns_nameserver_add_impl(struct evdns_base *base, const struct sockaddr *addre
        memcpy(&ns->address, address, addrlen);
        ns->addrlen = addrlen;
        ns->state = 1;
-       event_assign(&ns->event, ns->base->event_base, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
-       if (event_add(&ns->event, NULL) < 0) {
+       event_assign(&ns->event, ns->base->event_base, ns->socket,
+                                EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+       if (!base->disable_when_inactive && event_add(&ns->event, NULL) < 0) {
                err = 2;
                goto out2;
        }
 
        log(EVDNS_LOG_DEBUG, "Added nameserver %s as %p",
-           evutil_format_sockaddr_port(address, addrbuf, sizeof(addrbuf)), ns);
+           evutil_format_sockaddr_port_(address, addrbuf, sizeof(addrbuf)), ns);
 
        /* insert this nameserver into the list of them */
        if (!base->server_head) {
@@ -2537,7 +2559,7 @@ out1:
        event_debug_unassign(&ns->event);
        mm_free(ns);
        log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d",
-           evutil_format_sockaddr_port(address, addrbuf, sizeof(addrbuf)), err);
+           evutil_format_sockaddr_port_(address, addrbuf, sizeof(addrbuf)), err);
        return err;
 }
 
@@ -2552,7 +2574,7 @@ evdns_base_nameserver_add(struct evdns_base *base, unsigned long int address)
        sin.sin_port = htons(53);
        sin.sin_family = AF_INET;
        EVDNS_LOCK(base);
-       res = _evdns_nameserver_add_impl(base, (struct sockaddr*)&sin, sizeof(sin));
+       res = evdns_nameserver_add_impl_(base, (struct sockaddr*)&sin, sizeof(sin));
        EVDNS_UNLOCK(base);
        return res;
 }
@@ -2604,7 +2626,7 @@ evdns_base_nameserver_ip_add(struct evdns_base *base, const char *ip_as_string)
                sockaddr_setport(sa, 53);
 
        EVDNS_LOCK(base);
-       res = _evdns_nameserver_add_impl(base, sa, len);
+       res = evdns_nameserver_add_impl_(base, sa, len);
        EVDNS_UNLOCK(base);
        return res;
 }
@@ -2623,11 +2645,39 @@ evdns_base_nameserver_sockaddr_add(struct evdns_base *base,
        int res;
        EVUTIL_ASSERT(base);
        EVDNS_LOCK(base);
-       res = _evdns_nameserver_add_impl(base, sa, len);
+       res = evdns_nameserver_add_impl_(base, sa, len);
        EVDNS_UNLOCK(base);
        return res;
 }
 
+int
+evdns_base_get_nameserver_addr(struct evdns_base *base, int idx,
+    struct sockaddr *sa, ev_socklen_t len)
+{
+       int result = -1;
+       int i;
+       struct nameserver *server;
+       EVDNS_LOCK(base);
+       server = base->server_head;
+       for (i = 0; i < idx && server; ++i, server = server->next) {
+               if (server->next == base->server_head)
+                       goto done;
+       }
+       if (! server)
+               goto done;
+
+       if (server->addrlen > len) {
+               result = (int) server->addrlen;
+               goto done;
+       }
+
+       memcpy(sa, &server->address, server->addrlen);
+       result = (int) server->addrlen;
+done:
+       EVDNS_UNLOCK(base);
+       return result;
+}
+
 /* remove from the queue */
 static void
 evdns_request_remove(struct request *req, struct request **head)
@@ -2731,7 +2781,7 @@ request_new(struct evdns_base *base, struct evdns_request *handle, int type,
                strlcpy(namebuf, name, sizeof(namebuf));
                evutil_secure_rng_get_bytes(randbits, (name_len+7)/8);
                for (i = 0; i < name_len; ++i) {
-                       if (EVUTIL_ISALPHA(namebuf[i])) {
+                       if (EVUTIL_ISALPHA_(namebuf[i])) {
                                if ((randbits[i >> 3] & (1<<(i & 7))))
                                        namebuf[i] |= 0x20;
                                else
@@ -2779,7 +2829,10 @@ request_submit(struct request *const req) {
                /* if it has a nameserver assigned then this is going */
                /* straight into the inflight queue */
                evdns_request_insert(req, &REQ_HEAD(base, req->trans_id));
+
                base->global_requests_inflight++;
+               req->ns->requests_inflight++;
+
                evdns_request_transmit(req);
        } else {
                evdns_request_insert(req, &base->req_waiting_head);
@@ -3122,9 +3175,12 @@ search_set_from_hostname(struct evdns_base *base) {
 static char *
 search_make_new(const struct search_state *const state, int n, const char *const base_name) {
        const size_t base_len = strlen(base_name);
-       const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+       char need_to_append_dot;
        struct search_domain *dom;
 
+       if (!base_len) return NULL;
+       need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+
        for (dom = state->head; dom; dom = dom->next) {
                if (!n--) {
                        /* this is the postfix we want */
@@ -3263,7 +3319,7 @@ evdns_resolv_set_defaults(struct evdns_base *base, int flags) {
        if (flags & DNS_OPTION_NAMESERVERS) evdns_base_nameserver_ip_add(base,"127.0.0.1");
 }
 
-#ifndef _EVENT_HAVE_STRTOK_R
+#ifndef EVENT__HAVE_STRTOK_R
 static char *
 strtok_r(char *s, const char *delim, char **state) {
        char *cp, *start;
@@ -3297,7 +3353,7 @@ strtoint(const char *const str)
 
 /* Parse a number of seconds into a timeval; return -1 on error. */
 static int
-strtotimeval(const char *const str, struct timeval *out)
+evdns_strtotimeval(const char *const str, struct timeval *out)
 {
        double d;
        char *endptr;
@@ -3400,13 +3456,13 @@ evdns_base_set_option_impl(struct evdns_base *base,
                base->global_search_state->ndots = ndots;
        } else if (str_matches_option(option, "timeout:")) {
                struct timeval tv;
-               if (strtotimeval(val, &tv) == -1) return -1;
+               if (evdns_strtotimeval(val, &tv) == -1) return -1;
                if (!(flags & DNS_OPTION_MISC)) return 0;
                log(EVDNS_LOG_DEBUG, "Setting timeout to %s", val);
                memcpy(&base->global_timeout, &tv, sizeof(struct timeval));
        } else if (str_matches_option(option, "getaddrinfo-allow-skew:")) {
                struct timeval tv;
-               if (strtotimeval(val, &tv) == -1) return -1;
+               if (evdns_strtotimeval(val, &tv) == -1) return -1;
                if (!(flags & DNS_OPTION_MISC)) return 0;
                log(EVDNS_LOG_DEBUG, "Setting getaddrinfo-allow-skew to %s",
                    val);
@@ -3448,7 +3504,7 @@ evdns_base_set_option_impl(struct evdns_base *base,
                base->global_outgoing_addrlen = len;
        } else if (str_matches_option(option, "initial-probe-timeout:")) {
                struct timeval tv;
-               if (strtotimeval(val, &tv) == -1) return -1;
+               if (evdns_strtotimeval(val, &tv) == -1) return -1;
                if (tv.tv_sec > 3600)
                        tv.tv_sec = 3600;
                if (!(flags & DNS_OPTION_MISC)) return 0;
@@ -3528,7 +3584,7 @@ evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *con
 static char *
 evdns_get_default_hosts_filename(void)
 {
-#ifdef WIN32
+#ifdef _WIN32
        /* Windows is a little coy about where it puts its configuration
         * files.  Sure, they're _usually_ in C:\windows\system32, but
         * there's no reason in principle they couldn't be in
@@ -3541,8 +3597,8 @@ evdns_get_default_hosts_filename(void)
 
        if (! SHGetSpecialFolderPathA(NULL, path, CSIDL_SYSTEM, 0))
                return NULL;
-       len_out = strlen(path)+strlen(hostfile);
-       path_out = mm_malloc(len_out+1);
+       len_out = strlen(path)+strlen(hostfile)+1;
+       path_out = mm_malloc(len_out);
        evutil_snprintf(path_out, len_out, "%s%s", path, hostfile);
        return path_out;
 #else
@@ -3566,7 +3622,7 @@ evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char
                        mm_free(fname);
        }
 
-       if ((err = evutil_read_file(filename, &resolv, &n, 0)) < 0) {
+       if ((err = evutil_read_file_(filename, &resolv, &n, 0)) < 0) {
                if (err == -1) {
                        /* No file. */
                        evdns_resolv_set_defaults(base, flags);
@@ -3610,7 +3666,7 @@ evdns_resolv_conf_parse(int flags, const char *const filename) {
 }
 
 
-#ifdef WIN32
+#ifdef _WIN32
 /* Add multiple nameservers from a space-or-comma-separated list. */
 static int
 evdns_nameserver_ip_add_line(struct evdns_base *base, const char *ips) {
@@ -3653,7 +3709,7 @@ load_nameservers_with_getnetworkparams(struct evdns_base *base)
        GetNetworkParams_fn_t fn;
 
        ASSERT_LOCKED(base);
-       if (!(handle = evutil_load_windows_system_library(
+       if (!(handle = evutil_load_windows_system_library_(
                        TEXT("iphlpapi.dll")))) {
                log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll");
                status = -1;
@@ -3812,17 +3868,18 @@ evdns_base_config_windows_nameservers(struct evdns_base *base)
        if (base == NULL)
                return -1;
        EVDNS_LOCK(base);
+       fname = evdns_get_default_hosts_filename();
+       log(EVDNS_LOG_DEBUG, "Loading hosts entries from %s", fname);
+       evdns_base_load_hosts(base, fname);
+       if (fname)
+               mm_free(fname);
+
        if (load_nameservers_with_getnetworkparams(base) == 0) {
                EVDNS_UNLOCK(base);
                return 0;
        }
        r = load_nameservers_from_registry(base);
 
-       fname = evdns_get_default_hosts_filename();
-       evdns_base_load_hosts(base, fname);
-       if (fname)
-               mm_free(fname);
-
        EVDNS_UNLOCK(base);
        return r;
 }
@@ -3840,7 +3897,7 @@ evdns_config_windows_nameservers(void)
 #endif
 
 struct evdns_base *
-evdns_base_new(struct event_base *event_base, int initialize_nameservers)
+evdns_base_new(struct event_base *event_base, int flags)
 {
        struct evdns_base *base;
 
@@ -3853,7 +3910,8 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
        /* Give the evutil library a hook into its evdns-enabled
         * functionality.  We can't just call evdns_getaddrinfo directly or
         * else libevent-core will depend on libevent-extras. */
-       evutil_set_evdns_getaddrinfo_fn(evdns_getaddrinfo);
+       evutil_set_evdns_getaddrinfo_fn_(evdns_getaddrinfo);
+       evutil_set_evdns_getaddrinfo_cancel_fn_(evdns_getaddrinfo_cancel);
 
        base = mm_malloc(sizeof(struct evdns_base));
        if (base == NULL)
@@ -3888,9 +3946,18 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
 
        TAILQ_INIT(&base->hostsdb);
 
-       if (initialize_nameservers) {
+#define EVDNS_BASE_ALL_FLAGS (0x8001)
+       if (flags & ~EVDNS_BASE_ALL_FLAGS) {
+               flags = EVDNS_BASE_INITIALIZE_NAMESERVERS;
+               log(EVDNS_LOG_WARN,
+                   "Unrecognized flag passed to evdns_base_new(). Assuming "
+                   "you meant EVDNS_BASE_INITIALIZE_NAMESERVERS.");
+       }
+#undef EVDNS_BASE_ALL_FLAGS
+
+       if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) {
                int r;
-#ifdef WIN32
+#ifdef _WIN32
                r = evdns_base_config_windows_nameservers(base);
 #else
                r = evdns_base_resolv_conf_parse(base, DNS_OPTIONS_ALL, "/etc/resolv.conf");
@@ -3900,6 +3967,10 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
                        return NULL;
                }
        }
+       if (flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE) {
+               base->disable_when_inactive = 1;
+       }
+
        EVDNS_UNLOCK(base);
        return base;
 }
@@ -3941,7 +4012,7 @@ static void
 evdns_nameserver_free(struct nameserver *server)
 {
        if (server->socket >= 0)
-       evutil_closesocket(server->socket);
+               evutil_closesocket(server->socket);
        (void) event_del(&server->event);
        event_debug_unassign(&server->event);
        if (server->state == 0)
@@ -3965,15 +4036,6 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests)
 
        /* TODO(nickm) we might need to refcount here. */
 
-       for (server = base->server_head; server; server = server_next) {
-               server_next = server->next;
-               evdns_nameserver_free(server);
-               if (server_next == base->server_head)
-                       break;
-       }
-       base->server_head = NULL;
-       base->global_good_nameservers = 0;
-
        for (i = 0; i < base->n_req_heads; ++i) {
                while (base->req_heads[i]) {
                        if (fail_requests)
@@ -3988,6 +4050,16 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests)
        }
        base->global_requests_inflight = base->global_requests_waiting = 0;
 
+       for (server = base->server_head; server; server = server_next) {
+               server_next = server->next;
+               /** already done something before */
+               server->probe_request = NULL;
+               evdns_nameserver_free(server);
+               if (server_next == base->server_head)
+                       break;
+       }
+       base->server_head = NULL;
+       base->global_good_nameservers = 0;
 
        if (base->global_search_state) {
                for (dom = base->global_search_state->head; dom; dom = dom_next) {
@@ -4022,6 +4094,18 @@ evdns_base_free(struct evdns_base *base, int fail_requests)
 }
 
 void
+evdns_base_clear_host_addresses(struct evdns_base *base)
+{
+       struct hosts_entry *victim;
+       EVDNS_LOCK(base);
+       while ((victim = TAILQ_FIRST(&base->hostsdb))) {
+               TAILQ_REMOVE(&base->hostsdb, victim, next);
+               mm_free(victim);
+       }
+       EVDNS_UNLOCK(base);
+}
+
+void
 evdns_shutdown(int fail_requests)
 {
        if (current_base) {
@@ -4096,7 +4180,7 @@ evdns_base_load_hosts_impl(struct evdns_base *base, const char *hosts_fname)
        ASSERT_LOCKED(base);
 
        if (hosts_fname == NULL ||
-           (err = evutil_read_file(hosts_fname, &str, &len, 0)) < 0) {
+           (err = evutil_read_file_(hosts_fname, &str, &len, 0)) < 0) {
                char tmp[64];
                strlcpy(tmp, "127.0.0.1   localhost", sizeof(tmp));
                evdns_base_parse_hosts_line(base, tmp);
@@ -4325,17 +4409,23 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                other_req = &data->ipv4_request;
        }
 
-       EVDNS_LOCK(data->evdns_base);
-       if (evdns_result_is_answer(result)) {
-               if (req->type == DNS_IPv4_A)
-                       ++data->evdns_base->getaddrinfo_ipv4_answered;
-               else
-                       ++data->evdns_base->getaddrinfo_ipv6_answered;
+       /** Called from evdns_base_free() with @fail_requests == 1 */
+       if (result != DNS_ERR_SHUTDOWN) {
+               EVDNS_LOCK(data->evdns_base);
+               if (evdns_result_is_answer(result)) {
+                       if (req->type == DNS_IPv4_A)
+                               ++data->evdns_base->getaddrinfo_ipv4_answered;
+                       else
+                               ++data->evdns_base->getaddrinfo_ipv6_answered;
+               }
+               user_canceled = data->user_canceled;
+               if (other_req->r == NULL)
+                       data->request_done = 1;
+               EVDNS_UNLOCK(data->evdns_base);
+       } else {
+               data->evdns_base = NULL;
+               user_canceled = data->user_canceled;
        }
-       user_canceled = data->user_canceled;
-       if (other_req->r == NULL)
-               data->request_done = 1;
-       EVDNS_UNLOCK(data->evdns_base);
 
        req->r = NULL;
 
@@ -4369,7 +4459,9 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                        /* The other request is still working; maybe it will
                         * succeed. */
                        /* XXXX handle failure from set_timeout */
-                       evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+                       if (result != DNS_ERR_SHUTDOWN) {
+                               evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+                       }
                        data->pending_error = err;
                        return;
                }
@@ -4430,7 +4522,7 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
        for (i=0; i < count; ++i) {
                struct evutil_addrinfo *ai;
                memcpy(addrp, ((char*)addresses)+i*addrlen, addrlen);
-               ai = evutil_new_addrinfo(sa, socklen, &data->hints);
+               ai = evutil_new_addrinfo_(sa, socklen, &data->hints);
                if (!ai) {
                        if (other_req->r) {
                                evdns_cancel_request(NULL, other_req->r);
@@ -4443,7 +4535,7 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                                free_getaddrinfo_request(data);
                        return;
                }
-               res = evutil_addrinfo_append(res, ai);
+               res = evutil_addrinfo_append_(res, ai);
        }
 
        if (other_req->r) {
@@ -4457,10 +4549,10 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                 * results (if any) and return them. */
                if (data->pending_result) {
                        if (req->type == DNS_IPv4_A)
-                               res = evutil_addrinfo_append(res,
+                               res = evutil_addrinfo_append_(res,
                                    data->pending_result);
                        else
-                               res = evutil_addrinfo_append(
+                               res = evutil_addrinfo_append_(
                                    data->pending_result, res);
                        data->pending_result = NULL;
                }
@@ -4510,13 +4602,13 @@ evdns_getaddrinfo_fromhosts(struct evdns_base *base,
                if ((e->addr.sa.sa_family == AF_INET && f == PF_INET6) ||
                    (e->addr.sa.sa_family == AF_INET6 && f == PF_INET))
                        continue;
-               ai_new = evutil_new_addrinfo(&e->addr.sa, e->addrlen, hints);
+               ai_new = evutil_new_addrinfo_(&e->addr.sa, e->addrlen, hints);
                if (!ai_new) {
                        n_found = 0;
                        goto out;
                }
                sockaddr_setport(ai_new->ai_addr, port);
-               ai = evutil_addrinfo_append(ai, ai_new);
+               ai = evutil_addrinfo_append_(ai, ai_new);
        }
        EVDNS_UNLOCK(base);
 out:
@@ -4571,14 +4663,14 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
                hints.ai_family = PF_UNSPEC;
        }
 
-       evutil_adjust_hints_for_addrconfig(&hints);
+       evutil_adjust_hints_for_addrconfig_(&hints);
 
        /* Now try to see if we _can_ answer immediately. */
        /* (It would be nice to do this by calling getaddrinfo directly, with
         * AI_NUMERICHOST, on plaforms that have it, but we can't: there isn't
         * a reliable way to distinguish the "that wasn't a numeric host!" case
         * from any other EAI_NONAME cases.) */
-       err = evutil_getaddrinfo_common(nodename, servname, &hints, &res, &port);
+       err = evutil_getaddrinfo_common_(nodename, servname, &hints, &res, &port);
        if (err != EVUTIL_EAI_NEED_RESOLVE) {
                cb(err, res, arg);
                return NULL;
@@ -4629,7 +4721,7 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
                data->ipv4_request.r = evdns_base_resolve_ipv4(dns_base,
                    nodename, 0, evdns_getaddrinfo_gotresolve,
                    &data->ipv4_request);
-               if (want_cname)
+               if (want_cname && data->ipv4_request.r)
                        data->ipv4_request.r->current_req->put_cname_in_ptr =
                            &data->cname_result;
        }
@@ -4640,7 +4732,7 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
                data->ipv6_request.r = evdns_base_resolve_ipv6(dns_base,
                    nodename, 0, evdns_getaddrinfo_gotresolve,
                    &data->ipv6_request);
-               if (want_cname)
+               if (want_cname && data->ipv6_request.r)
                        data->ipv6_request.r->current_req->put_cname_in_ptr =
                            &data->cname_result;
        }