The entry points are evutil_getaddrinfo and evdns_getaddrinfo respectively.
There are fairly extensive unit tests.
I believe this code conforms to RFC3493 pretty closely, but there are
probably more issues. It should get tested on more platforms.
This code means we can dump the well-intentioned but weirdly-implemented
bufferevent_evdns and evutil_resolve code.
svn:r1537
o Default to using arc4random for DNS transaction IDs on systems that have it; from OpenBSD.
o Never check the environment when we're running setuid or setgid; from OpenBSD.
o Options passed to evdns_set_option() no longer need to end with a colon.
+ o Add an evutil_getaddrinfo() function to clone getaddrinfo on platforms that don't have it.
+ o Add an evdns_getaddrinfo() function to provide a nonblocking getaddrinfo using evdns, so programs can perform useful hostname lookup.
Changes in 2.0.2-alpha:
bufferevent.c bufferevent_sock.c bufferevent_filter.c \
bufferevent_pair.c listener.c \
evmap.c log.c evutil.c strlcpy.c $(SYS_SRC)
-EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c bufferevent_evdns.c
+EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c
libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
strlcpy.obj signal.obj bufferevent_filter.obj
WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj \
event_iocp.obj bufferevent_async.obj
-EXTRA_OBJS=event_tagging.obj http.obj evdns.obj bufferevent_evdns.obj evrpc.obj
+EXTRA_OBJS=event_tagging.obj http.obj evdns.obj evrpc.obj
ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib
#define _EVENT_HAVE_FCNTL_H 1
/* Define to 1 if you have the `getaddrinfo' function. */
-/* #undef _EVENT_HAVE_GETADDRINFO */
+#define _EVENT_HAVE_GETADDRINFO 1
/* Define to 1 if you have the `getnameinfo' function. */
-/* #undef _EVENT_HAVE_GETNAMEINFO */
+#define _EVENT_HAVE_GETNAMEINFO 1
+
+/* Define to 1 if you have the `getprotobynumber' function. */
+#define _EVENT_HAVE_GETPROTOBYNUMBER 1
+
+/* Define to 1 if you have the `getservbyname' function. */
+#define _EVENT_HAVE_GETSERVBYNAME 1
/* Define to 1 if you have the `gettimeofday' function. */
/* #define _EVENT_HAVE_GETTIMEOFDAY 1 */
/* Define to 1 if you have the `strtoll' function. */
/* #define _EVENT_HAVE_STRTOLL 1 */
+#define _EVENT_HAVE_STRUCT_ADDRINFO 1
+
/* Define to 1 if the system has the type `struct in6_addr'. */
#define _EVENT_HAVE_STRUCT_IN6_ADDR 1
EVLOCK_UNLOCK(locking->lock, EVTHREAD_WRITE); \
} while(0)
-struct evdns_base;
-int _bufferevent_socket_connect_hostname_evdns(
- struct bufferevent *bufev,
- struct evdns_base *evdns_base,
- int family,
- const char *hostname,
- int port);
-void _bufferevent_set_socket_connect_hostname_evdns_fn(
- int (*fn)(struct bufferevent *, struct evdns_base *, int,
- const char *, int));
-
#ifdef __cplusplus
}
#endif
+++ /dev/null
-/*
- * Copyright (c) 2009 Niels Provos, Nick Mathewson
- *
- * 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.
- */
-
-/** @file bufferevent_evdns.c
- *
- * This module contains code to implement the asynchronous
- * resolve-then-connect behavior of bufferevent_socket_connect_hostname.
- *
- * It isn't part of bufferevent_socket because evdns is in libevent_extras,
- * and bufferevent is in libevent_core.
- */
-
-#ifdef WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#endif
-
-#include "event-config.h"
-
-#include <sys/types.h>
-#ifdef _EVENT_HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef _EVENT_HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef _EVENT_HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef _EVENT_HAVE_NETINET_IN6_H
-#include <netinet/in6.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-
-#include <event2/event.h>
-#include <event2/bufferevent.h>
-#include <event2/bufferevent_struct.h>
-#include <event2/dns.h>
-#include "bufferevent-internal.h"
-#include "mm-internal.h"
-
-/* Holds info passed to the dns callback */
-struct resolveinfo {
- ev_uint8_t family; /* address family that we tried to resolve. */
- ev_uint16_t port; /* port to connect to, in network order. */
- struct bufferevent *bev; /* bufferevent to inform of the resolve. */
-};
-
-/* Callback: Invoked when we are done resolving (or failing to resolve) the
- * hostname */
-static void
-dns_reply_callback(int result, char type, int count, int ttl, void *addresses,
- void *arg)
-{
- struct resolveinfo *info = arg;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- struct sockaddr *sa = NULL;
- int socklen;
-
- EVUTIL_ASSERT(info->bev);
- BEV_LOCK(info->bev);
-
- if (result != DNS_ERR_NONE || count == 0) {
- _bufferevent_run_eventcb(info->bev, BEV_EVENT_ERROR);
- _bufferevent_decref_and_unlock(info->bev);
- memset(info, 0, sizeof(*info));
- mm_free(info);
- return;
- }
-
- if (type == DNS_IPv4_A) {
- EVUTIL_ASSERT(info->family == AF_INET);
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = info->port;
- /* XXX handle multiple addresses better */
- sin.sin_addr.s_addr = *(ev_uint32_t*)addresses;
- sa = (struct sockaddr*)&sin;
- socklen = sizeof(sin);
- } else if (type == DNS_IPv6_AAAA) {
- EVUTIL_ASSERT(info->family == AF_INET6);
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET;
- sin6.sin6_port = info->port;
- /* XXX handle multiple addresses better */
- memcpy(sin6.sin6_addr.s6_addr, addresses, 16);
- sa = (struct sockaddr*)&sin6;
- socklen = sizeof(sin6);
- } else {
- EVUTIL_ASSERT(info->family == AF_INET ||
- info->family == AF_INET6);
- return; /* unreachable */
- }
-
- bufferevent_socket_connect(info->bev, sa, socklen);
- _bufferevent_decref_and_unlock(info->bev);
- memset(info, 0, sizeof(*info));
- mm_free(info);
-}
-
-/* Implements the asynchronous-resolve side of
- * bufferevent_socket_connect_hostname(). */
-int
-_bufferevent_socket_connect_hostname_evdns(
- struct bufferevent *bufev,
- struct evdns_base *evdns_base,
- int family,
- const char *hostname,
- int port)
-{
- struct evdns_request *r;
- struct resolveinfo *resolveinfo;
-
- if (family == AF_UNSPEC)
- family = AF_INET; /* XXXX handle "unspec" correctly */
- if (family != AF_INET && family != AF_INET6)
- return -1;
- if (!bufev || !evdns_base || !hostname)
- return -1;
- if (port < 1 || port > 65535)
- return -1;
-
- resolveinfo = mm_calloc(1, sizeof(resolveinfo));
- if (!resolveinfo)
- return -1;
- resolveinfo->family = family;
- resolveinfo->port = htons(port);
- resolveinfo->bev = bufev;
-
- if (family == AF_INET) {
- r = evdns_base_resolve_ipv4(evdns_base, hostname, 0,
- dns_reply_callback, resolveinfo);
- } else {
- r = evdns_base_resolve_ipv6(evdns_base, hostname, 0,
- dns_reply_callback, resolveinfo);
- }
-
- if (!r) {
- mm_free(resolveinfo);
- return -1;
- }
-
- /* We either need to incref the bufferevent here, or have some code to
- * cancel the resolve if the bufferevent gets freed. Let's take the
- * first approach. */
- bufferevent_incref(bufev);
- return 0;
-}
-
return result;
}
-static int (*_bufferevent_socket_connect_hostname_evdns_fn)(
- struct bufferevent *, struct evdns_base *, int,
- const char *, int) = NULL;
-
-void _bufferevent_set_socket_connect_hostname_evdns_fn(
- int (*fn)(struct bufferevent *, struct evdns_base *, int,
- const char *, int))
+static void
+bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,
+ void *arg)
{
- if (!_bufferevent_socket_connect_hostname_evdns_fn)
- _bufferevent_socket_connect_hostname_evdns_fn = fn;
+ struct bufferevent *bev = arg;
+ int r;
+ BEV_LOCK(bev);
+
+ if (result != 0) {
+ /* XXX Communicate the error somehow. */
+ _bufferevent_run_eventcb(bev, BEV_EVENT_ERROR);
+ _bufferevent_decref_and_unlock(bev);
+ if (ai)
+ evutil_freeaddrinfo(ai);
+ return;
+ }
+
+ /* XXX use the other addrinfos? */
+ r = bufferevent_socket_connect(bev, ai->ai_addr, ai->ai_addrlen);
+ _bufferevent_decref_and_unlock(bev);
+ evutil_freeaddrinfo(ai);
}
int
bufferevent_socket_connect_hostname(struct bufferevent *bev,
struct evdns_base *evdns_base, int family, const char *hostname, int port)
{
- struct sockaddr_storage ss;
- ev_socklen_t socklen = sizeof(ss);
- int socklen_int = sizeof(ss);
+ char portbuf[10];
+ struct evutil_addrinfo hint;
+ int err;
if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
return -1;
if (port < 1 || port > 65535)
return -1;
- memset(&ss, 0, sizeof(ss));
- if (!evutil_parse_sockaddr_port(hostname, (struct sockaddr*)&ss,
- &socklen_int)) {
- socklen = socklen_int;
- if (ss.ss_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in*)&ss;
- if (family == AF_INET6)
- return -1;
- if (sin->sin_port)
- return -1;
- sin->sin_port = htons(port);
- } else if (ss.ss_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&ss;
- if (family == AF_INET)
- return -1;
- if (sin6->sin6_port)
- return -1;
- sin6->sin6_port = htons(port);
- }
- return bufferevent_socket_connect(bev, (struct sockaddr*)&ss,
- socklen);
- }
+ evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);
- if (evdns_base) {
- EVUTIL_ASSERT(_bufferevent_socket_connect_hostname_evdns_fn);
- return _bufferevent_socket_connect_hostname_evdns_fn(
- bev, evdns_base, family, hostname, port);
- }
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = family;
+ hint.ai_protocol = IPPROTO_TCP;
+ hint.ai_socktype = SOCK_STREAM;
- memset(&ss, 0, sizeof(ss));
+ bufferevent_incref(bev);
+ err = evutil_getaddrinfo_async(evdns_base, hostname, portbuf,
+ &hint, bufferevent_connect_getaddrinfo_cb, bev);
- if (evutil_resolve(family, hostname, (struct sockaddr*)&ss,
- &socklen, port)<0) {
- _bufferevent_incref_and_lock(bev);
- _bufferevent_run_eventcb(bev, BEV_EVENT_ERROR);
- _bufferevent_decref_and_unlock(bev);
+ if (err == 0)
+ return 0;
+ else
return -1;
- }
-
- return bufferevent_socket_connect(bev, (struct sockaddr*)&ss, socklen);
}
/*
AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
+if test x$bwin32 = xtrue; then
+ LIBS="$LIBS -lws2_32"
+fi
+
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_HEADER_TIME
dnl Checks for library functions.
-AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random issetugid geteuid getegid)
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random issetugid geteuid getegid getservbyname getprotobynumber)
+
+
+# Check for gethostbyname_r in all its glorious incompatible versions.
+# (This is cut-and-pasted from Tor, which based its logic on
+# Python's configure.in.)
+AH_TEMPLATE(HAVE_GETHOSTBYNAME_R,
+ [Define this if you have any gethostbyname_r()])
+
+AC_CHECK_FUNC(gethostbyname_r, [
+ AC_MSG_CHECKING([how many arguments gethostbyname_r() wants])
+ OLD_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $MY_CPPFLAGS $MY_THREAD_CPPFLAGS $MY_CFLAGS"
+ AC_COMPILE_IFELSE(AC_LANG_PROGRAM([
+#include <netdb.h>
+ ], [[
+ char *cp1, *cp2;
+ struct hostent *h1, *h2;
+ int i1, i2;
+ (void)gethostbyname_r(cp1,h1,cp2,i1,&h2,&i2);
+ ]]),[
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R)
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R_6_ARG, 1,
+ [Define this if gethostbyname_r takes 6 arguments])
+ AC_MSG_RESULT(6)
+ ], [
+ AC_TRY_COMPILE([
+#include <netdb.h>
+ ], [
+ char *cp1, *cp2;
+ struct hostent *h1;
+ int i1, i2;
+ (void)gethostbyname_r(cp1,h1,cp2,i1,&i2);
+ ], [
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R)
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R_5_ARG, 1,
+ [Define this if gethostbyname_r takes 5 arguments])
+ AC_MSG_RESULT(5)
+ ], [
+ AC_TRY_COMPILE([
+#include <netdb.h>
+ ], [
+ char *cp1;
+ struct hostent *h1;
+ struct hostent_data hd;
+ (void) gethostbyname_r(cp1,h1,&hd);
+ ], [
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R)
+ AC_DEFINE(HAVE_GETHOSTBYNAME_R_3_ARG, 1,
+ [Define this if gethostbyname_r takes 3 arguments])
+ AC_MSG_RESULT(3)
+ ], [
+ AC_MSG_RESULT(0)
+ ])
+ ])
+ ])
+ CFLAGS=$OLD_CFLAGS
+])
+
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(size_t)
-AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , ,
-[#include <sys/types.h>
+AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t, struct addrinfo], , ,
+[#define _GNU_SOURCE
+#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
#ifdef WIN32
#define WIN32_WINNT 0x400
#define _WIN32_WINNT 0x400
#define close _close
#endif
-#define MAX_ADDRS 32 /* maximum number of addresses from a single packet */
-/* which we bother recording */
+/* maximum number of addresses from a single packet */
+/* that we bother recording */
+#define MAX_V4_ADDRS 32
+#define MAX_V6_ADDRS 32
+
#define TYPE_A EVDNS_TYPE_A
#define TYPE_CNAME 5
struct evdns_request {
u8 *request; /* the dns packet data */
+ u8 request_type; /* TYPE_PTR or TYPE_A or TYPE_AAAA */
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 */
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 */
+ unsigned request_appended :1; /* true if the request pointer is data which follows this struct */
+ unsigned transmit_me :1; /* needs to be transmitted */
+
+ /* XXXX This is a horrible hack. */
+ char **put_cname_in_ptr; /* store the cname here if we get one. */
struct evdns_base *base;
};
struct reply {
unsigned int type;
- unsigned int have_answer;
+ unsigned int have_answer : 1;
union {
struct {
u32 addrcount;
- u32 addresses[MAX_ADDRS];
+ u32 addresses[MAX_V4_ADDRS];
} a;
struct {
u32 addrcount;
- struct in6_addr addresses[MAX_ADDRS];
+ struct in6_addr addresses[MAX_V6_ADDRS];
} aaaa;
struct {
char name[HOST_NAME_MAX];
int global_max_requests_inflight;
- struct timeval global_timeout; /* 5 seconds */
+ struct timeval global_timeout; /* 5 seconds by default */
int global_max_reissues; /* a reissue occurs when we get some errors from the server */
int global_max_retransmits; /* 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 */
/** ev_socklen_t for global_outgoing_address. 0 if it isn't set. */
ev_socklen_t global_outgoing_addrlen;
+ struct timeval global_getaddrinfo_allow_skew;
+
+ int getaddrinfo_ipv4_timeouts;
+ int getaddrinfo_ipv6_timeouts;
+ int getaddrinfo_ipv4_answered;
+ int getaddrinfo_ipv6_answered;
+
struct search_state *global_search_state;
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
static struct evdns_base *current_base = NULL;
+struct evdns_base *
+evdns_get_global_base(void)
+{
+ return current_base;
+}
+
/* Given a pointer to an evdns_server_request, get the corresponding */
/* server_request. */
#define TO_SERVER_REQUEST(base_ptr) \
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);
+ addrtocopy = MIN(MAX_V4_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
ttl_r = MIN(ttl_r, ttl);
/* we only bother with the first four addresses. */
j += 4*addrtocopy;
reply.data.a.addrcount += addrtocopy;
reply.have_answer = 1;
- if (reply.data.a.addrcount == MAX_ADDRS) break;
+ if (reply.data.a.addrcount == MAX_V4_ADDRS) break;
} else if (type == TYPE_PTR && class == CLASS_INET) {
if (req->request_type != TYPE_PTR) {
j += datalength; continue;
ttl_r = MIN(ttl_r, ttl);
reply.have_answer = 1;
break;
+ } else if (type == TYPE_CNAME) {
+ char cname[HOST_NAME_MAX];
+ if (!req->put_cname_in_ptr || *req->put_cname_in_ptr) {
+ j += datalength; continue;
+ }
+ if (name_parse(packet, length, &j, cname,
+ sizeof(cname))<0)
+ goto err;
+ *req->put_cname_in_ptr = mm_strdup(cname);
} else if (type == TYPE_AAAA && class == CLASS_INET) {
int addrcount, addrtocopy;
if (req->request_type != TYPE_AAAA) {
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);
+ addrtocopy = MIN(MAX_V6_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount);
ttl_r = MIN(ttl_r, ttl);
/* we only bother with the first four addresses. */
reply.data.aaaa.addrcount += addrtocopy;
j += 16*addrtocopy;
reply.have_answer = 1;
- if (reply.data.aaaa.addrcount == MAX_ADDRS) break;
+ if (reply.data.aaaa.addrcount == MAX_V6_ADDRS) break;
} else {
/* skip over any other type of resource */
j += datalength;
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 (!(flags & DNS_OPTION_MISC)) return 0;
+ log(EVDNS_LOG_DEBUG, "Setting getaddrinfo-allow-skew to %s",
+ val);
+ memcpy(&base->global_getaddrinfo_allow_skew, &tv,
+ sizeof(struct timeval));
} else if (str_matches_option(option, "max-timeouts:")) {
const int maxtimeout = strtoint_clipped(val, 1, 255);
if (maxtimeout == -1) return -1;
{
struct evdns_base *base;
- /* Give the bufferevent library a hook into its evdns-enabled
- * functionality. We can't do this correctly or else libevent-core
- * will depend on libevent-extras. */
- _bufferevent_set_socket_connect_hostname_evdns_fn(
- _bufferevent_socket_connect_hostname_evdns);
+ /* 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);
base = mm_malloc(sizeof(struct evdns_base));
if (base == NULL)
base->global_max_nameserver_timeout = 3;
base->global_search_state = NULL;
base->global_randomize_case = 1;
+ base->global_getaddrinfo_allow_skew.tv_sec = 3;
+ base->global_getaddrinfo_allow_skew.tv_usec = 0;
if (initialize_nameservers) {
int r;
evdns_log_fn = NULL;
}
+/* A single request for a getaddrinfo, either v4 or v6. */
+struct getaddrinfo_subrequest {
+ struct evdns_request *r;
+ ev_uint32_t type;
+};
+
+/* State data used to implement an in-progress getaddrinfo. */
+struct evdns_getaddrinfo_request {
+ struct evdns_base *evdns_base;
+ /* Copy of the modified 'hints' data that we'll use to build
+ * answers. */
+ struct evutil_addrinfo hints;
+ /* The callback to invoke when we're done */
+ evdns_getaddrinfo_cb user_cb;
+ /* User-supplied data to give to the callback. */
+ void *user_data;
+ /* The port to use when building sockaddrs. */
+ ev_uint16_t port;
+ /* The sub_request for an A record (if any) */
+ struct getaddrinfo_subrequest ipv4_request;
+ /* The sub_request for an AAAA record (if any) */
+ struct getaddrinfo_subrequest ipv6_request;
+
+ /* The cname result that we were told (if any) */
+ char *cname_result;
+
+ /* If we have one request answered and one request still inflight,
+ * then this field holds the answer from the first request... */
+ struct evutil_addrinfo *pending_result;
+ /* And this field holds the error code from the first request... */
+ int pending_error;
+ /* And this event is a timeout that will tell us to cancel the second
+ * request if it's taking a long time. */
+ struct event timeout;
+};
+
+/* Convert an evdns errors to the equivalent getaddrinfo error. */
+static int
+evdns_err_to_getaddrinfo_err(int e1)
+{
+ /* XXX Do this better! */
+ if (e1 == DNS_ERR_NONE)
+ return 0;
+ else if (e1 == DNS_ERR_NOTEXIST)
+ return EVUTIL_EAI_NONAME;
+ else
+ return EVUTIL_EAI_FAIL;
+}
+
+/* Return the more informative of two getaddrinfo errors. */
+static int
+getaddrinfo_merge_err(int e1, int e2)
+{
+ /* XXXX be cleverer here. */
+ if (e1 == 0)
+ return e2;
+ else
+ return e1;
+}
+
+static void
+free_getaddrinfo_request(struct evdns_getaddrinfo_request *data)
+{
+ if (data->pending_result)
+ evutil_freeaddrinfo(data->pending_result);
+ if (data->cname_result)
+ mm_free(data->cname_result);
+ event_del(&data->timeout);
+ mm_free(data);
+ return;
+}
+
+static void
+add_cname_to_reply(struct evdns_getaddrinfo_request *data,
+ struct evutil_addrinfo *ai)
+{
+ if (data->cname_result && ai) {
+ ai->ai_canonname = data->cname_result;
+ data->cname_result = NULL;
+ }
+}
+
+/* Callback: invoked when one request in a mixed-format A/AAAA getaddrinfo
+ * request has finished, but the other one took too long to answer. Pass
+ * along the answer we got, and cancel the other request.
+ */
+static void
+evdns_getaddrinfo_timeout_cb(evutil_socket_t fd, short what, void *ptr)
+{
+ int v4_timedout = 0, v6_timedout = 0;
+ struct evdns_getaddrinfo_request *data = ptr;
+
+ /* Cancel any pending requests, and note which one */
+ if (data->ipv4_request.r) {
+ evdns_cancel_request(NULL, data->ipv4_request.r);
+ data->ipv4_request.r = NULL;
+ v4_timedout = 1;
+ EVDNS_LOCK(data->evdns_base);
+ ++data->evdns_base->getaddrinfo_ipv4_timeouts;
+ }
+ if (data->ipv6_request.r) {
+ evdns_cancel_request(NULL, data->ipv6_request.r);
+ data->ipv6_request.r = NULL;
+ v6_timedout = 1;
+ EVDNS_LOCK(data->evdns_base);
+ ++data->evdns_base->getaddrinfo_ipv6_timeouts;
+ EVDNS_UNLOCK(data->evdns_base);
+ }
+
+ /* We only use this timeout callback when we have an answer for
+ * one address. */
+ EVUTIL_ASSERT(!v4_timedout || !v6_timedout);
+
+ /* Report the outcome of the other request that didn't time out. */
+ if (data->pending_result) {
+ add_cname_to_reply(data, data->pending_result);
+ data->user_cb(0, data->pending_result, data->user_data);
+ data->pending_result = NULL;
+ } else {
+ int e = data->pending_error;
+ if (!e)
+ e = EVUTIL_EAI_AGAIN;
+ data->user_cb(e, NULL, data->user_data);
+ }
+
+ free_getaddrinfo_request(data);
+}
+
+static void
+evdns_getaddrinfo_set_timeout(struct evdns_base *evdns_base,
+ struct evdns_getaddrinfo_request *data)
+{
+ event_add(&data->timeout, &evdns_base->global_getaddrinfo_allow_skew);
+}
+
+static void
+evdns_getaddrinfo_gotresolve(int result, char type, int count,
+ int ttl, void *addresses, void *arg)
+{
+ int i;
+ struct getaddrinfo_subrequest *req = arg;
+ struct getaddrinfo_subrequest *other_req;
+ struct evdns_getaddrinfo_request *data;
+
+ struct evutil_addrinfo *res;
+
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ int socklen, addrlen;
+ void *addrp;
+ int err;
+
+ if (result == DNS_ERR_CANCEL)
+ return;
+
+ EVUTIL_ASSERT(req->type == DNS_IPv4_A || req->type == DNS_IPv6_AAAA);
+ if (req->type == DNS_IPv4_A) {
+ data = EVUTIL_UPCAST(req, struct evdns_getaddrinfo_request, ipv4_request);
+ other_req = &data->ipv6_request;
+ if (result != DNS_ERR_NOTIMPL && result != DNS_ERR_REFUSED &&
+ result != DNS_ERR_SERVERFAILED) {
+ EVDNS_LOCK(data->evdns_base);
+ ++data->evdns_base->getaddrinfo_ipv4_answered;
+ EVDNS_UNLOCK(data->evdns_base);
+ }
+ } else {
+ data = EVUTIL_UPCAST(req, struct evdns_getaddrinfo_request, ipv6_request);
+ other_req = &data->ipv4_request;
+ if (result != DNS_ERR_NOTIMPL && result != DNS_ERR_REFUSED &&
+ result != DNS_ERR_SERVERFAILED) {
+ EVDNS_LOCK(data->evdns_base);
+ ++data->evdns_base->getaddrinfo_ipv6_answered;
+ EVDNS_UNLOCK(data->evdns_base);
+ }
+ }
+
+ req->r = NULL;
+
+ if (result == DNS_ERR_NONE) {
+ if (count == 0)
+ err = EVUTIL_EAI_NODATA;
+ else
+ err = 0;
+ } else {
+ err = evdns_err_to_getaddrinfo_err(result);
+ }
+
+ if (err) {
+ /* Looks like we got an error. */
+ if (other_req->r) {
+ /* The other request is still working; maybe it will
+ * succeed. */
+ evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+ data->pending_error = err;
+ return;
+ }
+
+ if (data->pending_result) {
+ /* If we have an answer waiting, ignore this error. */
+ add_cname_to_reply(data, data->pending_result);
+ data->user_cb(0, data->pending_result, data->user_data);
+ data->pending_result = NULL;
+ } else {
+ if (data->pending_error)
+ err = getaddrinfo_merge_err(err,
+ data->pending_error);
+ data->user_cb(err, NULL, data->user_data);
+ }
+ free_getaddrinfo_request(data);
+ return;
+ }
+
+ /* Looks like we got some answers. We should turn them into addrinfos
+ * and then either queue those or return them all. */
+ EVUTIL_ASSERT(type == DNS_IPv4_A || type == DNS_IPv6_AAAA);
+
+ if (type == DNS_IPv4_A) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(data->port);
+
+ sa = (struct sockaddr *)&sin;
+ socklen = sizeof(sin);
+ addrlen = 4;
+ addrp = &sin.sin_addr.s_addr;
+ } else {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(data->port);
+
+ sa = (struct sockaddr *)&sin6;
+ socklen = sizeof(sin6);
+ addrlen = 16;
+ addrp = &sin6.sin6_addr.s6_addr;
+ }
+
+ res = NULL;
+ 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);
+ if (!ai) {
+ if (other_req->r) {
+ evdns_cancel_request(NULL, other_req->r);
+ other_req->r = NULL;
+ }
+ data->user_cb(EVUTIL_EAI_MEMORY, NULL, data->user_data);
+ evutil_freeaddrinfo(res);
+
+ free_getaddrinfo_request(data);
+ return;
+ }
+ res = evutil_addrinfo_append(res, ai);
+ }
+
+ if (other_req->r) {
+ /* The other request is still in progress; wait for it */
+ evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+ data->pending_result = res;
+ return;
+ } else {
+ /* The other request is done or never started; append its
+ * results (if any) and return them. */
+ if (data->pending_result) {
+ if (req->type == DNS_IPv4_A)
+ res = evutil_addrinfo_append(res,
+ data->pending_result);
+ else
+ res = evutil_addrinfo_append(
+ data->pending_result, res);
+ data->pending_result = NULL;
+ }
+
+ /* Call the user callback. */
+ add_cname_to_reply(data, res);
+ data->user_cb(0, res, data->user_data);
+
+ /* Free data. */
+ free_getaddrinfo_request(data);
+ }
+}
+
+struct evdns_getaddrinfo_request *
+evdns_getaddrinfo(struct evdns_base *dns_base,
+ const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in,
+ evdns_getaddrinfo_cb cb, void *arg)
+{
+ struct evdns_getaddrinfo_request *data;
+ struct evutil_addrinfo hints;
+ struct evutil_addrinfo *res = NULL;
+ int err;
+ int port = 0;
+ int want_cname = 0;
+
+ if (!dns_base) {
+ dns_base = current_base;
+ if (!dns_base) {
+ log(EVDNS_LOG_WARN,
+ "Call to getaddrinfo_async with no "
+ "evdns_base configured.");
+ cb(EVUTIL_EAI_FAIL, NULL, arg); /* ??? better error? */
+ return NULL;
+ }
+ }
+
+ /* If we _must_ answer this immediately, do so. */
+ if ((hints_in && (hints_in->ai_flags & EVUTIL_AI_NUMERICHOST))) {
+ res = NULL;
+ err = evutil_getaddrinfo(nodename, servname, hints_in, &res);
+ cb(err, res, arg);
+ return NULL;
+ }
+
+ if (hints_in) {
+ memcpy(&hints, hints_in, sizeof(hints));
+ } else {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ }
+
+ 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);
+ if (err != EVUTIL_EAI_NEED_RESOLVE) {
+ cb(err, res, arg);
+ return NULL;
+ }
+
+ /* Okay, things are serious now. We're going to need to actually
+ * launch a request.
+ */
+ data = mm_calloc(1,sizeof(struct evdns_getaddrinfo_request));
+ if (!data) {
+ cb(EVUTIL_EAI_MEMORY, NULL, arg);
+ return NULL;
+ }
+
+ memcpy(&data->hints, &hints, sizeof(data->hints));
+ data->port = (ev_uint16_t)port;
+ data->ipv4_request.type = DNS_IPv4_A;
+ data->ipv6_request.type = DNS_IPv6_AAAA;
+ data->user_cb = cb;
+ data->user_data = arg;
+ data->evdns_base = dns_base;
+
+ want_cname = (hints.ai_flags & EVUTIL_AI_CANONNAME);
+
+ /* If we are asked for a PF_UNSPEC address, we launch two requests in
+ * parallel: one for an A address and one for an AAAA address. We
+ * can't send just one request, since many servers only answer one
+ * question per DNS request.
+ *
+ * Once we have the answer to one request, we allow for a short
+ * timeout before we report it, to see if the other one arrives. If
+ * they both show up in time, then we report both the answers.
+ *
+ * If too many addresses of one type time out or fail, we should stop
+ * launching those requests. (XXX we don't do that yet.)
+ */
+
+ if (hints.ai_family != PF_INET6) {
+ log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p",
+ nodename, &data->ipv4_request);
+
+ data->ipv4_request.r = evdns_base_resolve_ipv4(dns_base,
+ nodename, 0, evdns_getaddrinfo_gotresolve,
+ &data->ipv4_request);
+ if (want_cname)
+ data->ipv4_request.r->put_cname_in_ptr =
+ &data->cname_result;
+ }
+ if (hints.ai_family != PF_INET) {
+ log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv6 as %p",
+ nodename, &data->ipv6_request);
+
+ data->ipv6_request.r = evdns_base_resolve_ipv6(dns_base,
+ nodename, 0, evdns_getaddrinfo_gotresolve,
+ &data->ipv6_request);
+ if (want_cname)
+ data->ipv6_request.r->put_cname_in_ptr =
+ &data->cname_result;
+ }
+
+ evtimer_assign(&data->timeout, dns_base->event_base,
+ evdns_getaddrinfo_timeout_cb, data);
+
+ if (data->ipv4_request.r || data->ipv6_request.r) {
+ return data;
+ } else {
+ mm_free(data);
+ cb(EVUTIL_EAI_FAIL, NULL, arg);
+ return NULL;
+ }
+}
+
+void
+evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *data)
+{
+ event_del(&data->timeout);
+ if (data->ipv4_request.r)
+ evdns_cancel_request(data->evdns_base, data->ipv4_request.r);
+ if (data->ipv6_request.r)
+ evdns_cancel_request(data->evdns_base, data->ipv6_request.r);
+ data->ipv4_request.r = data->ipv6_request.r = NULL;
+
+ data->user_cb(EVUTIL_EAI_CANCEL, NULL, data->user_data);
+
+ free_getaddrinfo_request(data);
+}
#include "event-config.h"
+#define _REENTRANT
+#define _GNU_SOURCE
+
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#ifdef _EVENT_HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
-#ifdef _EVENT_HAVE_NETDB_H
-#include <netdb.h>
-#endif
#ifndef _EVENT_HAVE_GETTIMEOFDAY
#include <sys/timeb.h>
#include "event2/util.h"
#include "util-internal.h"
#include "log-internal.h"
+#include "mm-internal.h"
#include "strlcpy-internal.h"
#include "ipv6-internal.h"
return 1;
}
-/** Internal helper: use the host's (blocking) resolver to look up 'hostname',
- * and set the sockaddr pointed to by 'sa' to the answer. Assume we have
- * *socklen bytes of storage; adjust *socklen to the number of bytes used.
- * Try to return answers of type 'family', unless family is AF_UNSPEC.
- * Return 0 on success and -1 on failure. If 'port' is nonzero, it is
- * a port number in host order: set the port in any resulting sockaddr to
- * the specified port.
- */
-int
-evutil_resolve(int family, const char *hostname, struct sockaddr *sa,
- ev_socklen_t *socklen, int port)
+/* We sometimes need to know whether we have an ipv4 address and whether we
+ have an ipv6 address. If 'have_checked_interfaces', then we've already done
+ the test. If 'had_ipv4_address', then it turns out we had an ipv4 address.
+ If 'had_ipv6_address', then it turns out we had an ipv6 address. These are
+ set by evutil_check_interfaces. */
+static int have_checked_interfaces, had_ipv4_address, had_ipv6_address;
+
+/* Test whether we have an ipv4 interface and an ipv6 interface. Return 0 if
+ * the test seemed successful. */
+static int
+evutil_check_interfaces(int force_recheck)
{
-#ifdef _EVENT_HAVE_GETADDRINFO_XXX
- struct addrinfo hint, *hintp=NULL;
- struct addrinfo *ai=NULL;
+ const char ZEROES[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00";
+ evutil_socket_t fd = -1;
+ struct sockaddr_in sin, sin_out;
+ struct sockaddr_in6 sin6, sin6_out;
+ ev_socklen_t sin_out_len = sizeof(sin_out);
+ ev_socklen_t sin6_out_len = sizeof(sin6_out);
int r;
- memset(&hint, 0, sizeof(hint));
+ char buf[128];
+ if (have_checked_interfaces && !force_recheck)
+ return 0;
+
+ /* To check whether we have an interface open for a given protocol, we
+ * try to make a UDP 'connection' to a remote host on the internet.
+ * We don't actually use it, so the address doesn't matter, but we
+ * want to pick one that keep us from using a host- or link-local
+ * interface. */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(53);
+ r = evutil_inet_pton(AF_INET, "18.244.0.188", &sin.sin_addr);
+ EVUTIL_ASSERT(r);
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(53);
+ r = evutil_inet_pton(AF_INET6, "2001:4860:b002::68", &sin6.sin6_addr);
+ EVUTIL_ASSERT(r);
+
+ memset(&sin_out, 0, sizeof(sin_out));
+ memset(&sin6_out, 0, sizeof(sin6_out));
+
+ /* XXX some errnos mean 'no address'; some mean 'not enough sockets'. */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) >= 0 &&
+ connect(fd, (struct sockaddr*)&sin, sizeof(sin)) == 0 &&
+ getsockname(fd, (struct sockaddr*)&sin_out, &sin_out_len) == 0) {
+ /* We might have an IPv4 interface. */
+ ev_uint32_t addr = ntohl(sin_out.sin_addr.s_addr);
+ if (addr == 0 || (addr&0xff000000) == 127 ||
+ (addr && 0xff) == 255 || (addr & 0xf0) == 14) {
+ evutil_inet_ntop(AF_INET, &sin_out.sin_addr,
+ buf, sizeof(buf));
+ /* This is a reserved, ipv4compat, ipv4map, loopback,
+ * link-local or unspecified address. The host should
+ * never have given it to us; it could never connect
+ * to sin. */
+ event_warnx("Got a strange local ipv4 address %s",buf);
+ } else {
+ event_debug(("Detected an IPv4 interface"));
+ had_ipv4_address = 1;
+ }
+ }
+ if (fd >= 0)
+ EVUTIL_CLOSESOCKET(fd);
+
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) >= 0 &&
+ connect(fd, (struct sockaddr*)&sin6, sizeof(sin6)) == 0 &&
+ getsockname(fd, (struct sockaddr*)&sin6_out, &sin6_out_len) == 0) {
+ /* We might have an IPv6 interface. */
+ const unsigned char *addr =
+ (unsigned char*)sin6_out.sin6_addr.s6_addr;
+ if (!memcmp(addr, ZEROES, 8) ||
+ (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80)) {
+ /* This is a reserved, ipv4compat, ipv4map, loopback,
+ * link-local or unspecified address. The host should
+ * never have given it to us; it could never connect
+ * to sin6. */
+ evutil_inet_ntop(AF_INET6, &sin6_out.sin6_addr,
+ buf, sizeof(buf));
+ event_warnx("Got a strange local ipv6 address %s",buf);
+ } else {
+ event_debug(("Detected an IPv4 interface"));
+ had_ipv6_address = 1;
+ }
+ }
+
+ if (fd >= 0)
+ EVUTIL_CLOSESOCKET(fd);
+
+ return 0;
+}
+
+/* Internal addrinfo flag. This one is set when we allocate the addrinfo from
+ * inside libevent. Otherwise, the built-in getaddrinfo() function allocated
+ * it, and we should trust what they said.
+ **/
+#define EVUTIL_AI_LIBEVENT_ALLOCATED 0x80000000
+
+/* Helper: construct a new addrinfo containing the socket address in
+ * 'sa', which must be a sockaddr_in or a sockaddr_in6. Take the
+ * socktype and protocol info from hints. If they weren't set, then
+ * allocate both a TCP and a UDP addrinfo.
+ */
+struct evutil_addrinfo *
+evutil_new_addrinfo(struct sockaddr *sa, ev_socklen_t socklen,
+ const struct evutil_addrinfo *hints)
+{
+ size_t extra;
+ struct evutil_addrinfo *res;
+ EVUTIL_ASSERT(hints);
- if (family != AF_UNSPEC) {
- hint.ai_family = family;
- hintp = &hint;
+ if (hints->ai_socktype == 0 && hints->ai_protocol == 0) {
+ /* Indecisive user! Give them a UDP and a TCP. */
+ struct evutil_addrinfo *r1, *r2;
+ struct evutil_addrinfo tmp;
+ memcpy(&tmp, hints, sizeof(tmp));
+ tmp.ai_socktype = SOCK_STREAM; tmp.ai_protocol = IPPROTO_TCP;
+ r1 = evutil_new_addrinfo(sa, socklen, &tmp);
+ if (!r1)
+ return NULL;
+ tmp.ai_socktype = SOCK_DGRAM; tmp.ai_protocol = IPPROTO_UDP;
+ r2 = evutil_new_addrinfo(sa, socklen, &tmp);
+ if (!r2) {
+ evutil_freeaddrinfo(r2);
+ return NULL;
+ }
+ r1->ai_next = r2;
+ return r1;
}
- r = getaddrinfo(hostname, NULL, hintp, &ai);
+ /* We're going to allocate extra space to hold the sockaddr. */
+ extra = (hints->ai_family == PF_INET) ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ res = mm_calloc(1,sizeof(struct evutil_addrinfo)+socklen);
+ if (!res)
+ return NULL;
+ res->ai_addr = (struct sockaddr*)
+ (((char*)res) + sizeof(struct evutil_addrinfo));
+ memcpy(res->ai_addr, sa, socklen);
+ res->ai_addrlen = socklen;
+ res->ai_family = sa->sa_family; /* Same or not? XXX */
+ res->ai_flags = EVUTIL_AI_LIBEVENT_ALLOCATED;
+ res->ai_socktype = hints->ai_socktype;
+ res->ai_protocol = hints->ai_protocol;
+
+ return res;
+}
+
+/* Append the addrinfo 'append' to the end of 'first', and return the start of
+ * the list. Either element can be NULL, in which case we return the element
+ * that is not NULL. */
+struct evutil_addrinfo *
+evutil_addrinfo_append(struct evutil_addrinfo *first,
+ struct evutil_addrinfo *append)
+{
+ struct evutil_addrinfo *ai = first;
if (!ai)
- return -1;
- if (r || ai->ai_addrlen > *socklen) {
- /* log/report error? */
- freeaddrinfo(ai);
- return -1;
+ return append;
+ while (ai->ai_next)
+ ai = ai->ai_next;
+ ai->ai_next = append;
+
+ return first;
+}
+
+/** Parse a service name in 'servname', which can be a decimal port.
+ * Return the port number, or -1 on error.
+ */
+static int
+evutil_parse_servname(const char *servname, const char *protocol,
+ const struct evutil_addrinfo *hints)
+{
+ int n;
+ char *endptr=NULL;
+ n = (int) strtol(servname, &endptr, 10);
+ if (n>=0 && n <= 65535 && servname[0] && endptr && !endptr[0])
+ return n;
+#ifdef _EVENT_HAVE_GETSERVBYNAME
+ if (!(hints->ai_flags & EVUTIL_AI_NUMERICSERV)) {
+ struct servent *ent = getservbyname(servname, protocol);
+ if (ent) {
+ return ntohs(ent->s_port);
+ }
}
- /* XXX handle multiple return values better. */
- memcpy(sa, ai->ai_addr, ai->ai_addrlen);
- if (port) {
- if (sa->sa_family == AF_INET)
- ((struct sockaddr_in*)sa)->sin_port = htons(port);
- else if (sa->sa_family == AF_INET6)
- ((struct sockaddr_in6*)sa)->sin6_port = htons(port);
+#endif
+ return -1;
+}
+
+/* Return a string corresponding to a protocol number that we can pass to
+ * getservyname. */
+static const char *
+evutil_unparse_protoname(int proto)
+{
+ if (proto == 0)
+ return NULL;
+ else if (proto == IPPROTO_TCP)
+ return "tcp";
+ else if (proto == IPPROTO_UDP)
+ return "udp";
+#ifdef IPPROTO_SCTP
+ else if (proto == IPPROTO_SCTP)
+ return "sctp";
+#endif
+#ifdef _EVENT_HAVE_GETPROTOBYNUMBER
+ {
+ struct protoent *ent = getprotobynumber(proto);
+ if (ent)
+ return ent->p_name;
}
- *socklen = ai->ai_addrlen;
- freeaddrinfo(ai);
- return 0;
-#else
- /* XXXX use gethostbyname_r/gethostbyname2_r where available */
- struct hostent *he;
- struct sockaddr *sa_ptr;
+#endif
+ return NULL;
+}
+
+static void
+evutil_getaddrinfo_infer_protocols(struct evutil_addrinfo *hints)
+{
+ /* If we can guess the protocol from the socktype, do so. */
+ if (!hints->ai_protocol && hints->ai_socktype) {
+ if (hints->ai_socktype == SOCK_DGRAM)
+ hints->ai_protocol = IPPROTO_UDP;
+ else if (hints->ai_socktype == SOCK_STREAM)
+ hints->ai_protocol = IPPROTO_TCP;
+ }
+
+ /* Set the socktype if it isn't set. */
+ if (!hints->ai_socktype && hints->ai_protocol) {
+ if (hints->ai_protocol == IPPROTO_UDP)
+ hints->ai_socktype = SOCK_DGRAM;
+ else if (hints->ai_protocol == IPPROTO_TCP)
+ hints->ai_socktype = SOCK_STREAM;
+#ifdef IPPROTO_SCTP
+ else if (hints->ai_protocol == IPPROTO_SCTP)
+ hints->ai_socktype = SOCK_STREAM;
+#endif
+ }
+}
+
+/** Implements the part of looking up hosts by name that's common to both
+ * the blocking and nonblocking resolver:
+ * - Adjust 'hints' to have a reasonable socktype and protocol.
+ * - Look up the port based on 'servname', and store it in *portnum,
+ * - Handle the nodename==NULL case
+ * - Handle some invalid arguments cases.
+ * - Handle the cases where nodename is an IPv4 or IPv6 address.
+ *
+ * If we need the resolver to look up the hostname, we return
+ * EVUTIL_EAI_NEED_RESOLVE. Otherwise, we can completely implement
+ * getaddrinfo: we return 0 or an appropriate EVUTIL_EAI_* error, and
+ * set *res as getaddrinfo would.
+ */
+int
+evutil_getaddrinfo_common(const char *nodename, const char *servname,
+ struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum)
+{
+ int port = 0;
+ const char *pname;
+
+ if (nodename == NULL && servname == NULL)
+ return EVUTIL_EAI_NONAME;
+
+ /* We only understand 3 families */
+ if (hints->ai_family != PF_UNSPEC && hints->ai_family != PF_INET &&
+ hints->ai_family != PF_INET6)
+ return EVUTIL_EAI_FAMILY;
+
+ evutil_getaddrinfo_infer_protocols(hints);
+
+ /* Look up the port number and protocol, if possible. */
+ pname = evutil_unparse_protoname(hints->ai_protocol);
+ if (servname) {
+ /* XXXX We could look at the protocol we got back from
+ * getservbyname, but it doesn't seem too useful. */
+ port = evutil_parse_servname(servname, pname, hints);
+ if (port < 0) {
+ return EVUTIL_EAI_NONAME;
+ }
+ }
+
+ /* If we have no node name, then we're supposed to bind to 'any' and
+ * connect to localhost. */
+ if (nodename == NULL) {
+ struct evutil_addrinfo *res4=NULL, *res6=NULL;
+ if (hints->ai_family != PF_INET) { /* INET6 or UNSPEC. */
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ if (hints->ai_flags & AI_PASSIVE) {
+ /* Bind to :: */
+ } else {
+ /* connect to ::1 */
+ sin6.sin6_addr.s6_addr[15] = 1;
+ }
+ res6 = evutil_new_addrinfo((struct sockaddr*)&sin6,
+ sizeof(sin6), hints);
+ if (!res6)
+ return EVUTIL_EAI_MEMORY;
+ }
+
+ if (hints->ai_family != PF_INET6) { /* INET or UNSPEC */
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ if (hints->ai_flags & AI_PASSIVE) {
+ /* Bind to 0.0.0.0 */
+ } else {
+ /* connect to 127.0.0.1 */
+ sin.sin_addr.s_addr = htonl(0x7f000001);
+ }
+ res4 = evutil_new_addrinfo((struct sockaddr*)&sin,
+ sizeof(sin), hints);
+ if (!res4) {
+ if (res6)
+ evutil_freeaddrinfo(res6);
+ return EVUTIL_EAI_MEMORY;
+ }
+ }
+ *res = evutil_addrinfo_append(res4, res6);
+ return 0;
+ }
+
+ /* If we can, we should try to parse the hostname without resolving
+ * it. */
+ /* Try ipv6. */
+ if (hints->ai_family == PF_INET6 || hints->ai_family == PF_UNSPEC){
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ if (1==evutil_inet_pton(AF_INET6, nodename, &sin6.sin6_addr)) {
+ /* Got an ipv6 address. */
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ *res = evutil_new_addrinfo((struct sockaddr*)&sin6,
+ sizeof(sin6), hints);
+ if (!*res)
+ return EVUTIL_EAI_MEMORY;
+ return 0;
+ }
+ }
+
+ /* Try ipv4. */
+ if (hints->ai_family == PF_INET || hints->ai_family == PF_UNSPEC) {
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ if (1==evutil_inet_pton(AF_INET, nodename, &sin.sin_addr)) {
+ /* Got an ipv6 address. */
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ *res = evutil_new_addrinfo((struct sockaddr*)&sin,
+ sizeof(sin), hints);
+ if (!*res)
+ return EVUTIL_EAI_MEMORY;
+ return 0;
+ }
+ }
+
+
+ /* If we have reached this point, we definitely need to do a DNS
+ * lookup. */
+ if ((hints->ai_flags & EVUTIL_AI_NUMERICHOST)) {
+ /* If we're not allowed to do one, then say so. */
+ return EVUTIL_EAI_NONAME;
+ }
+ *portnum = port;
+ return EVUTIL_EAI_NEED_RESOLVE;
+}
+
+#ifdef _EVENT_HAVE_GETADDRINFO
+#define USE_NATIVE_GETADDRINFO
+#endif
+
+#ifndef USE_NATIVE_GETADDRINFO
+/* Helper for systems with no getaddrinfo(): make one or more addrinfos out of
+ * a struct hostent.
+ */
+static struct evutil_addrinfo *
+addrinfo_from_hostent(const struct hostent *ent,
+ int port, const struct evutil_addrinfo *hints)
+{
+ int i;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
- ev_socklen_t slen;
- he = gethostbyname(hostname);
- if (!he || !he->h_length) {
- return -1;
- }
- /* XXX handle multiple return values better. */
- if (he->h_addrtype == AF_INET) {
- if (family != AF_INET && family != AF_UNSPEC)
- return -1;
+ struct sockaddr *sa;
+ int socklen;
+ struct evutil_addrinfo *res=NULL, *ai;
+ void *addrp;
+
+ if (ent->h_addrtype == PF_INET) {
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
- memcpy(&sin.sin_addr, he->h_addr_list[0], 4);
- sa_ptr = (struct sockaddr*)&sin;
- slen = sizeof(struct sockaddr_in);
- } else if (he->h_addrtype == AF_INET6) {
- if (family != AF_INET6 && family != AF_UNSPEC)
- return -1;
+ sa = (struct sockaddr *)&sin;
+ socklen = sizeof(struct sockaddr_in);
+ addrp = &sin.sin_addr;
+ if (ent->h_length != sizeof(sin.sin_addr)) {
+ event_warnx("Weird h_length from gethostbyname");
+ return NULL;
+ }
+ } else if (ent->h_addrtype == PF_INET6) {
+ memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
- memset(&sin6, 0, sizeof(sin6));
- memcpy(sin6.sin6_addr.s6_addr, &he->h_addr_list[1], 16);
- sa_ptr = (struct sockaddr*)&sin6;
- slen = sizeof(struct sockaddr_in6);
+ sa = (struct sockaddr *)&sin6;
+ socklen = sizeof(struct sockaddr_in);
+ addrp = &sin6.sin6_addr;
+ if (ent->h_length != sizeof(sin6.sin6_addr)) {
+ event_warnx("Weird h_length from gethostbyname");
+ return NULL;
+ }
+ } else
+ return NULL;
+
+ for (i = 0; ent->h_addr_list[i]; ++i) {
+ memcpy(addrp, ent->h_addr_list[i], ent->h_length);
+ ai = evutil_new_addrinfo(sa, socklen, hints);
+ if (!ai) {
+ evutil_freeaddrinfo(res);
+ return NULL;
+ }
+ res = evutil_addrinfo_append(res, ai);
+ }
+
+ if (res && ((hints->ai_flags & EVUTIL_AI_CANONNAME) && ent->h_name))
+ res->ai_canonname = mm_strdup(ent->h_name);
+
+ return res;
+}
+#endif
+
+/* If the EVUTIL_AI_ADDRCONFIG flag is set on hints->ai_flags, and
+ * hints->ai_family is PF_UNSPEC, then revise the value of hints->ai_family so
+ * that we'll only get addresses we could maybe connect to.
+ */
+void
+evutil_adjust_hints_for_addrconfig(struct evutil_addrinfo *hints)
+{
+ if (!(hints->ai_flags & EVUTIL_AI_ADDRCONFIG))
+ return;
+ if (hints->ai_family != PF_UNSPEC)
+ return;
+ if (!have_checked_interfaces)
+ evutil_check_interfaces(0);
+ if (had_ipv4_address && !had_ipv6_address) {
+ hints->ai_family = PF_INET;
+ } else if (!had_ipv4_address && had_ipv6_address) {
+ hints->ai_family = PF_INET6;
+ }
+}
+
+int
+evutil_getaddrinfo(const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in, struct evutil_addrinfo **res)
+{
+#ifdef USE_NATIVE_GETADDRINFO
+#if !defined(AI_ADDRCONFIG) || !defined(AI_NUMERICSERV) || defined(WIN32)
+ struct evutil_addrinfo hints;
+ if (hints_in) {
+ memcpy(&hints, hints_in, sizeof(hints));
+ hints_in = &hints;
+
+#ifndef AI_ADDRCONFIG
+ /* Not every system has AI_ADDRCONFIG, so fake it. */
+ if (hints.ai_family == PF_UNSPEC &&
+ (hints.ai_flags & EVUTIL_AI_ADDRCONFIG)) {
+ evutil_adjust_hints_for_addrconfig(&hints);
+ }
+#endif
+
+#ifndef AI_NUMERICSERV
+ /* Not every system has AI_NUMERICSERV, so fake it. */
+ if (hints.ai_flags & EVUTIL_AI_NUMERICSERV) {
+ if (evutil_parse_servname(servname,
+ NULL, &hints) < 0)
+ return EVUTIL_EAI_NONAME;
+ }
+#endif
+
+#ifdef WIN32
+ /* Windows handles enough cases here weirdly enough that we
+ * are better off just overriding a bunch of them. */
+ {
+ int err, port;
+ err = evutil_getaddrinfo_common(nodename,servname,&hints,
+ res, &port);
+ if (err != EVUTIL_EAI_NEED_RESOLVE)
+ return err;
+ }
+#endif
+ }
+#endif
+
+ return getaddrinfo(nodename, servname, hints_in, res);
+#else
+ int port=0, err;
+ struct hostent *ent = NULL;
+ struct evutil_addrinfo hints;
+
+ if (hints_in) {
+ memcpy(&hints, hints_in, sizeof(hints));
} else {
- event_warnx("gethostbyname returned unknown family %d",
- he->h_addrtype);
- return -1;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
}
- if (slen > *socklen) {
- return -1;
+
+ evutil_adjust_hints_for_addrconfig(&hints);
+
+ err = evutil_getaddrinfo_common(nodename, servname, &hints, res, &port);
+ if (err != EVUTIL_EAI_NEED_RESOLVE) {
+ /* We either succeeded or failed. No need to continue */
+ return err;
}
- memcpy(sa, sa_ptr, slen);
- *socklen = slen;
+
+ err = 0;
+ /* Use any of the various gethostbyname_r variants as available. */
+ {
+#ifdef _EVENT_HAVE_GETHOSTBYNAME_R_6_ARG
+ /* This one is what glibc provides. */
+ char buf[2048];
+ struct hostent hostent;
+ int r;
+ r = gethostbyname_r(nodename, &hostent, buf, sizeof(buf), &ent,
+ &err);
+#elif defined(_EVENT_HAVE_GETHOSTBYNAME_R_5_ARG)
+ char buf[2048];
+ struct hostent hostent;
+ ent = gethostbyname_r(nodename, &hostent, buf, sizeof(buf),
+ &err);
+#elif defined(_EVENT_HAVE_GETHOSTBYNAME_R_3_ARG)
+ struct hostent_data data;
+ struct hostent hostent;
+ memset(&data, 0, sizeof(data));
+ err = gethostbyname_r(nodename, &hostent, &data);
+ ent = err ? NULL : &hostent;
+#else
+ /* fall back to gethostbyname. */
+ /* XXXX This needs a lock everywhere but Windows. */
+ ent = gethostbyname(nodename);
+#ifdef WIN32
+ err = WSAGetLastError();
+#else
+ err = h_errno;
+#endif
+#endif
+
+ /* Now we have either ent or err set. */
+ if (!ent) {
+ /* XXX is this right for windows ? */
+ switch (err) {
+ case TRY_AGAIN:
+ return EVUTIL_EAI_AGAIN;
+ case NO_RECOVERY:
+ default:
+ return EVUTIL_EAI_FAIL;
+ case HOST_NOT_FOUND:
+ return EVUTIL_EAI_NONAME;
+ case NO_ADDRESS:
+#if NO_DATA != NO_ADDRESS
+ case NO_DATA:
+#endif
+ return EVUTIL_EAI_NODATA;
+ }
+ }
+
+ if (ent->h_addrtype != hints.ai_family &&
+ hints.ai_family != PF_UNSPEC) {
+ /* This wasn't the type we were hoping for. Too bad
+ * we never had a chance to ask gethostbyname for what
+ * we wanted. */
+ return EVUTIL_EAI_NONAME;
+ }
+
+ /* Make sure we got _some_ answers. */
+ if (ent->h_length == 0)
+ return EVUTIL_EAI_NODATA;
+
+ /* If we got an address type we don't know how to make a
+ sockaddr for, give up. */
+ if (ent->h_addrtype != PF_INET && ent->h_addrtype != PF_INET6)
+ return EVUTIL_EAI_FAMILY;
+
+ *res = addrinfo_from_hostent(ent, port, &hints);
+ if (! *res)
+ return EVUTIL_EAI_MEMORY;
+ }
+
return 0;
#endif
}
+void
+evutil_freeaddrinfo(struct evutil_addrinfo *ai)
+{
+#ifdef _EVENT_HAVE_GETADDRINFO
+ if (!(ai->ai_flags & EVUTIL_AI_LIBEVENT_ALLOCATED)) {
+ freeaddrinfo(ai);
+ return;
+ }
+#endif
+ while (ai) {
+ struct evutil_addrinfo *next = ai->ai_next;
+ if (ai->ai_canonname)
+ mm_free(ai->ai_canonname);
+ mm_free(ai);
+ ai = next;
+ }
+}
+
+static evdns_getaddrinfo_fn evdns_getaddrinfo_impl = NULL;
+
+void
+evutil_set_evdns_getaddrinfo_fn(evdns_getaddrinfo_fn fn)
+{
+ if (!evdns_getaddrinfo_impl)
+ evdns_getaddrinfo_impl = fn;
+}
+
+/* Internal helper function: act like evdns_getaddrinfo if dns_base is set;
+ * otherwise do a blocking resolve and pass the result to the callback in the
+ * way that evdns_getaddrinfo would.
+ */
+int
+evutil_getaddrinfo_async(struct evdns_base *dns_base,
+ const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in,
+ void (*cb)(int, struct evutil_addrinfo *, void *), void *arg)
+{
+ if (dns_base && evdns_getaddrinfo_impl) {
+ evdns_getaddrinfo_impl(
+ dns_base, nodename, servname, hints_in, cb, arg);
+ } else {
+ struct evutil_addrinfo *ai=NULL;
+ int err;
+ err = evutil_getaddrinfo(nodename, servname, hints_in, &ai);
+ cb(err, ai, arg);
+ }
+ return 0;
+}
+
+const char *
+evutil_gai_strerror(int err)
+{
+ switch (err) {
+ case EVUTIL_EAI_CANCEL: return "Request cancelled";
+#ifdef USE_NATIVE_GETADDRINFO
+ default:
+ return gai_strerror(err);
+#else
+ case 0: return "No error";
+ case EVUTIL_EAI_ADDRFAMILY:
+ return "address family for nodename not supported";
+ case EVUTIL_EAI_AGAIN:
+ return "temporary failure in name resolution";
+ case EVUTIL_EAI_BADFLAGS:
+ return "invalid value for ai_flags";
+ case EVUTIL_EAI_FAIL:
+ return "non-recoverable failure in name resolution";
+ case EVUTIL_EAI_FAMILY:
+ return "ai_family not supported";
+ case EVUTIL_EAI_MEMORY:
+ return "memory allocation failure";
+ case EVUTIL_EAI_NODATA:
+ return "no address associated with nodename";
+ case EVUTIL_EAI_NONAME:
+ return "nodename nor servname provided, or not known";
+ case EVUTIL_EAI_SERVICE:
+ return "servname not supported for ai_socktype";
+ case EVUTIL_EAI_SOCKTYPE:
+ return "ai_socktype not supported";
+ case EVUTIL_EAI_SYSTEM: return "system error";
+ default:
+ return "Unknown error code";
+#endif
+ }
+}
+
#ifdef WIN32
#define E(code, s) { code, (s " [" #code " ]") }
static struct { int code; const char *msg; } windows_socket_errors[] = {
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#else
+#include <ws2tcpip.h>
#endif
#include <sys/queue.h>
#define NI_MAXSERV 32
#define NI_MAXHOST 1025
+#ifndef NI_NUMERICHOST
#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
#define NI_NUMERICSERV 2
+#endif
static int
fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
#endif
-#ifndef _EVENT_HAVE_GETADDRINFO
-struct addrinfo {
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- size_t ai_addrlen;
- struct sockaddr *ai_addr;
- struct addrinfo *ai_next;
-};
-static int
-fake_getaddrinfo(const char *hostname, struct addrinfo *ai)
-{
- struct hostent *he = NULL;
- struct sockaddr_in *sa;
- if (hostname) {
- he = gethostbyname(hostname);
- if (!he)
- return (-1);
- }
- ai->ai_family = he ? he->h_addrtype : AF_INET;
- ai->ai_socktype = SOCK_STREAM;
- ai->ai_protocol = 0;
- ai->ai_addrlen = sizeof(struct sockaddr_in);
- if (NULL == (ai->ai_addr = mm_malloc(ai->ai_addrlen)))
- return (-1);
- sa = (struct sockaddr_in*)ai->ai_addr;
- memset(sa, 0, ai->ai_addrlen);
- if (he) {
- sa->sin_family = he->h_addrtype;
- memcpy(&sa->sin_addr, he->h_addr_list[0], he->h_length);
- } else {
- sa->sin_family = AF_INET;
- sa->sin_addr.s_addr = INADDR_ANY;
- }
- ai->ai_next = NULL;
- return (0);
-}
-static void
-fake_freeaddrinfo(struct addrinfo *ai)
-{
- mm_free(ai->ai_addr);
-}
-#endif
-
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
extern int debug;
static int socket_connect(evutil_socket_t kefd, const char *address, unsigned short port);
-static evutil_socket_t bind_socket_ai(struct addrinfo *, int reuse);
+static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
static int evhttp_associate_new_request_with_connection(
* Network helper functions that we do not want to export to the rest of
* the world.
*/
-#if 0 /* Unused */
-static struct addrinfo *
-addr_from_name(char *address)
-{
-#ifdef _EVENT_HAVE_GETADDRINFO
- struct addrinfo ai, *aitop;
- int ai_result;
-
- memset(&ai, 0, sizeof(ai));
- ai.ai_family = AF_INET;
- ai.ai_socktype = SOCK_RAW;
- ai.ai_flags = 0;
- if ((ai_result = getaddrinfo(address, NULL, &ai, &aitop)) != 0) {
- if ( ai_result == EAI_SYSTEM )
- event_warn("getaddrinfo");
- else
- event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
- }
-
- return (aitop);
-#else
- EVUTIL_ASSERT(0);
- return NULL; /* XXXXX Use gethostbyname, if this function is ever used. */
-#endif
-}
-#endif
static void
name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
NI_NUMERICHOST|NI_NUMERICSERV);
if (ni_result != 0) {
+#ifdef EAI_SYSTEM
+ /* Windows doesn't have an EAI_SYSTEM. */
if (ni_result == EAI_SYSTEM)
event_err(1, "getnameinfo failed");
else
+#endif
event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
return;
}
/* Create a non-blocking socket and bind it */
/* todo: rename this function */
static evutil_socket_t
-bind_socket_ai(struct addrinfo *ai, int reuse)
+bind_socket_ai(struct evutil_addrinfo *ai, int reuse)
{
evutil_socket_t fd;
return (-1);
}
-static struct addrinfo *
+static struct evutil_addrinfo *
make_addrinfo(const char *address, ev_uint16_t port)
{
- struct addrinfo *aitop = NULL;
+ struct evutil_addrinfo *ai = NULL;
-#ifdef _EVENT_HAVE_GETADDRINFO
- struct addrinfo ai;
+ struct evutil_addrinfo hints;
char strport[NI_MAXSERV];
int ai_result;
- memset(&ai, 0, sizeof(ai));
- ai.ai_family = AF_UNSPEC;
- ai.ai_socktype = SOCK_STREAM;
- ai.ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ /* turn NULL hostname into INADDR_ANY, and skip looking up any address
+ * types we don't have an interface to connect to. */
+ hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG;
evutil_snprintf(strport, sizeof(strport), "%d", port);
- if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) {
- if ( ai_result == EAI_SYSTEM )
+ if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai))
+ != 0) {
+ if (ai_result == EVUTIL_EAI_SYSTEM)
event_warn("getaddrinfo");
else
- event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
+ event_warnx("getaddrinfo: %s",
+ evutil_gai_strerror(ai_result));
return (NULL);
}
-#else
- static int cur;
- static struct addrinfo ai[2]; /* We will be returning the address of some of this memory so it has to last even after this call. */
- if (++cur == 2) cur = 0; /* allow calling this function twice */
-
- if (fake_getaddrinfo(address, &ai[cur]) < 0) {
- event_warn("fake_getaddrinfo");
- return (NULL);
- }
- aitop = &ai[cur];
- ((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port);
-#endif
- return (aitop);
+ return (ai);
}
static evutil_socket_t
bind_socket(const char *address, ev_uint16_t port, int reuse)
{
evutil_socket_t fd;
- struct addrinfo *aitop = NULL;
+ struct evutil_addrinfo *aitop = NULL;
/* just create an unbound socket */
if (address == NULL && port == 0)
fd = bind_socket_ai(aitop, reuse);
-#ifdef _EVENT_HAVE_GETADDRINFO
- freeaddrinfo(aitop);
-#else
- fake_freeaddrinfo(aitop);
-#endif
+ evutil_freeaddrinfo(aitop);
return (fd);
}
static int
socket_connect(evutil_socket_t fd, const char *address, unsigned short port)
{
- struct addrinfo *ai = make_addrinfo(address, port);
+ struct evutil_addrinfo *ai = make_addrinfo(address, port);
int res = -1;
if (ai == NULL) {
res = 0;
out:
-#ifdef _EVENT_HAVE_GETADDRINFO
- freeaddrinfo(ai);
-#else
- fake_freeaddrinfo(ai);
-#endif
+ evutil_freeaddrinfo(ai);
return (res);
}
*/
int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len);
+/** Callback for evdns_getaddrinfo. */
+typedef void (*evdns_getaddrinfo_cb)(int result, struct evutil_addrinfo *res, void *arg);
+
+struct evdns_base;
+struct evdns_getaddrinfo_request;
+/** Make a non-blocking getaddrinfo request using the dns_base in 'dns_base'.
+ *
+ * If we can answer the request immediately (with an error or not!), then we
+ * invoke cb immediately and return NULL. Otherwise we return
+ * an evdns_getaddrinfo_request and invoke cb later.
+ *
+ * When the callback is invoked, we pass as its first argument the error code
+ * that getaddrinfo would return (or 0 for no error). As its second argument,
+ * we pass the evutil_addrinfo structures we found (or NULL on error). We
+ * pass 'arg' as the third argument.
+ *
+ * - The AI_V4MAPPED and AI_ALL flags are not currently implemented.
+ * - We don't look at the /etc/hosts file.
+ */
+struct evdns_getaddrinfo_request *evdns_getaddrinfo(
+ struct evdns_base *dns_base,
+ const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in,
+ evdns_getaddrinfo_cb cb, void *arg);
+
+/* Cancel an in-progress evdns_getaddrinfo. This MUST NOT be called after the
+ * getaddrinfo's callback has been invoked. The resolves will be cancelled,
+ * and the callback will be invoked with the error EVUTIL_EAI_CANCEL. */
+void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *req);
+
#ifdef __cplusplus
}
#endif
*/
int evdns_init(void);
+struct evdns_base;
+/**
+ Return the global evdns_base created by event_init() and used by the other
+ deprecated functions.
+
+ @deprecated This function is deprecated because use of the global
+ evdns_base is error-prone.
+ */
+struct evdns_base *evdns_get_global_base(void);
+
/**
Shut down the asynchronous DNS resolver and terminate all active requests.
#include <BaseTsd.h>
#endif
#include <stdarg.h>
+#ifdef _EVENT_HAVE_NETDB_H
+#define _GNU_SOURCE
+#include <netdb.h>
+#endif
/* Integer type definitions for types that are supposed to be defined in the
* C99-specified stdint.h. Shamefully, some platforms do not include
*/
int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n);
+/* Here we define evutil_addrinfo to the native addrinfo type, or redefinte it
+ * if this system has no getaddrinfo(). */
+#ifdef _EVENT_HAVE_STRUCT_ADDRINFO
+#define evutil_addrinfo addrinfo
+#else
+struct evutil_addrinfo {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for nodename */
+ struct sockaddr *ai_addr; /* binary address */
+ struct evutil_addrinfo *ai_next; /* next structure in linked list */
+};
+#endif
+#ifdef EAI_ADDRFAMILY
+#define EVUTIL_EAI_ADDRFAMILY EAI_ADDRFAMILY
+#else
+#define EVUTIL_EAI_ADDRFAMILY -901
+#endif
+#ifdef EAI_AGAIN
+#define EVUTIL_EAI_AGAIN EAI_AGAIN
+#else
+#define EVUTIL_EAI_AGAIN -902
+#endif
+#ifdef EAI_BADFLAGS
+#define EVUTIL_EAI_BADFLAGS EAI_BADFLAGS
+#else
+#define EVUTIL_EAI_BADFLAGS -903
+#endif
+#ifdef EAI_FAIL
+#define EVUTIL_EAI_FAIL EAI_FAIL
+#else
+#define EVUTIL_EAI_FAIL -904
+#endif
+#ifdef EAI_FAMILY
+#define EVUTIL_EAI_FAMILY EAI_FAMILY
+#else
+#define EVUTIL_EAI_FAMILY -905
+#endif
+#ifdef EAI_MEMORY
+#define EVUTIL_EAI_MEMORY EAI_MEMORY
+#else
+#define EVUTIL_EAI_MEMORY -906
+#endif
+/* This test is a bit complicated, since some MS SDKs decide to
+ * remove NODATA or redefine it to be the same as NONAME, in a
+ * fun interpretation of RFC 2553 and RFC 3493. */
+#if defined(EAI_NODATA) && (!defined(EAI_NONAME) || EAI_NODATA != EAI_NONAME)
+#define EVUTIL_EAI_NODATA EAI_NODATA
+#else
+#define EVUTIL_EAI_NODATA -907
+#endif
+#ifdef EAI_NONAME
+#define EVUTIL_EAI_NONAME EAI_NONAME
+#else
+#define EVUTIL_EAI_NONAME -908
+#endif
+#ifdef EAI_SERVICE
+#define EVUTIL_EAI_SERVICE EAI_SERVICE
+#else
+#define EVUTIL_EAI_SERVICE -909
+#endif
+#ifdef EAI_SOCKTYPE
+#define EVUTIL_EAI_SOCKTYPE EAI_SOCKTYPE
+#else
+#define EVUTIL_EAI_SOCKTYPE -910
+#endif
+#ifdef EAI_SYSTEM
+#define EVUTIL_EAI_SYSTEM EAI_SYSTEM
+#else
+#define EVUTIL_EAI_SYSTEM -911
+#endif
+
+#define EVUTIL_EAI_CANCEL -90001
+
+#ifdef AI_PASSIVE
+#define EVUTIL_AI_PASSIVE AI_PASSIVE
+#else
+#define EVUTIL_AI_PASSIVE 0x1000
+#endif
+#ifdef AI_CANONNAME
+#define EVUTIL_AI_CANONNAME AI_CANONNAME
+#else
+#define EVUTIL_AI_CANONNAME 0x2000
+#endif
+#ifdef AI_NUMERICHOST
+#define EVUTIL_AI_NUMERICHOST AI_NUMERICHOST
+#else
+#define EVUTIL_AI_NUMERICHOST 0x4000
+#endif
+#ifdef AI_NUMERICSERV
+#define EVUTIL_AI_NUMERICSERV AI_NUMERICSERV
+#else
+#define EVUTIL_AI_NUMERICSERV 0x8000
+#endif
+#ifdef AI_V4MAPPED
+#define EVUTIL_AI_V4MAPPED AI_V4MAPPED
+#else
+#define EVUTIL_AI_V4MAPPED 0x10000
+#endif
+#ifdef AI_ALL
+#define EVUTIL_AI_ALL AI_ALL
+#else
+#define EVUTIL_AI_ALL 0x20000
+#endif
+#ifdef AI_ADDRCONFIG
+#define EVUTIL_AI_ADDRCONFIG AI_ADDRCONFIG
+#else
+#define EVUTIL_AI_ADDRCONFIG 0x40000
+#endif
+
+struct evutil_addrinfo;
+/* This function clones getaddrinfo for systems that don't have it. For full
+ * details, see RFC 3493, section 6.1.
+ *
+ * Limitations:
+ * - When the system has no getaddrinfo, we fall back to gethostbyname_r or
+ * gethostbyname, with their attendant issues.
+ * - The AI_V4MAPPED and AI_ALL flags are not currently implemented.
+ *
+ * For a nonblocking variant, see evdns_getaddrinfo.
+ */
+int evutil_getaddrinfo(const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in, struct evutil_addrinfo **res);
+
+/* Release storage allocated by evutil_getaddrinfo or evdns_getaddrinfo. */
+void evutil_freeaddrinfo(struct evutil_addrinfo *ai);
+
+const char *evutil_gai_strerror(int err);
+
#ifdef __cplusplus
}
#endif
fflush(stdout);
}
+static void
+gai_callback(int err, struct evutil_addrinfo *ai, void *arg)
+{
+ const char *name = arg;
+ struct evutil_addrinfo *ai_first = NULL;
+ int i;
+ if (err) {
+ printf("%s: %s\n", name, evutil_gai_strerror(err));
+ }
+ if (ai && ai->ai_canonname)
+ printf(" %s ==> %s\n", name, ai->ai_canonname);
+ for (i=0; ai; ai = ai->ai_next, ++i) {
+ char buf[128];
+ if (ai->ai_family == PF_INET) {
+ struct sockaddr_in *sin =
+ (struct sockaddr_in*)ai->ai_addr;
+ evutil_inet_ntop(AF_INET, &sin->sin_addr, buf,
+ sizeof(buf));
+ printf("[%d] %s: %s\n",i,name,buf);
+ } else {
+ struct sockaddr_in6 *sin6 =
+ (struct sockaddr_in6*)ai->ai_addr;
+ evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf,
+ sizeof(buf));
+ printf("[%d] %s: %s\n",i,name,buf);
+ }
+ }
+ if (ai_first)
+ evutil_freeaddrinfo(ai_first);
+}
+
static void
evdns_server_callback(struct evdns_server_request *req, void *data)
{
int
main(int c, char **v) {
int idx;
- int reverse = 0, servertest = 0;
+ int reverse = 0, servertest = 0, use_getaddrinfo = 0;
struct event_base *event_base = NULL;
struct evdns_base *evdns_base = NULL;
if (c<2) {
reverse = 1;
else if (!strcmp(v[idx], "-v"))
verbose = 1;
+ else if (!strcmp(v[idx], "-g"))
+ use_getaddrinfo = 1;
else if (!strcmp(v[idx], "-servertest"))
servertest = 1;
else
"/etc/resolv.conf");
#endif
}
+
+ printf("EVUTIL_AI_CANONNAME in example = %d\n", EVUTIL_AI_CANONNAME);
for (; idx < c; ++idx) {
if (reverse) {
struct in_addr addr;
- if (!inet_aton(v[idx], &addr)) {
+ if (evutil_inet_pton(AF_INET, v[idx], &addr)!=1) {
fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
continue;
}
fprintf(stderr, "resolving %s...\n",v[idx]);
evdns_base_resolve_reverse(evdns_base, &addr, 0, main_callback, v[idx]);
+ } else if (use_getaddrinfo) {
+ struct evutil_addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+ evdns_getaddrinfo(evdns_base, v[idx], NULL, &hints,
+ gai_callback, v[idx]);
} else {
fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
evdns_base_resolve_ipv4(evdns_base, v[idx], 0, main_callback, v[idx]);
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
test_## name }
+struct evutil_addrinfo;
+struct evutil_addrinfo *ai_find_by_family(struct evutil_addrinfo *ai, int f);
+struct evutil_addrinfo *ai_find_by_protocol(struct evutil_addrinfo *ai, int p);
+int _test_ai_eq(const struct evutil_addrinfo *ai, const char *sockaddr_port,
+ int socktype, int protocol, int line);
+
+#define test_ai_eq(ai, str, s, p) do { \
+ if (_test_ai_eq((ai), (str), (s), (p), __LINE__)<0) \
+ goto end; \
+ } while(0)
+
#ifdef __cplusplus
}
static int dns_got_cancel = 0;
static int dns_err = 0;
+static int get_socket_port(evutil_socket_t fd);
+
static void
dns_gethostbyname_cb(int result, char type, int count, int ttl,
void *addresses, void *arg)
static struct evdns_server_port *
get_generic_server(struct event_base *base,
- ev_uint16_t portnum,
+ ev_uint16_t *portnum,
evdns_request_callback_fn_type cb,
void *arg)
{
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(portnum);
+ my_addr.sin_port = htons(*portnum);
my_addr.sin_addr.s_addr = htonl(0x7f000001UL);
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) {
tt_abort_perror("bind");
}
port = evdns_add_server_port_with_base(base, sock, 0, cb, arg);
+ if (!*portnum)
+ *portnum = get_socket_port(sock);
return port;
end:
struct event_base *base = data->base;
struct evdns_server_port *port = NULL;
struct evdns_base *dns = NULL;
+ ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
struct generic_dns_callback_result r1, r2, r3, r4, r5;
- port = get_generic_server(base, 53900, generic_dns_server_cb,
+ port = get_generic_server(base, &portnum, generic_dns_server_cb,
search_table);
tt_assert(port);
struct evdns_server_port *port = NULL;
struct evdns_base *dns = NULL;
int drop_count = 2;
+ ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
struct generic_dns_callback_result r1;
- port = get_generic_server(base, 53900, fail_server_cb,
+ port = get_generic_server(base, &portnum, fail_server_cb,
&drop_count);
tt_assert(port);
struct evdns_server_port *port1 = NULL, *port2 = NULL;
struct evdns_base *dns = NULL;
struct generic_dns_callback_result r1;
+ ev_uint16_t portnum1 = 53900, portnum2=53901;
- port1 = get_generic_server(base, 53900, generic_dns_server_cb,
+ port1 = get_generic_server(base, &portnum1, generic_dns_server_cb,
internal_error_table);
tt_assert(port1);
- port2 = get_generic_server(base, 53901, generic_dns_server_cb,
+ port2 = get_generic_server(base, &portnum2, generic_dns_server_cb,
reissue_table);
tt_assert(port2);
struct event_base *base = data->base;
struct evdns_server_port *port = NULL;
struct evdns_base *dns = NULL;
+ ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
struct generic_dns_callback_result r[20];
int i;
- port = get_generic_server(base, 53900, generic_dns_server_cb,
+ port = get_generic_server(base, &portnum, generic_dns_server_cb,
reissue_table);
tt_assert(port);
static int total_connected_or_failed = 0;
static struct event_base *be_connect_hostname_base = NULL;
-/* Implements a DNS server for the connect_hostname test. */
+/* Implements a DNS server for the connect_hostname test and the
+ * getaddrinfo_async test */
static void
-be_connect_hostname_server_cb(struct evdns_server_request *req, void *data)
+be_getaddrinfo_server_cb(struct evdns_server_request *req, void *data)
{
int i;
int *n_got_p=data;
const int qclass = req->questions[i]->dns_question_class;
const char *qname = req->questions[i]->name;
struct in_addr ans;
+ struct in6_addr ans6;
+ memset(&ans6, 0, sizeof(ans6));
if (qtype == EVDNS_TYPE_A &&
qclass == EVDNS_CLASS_INET &&
} else if (!evutil_ascii_strcasecmp(qname,
"nosuchplace.example.com")) {
/* ok, just say notfound. */
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "both.example.com")) {
+ if (qtype == EVDNS_TYPE_A) {
+ ans.s_addr = htonl(0x50502020);
+ evdns_server_request_add_a_reply(req, qname,
+ 1, &ans.s_addr, 2000);
+ added_any = 1;
+ } else if (qtype == EVDNS_TYPE_AAAA) {
+ ans6.s6_addr[0] = 0x80;
+ ans6.s6_addr[1] = 0xff;
+ ans6.s6_addr[14] = 0xbb;
+ ans6.s6_addr[15] = 0xbb;
+ evdns_server_request_add_aaaa_reply(req, qname,
+ 1, &ans6.s6_addr, 2000);
+ added_any = 1;
+ }
+ evdns_server_request_add_cname_reply(req, qname,
+ "both-canonical.example.com", 1000);
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v4only.example.com") ||
+ !evutil_ascii_strcasecmp(qname, "v4assert.example.com")) {
+ if (qtype == EVDNS_TYPE_A) {
+ ans.s_addr = htonl(0x12345678);
+ evdns_server_request_add_a_reply(req, qname,
+ 1, &ans.s_addr, 2000);
+ added_any = 1;
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v4assert.example.com")) {
+ TT_FAIL(("Got an AAAA request for v4assert"));
+ }
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v6only.example.com") ||
+ !evutil_ascii_strcasecmp(qname, "v6assert.example.com")) {
+ if (qtype == EVDNS_TYPE_AAAA) {
+ ans6.s6_addr[0] = 0x0b;
+ ans6.s6_addr[1] = 0x0b;
+ ans6.s6_addr[14] = 0xf0;
+ ans6.s6_addr[15] = 0x0d;
+ evdns_server_request_add_aaaa_reply(req, qname,
+ 1, &ans6.s6_addr, 2000);
+ added_any = 1;
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v6assert.example.com")) {
+ TT_FAIL(("Got a A request for v6assert"));
+ }
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v6timeout.example.com")) {
+ if (qtype == EVDNS_TYPE_A) {
+ ans.s_addr = htonl(0xabcdef01);
+ evdns_server_request_add_a_reply(req, qname,
+ 1, &ans.s_addr, 2000);
+ added_any = 1;
+ } else if (qtype == EVDNS_TYPE_AAAA) {
+ /* Let the v6 request time out.*/
+ evdns_server_request_drop(req);
+ return;
+ }
+ } else if (!evutil_ascii_strcasecmp(qname,
+ "v6timeout-nonexist.example.com")) {
+ if (qtype == EVDNS_TYPE_A) {
+ /* Fall through, give an nexist. */
+ } else if (qtype == EVDNS_TYPE_AAAA) {
+ /* Let the v6 request time out.*/
+ evdns_server_request_drop(req);
+ return;
+ }
} else {
TT_GRIPE(("Got weird request for %s",qname));
}
if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) {
++total_connected_or_failed;
+ TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed));
if (total_connected_or_failed >= 5)
event_base_loopexit(be_connect_hostname_base,
NULL);
struct evdns_server_port *port=NULL;
evutil_socket_t server_fd=-1;
struct sockaddr_in sin;
- int listener_port=-1, dns_port=-1;
+ int listener_port=-1;
+ ev_uint16_t dns_port=0;
int n_accept=0, n_dns=0;
char buf[128];
-1, (struct sockaddr *)&sin, sizeof(sin));
listener_port = get_socket_port(evconnlistener_get_fd(listener));
+#if 0
/* Start an evdns server that resolves nobodaddy.example.com to
* 127.0.0.1 */
memset(&sin, 0, sizeof(sin));
evutil_make_socket_nonblocking(server_fd);
dns_port = get_socket_port(server_fd);
port = evdns_add_server_port_with_base(data->base, server_fd, 0,
- be_connect_hostname_server_cb, &n_dns);
+ be_getaddrinfo_server_cb, &n_dns);
+#endif
+ port = get_generic_server(data->base, &dns_port,
+ be_getaddrinfo_server_cb, &n_dns);
+ tt_assert(port);
+ tt_int_op(dns_port, >=, 0);
/* Start an evdns_base that uses the server as its resolver. */
dns = evdns_base_new(data->base, 0);
tt_assert(!bufferevent_socket_connect_hostname(be4, NULL, AF_INET,
"localhost", listener_port));
/* Use the blocking resolver with a nonexistent hostname. */
- tt_assert(bufferevent_socket_connect_hostname(be5, NULL, AF_INET,
- "nonesuch.nowhere.example.com", 80) < 0);
+ tt_assert(!bufferevent_socket_connect_hostname(be5, NULL, AF_INET,
+ "nonesuch.nowhere.example.com", 80));
event_base_dispatch(data->base);
bufferevent_free(be5);
}
+
+struct gai_outcome {
+ int err;
+ struct evutil_addrinfo *ai;
+};
+
+static int n_gai_results_pending = 0;
+static struct event_base *exit_base_on_no_pending_results = NULL;
+
+static void
+gai_cb(int err, struct evutil_addrinfo *res, void *ptr)
+{
+ struct gai_outcome *go = ptr;
+ go->err = err;
+ go->ai = res;
+ if (--n_gai_results_pending <= 0 && exit_base_on_no_pending_results)
+ event_base_loopexit(exit_base_on_no_pending_results, NULL);
+ if (n_gai_results_pending < 900)
+ TT_BLATHER(("Got an answer; expecting %d more.",
+ n_gai_results_pending));
+}
+
+static void
+test_getaddrinfo_async(void *arg)
+{
+ struct basic_test_data *data = arg;
+ struct evutil_addrinfo hints, *a;
+ struct gai_outcome local_outcome;
+ struct gai_outcome a_out[10];
+ int i;
+ struct evdns_getaddrinfo_request *r;
+ char buf[128];
+ struct evdns_server_port *port = NULL;
+ ev_uint16_t dns_port = 0;
+ int n_dns_questions = 0;
+
+ struct evdns_base *dns_base = evdns_base_new(data->base, 0);
+
+ memset(a_out, 0, sizeof(a_out));
+
+ n_gai_results_pending = 10000; /* don't think about exiting yet. */
+
+ /* 1. Try some cases that will never hit the asynchronous resolver. */
+ /* 1a. Simple case with a symbolic service name */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ r = evdns_getaddrinfo(dns_base, "1.2.3.4", "http",
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,0);
+ tt_ptr_op(local_outcome.ai,!=,NULL);
+ test_ai_eq(local_outcome.ai, "1.2.3.4:80", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(local_outcome.ai);
+ local_outcome.ai = NULL;
+
+ /* 1b. EVUTIL_AI_NUMERICHOST is set */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_NUMERICHOST;
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ r = evdns_getaddrinfo(dns_base, "www.google.com", "80",
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,EVUTIL_EAI_NONAME);
+ tt_ptr_op(local_outcome.ai,==,NULL);
+
+ /* 1c. We give a numeric address (ipv6) */
+ memset(&hints, 0, sizeof(hints));
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_protocol = IPPROTO_TCP;
+ r = evdns_getaddrinfo(dns_base, "f::f", "8008",
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,0);
+ tt_assert(local_outcome.ai);
+ tt_ptr_op(local_outcome.ai->ai_next,==,NULL);
+ test_ai_eq(local_outcome.ai, "[f::f]:8008", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(local_outcome.ai);
+ local_outcome.ai = NULL;
+
+ /* 1d. We give a numeric address (ipv4) */
+ memset(&hints, 0, sizeof(hints));
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ hints.ai_family = PF_UNSPEC;
+ r = evdns_getaddrinfo(dns_base, "5.6.7.8", NULL,
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,0);
+ tt_assert(local_outcome.ai);
+ a = ai_find_by_protocol(local_outcome.ai, IPPROTO_TCP);
+ tt_assert(a);
+ test_ai_eq(a, "5.6.7.8", SOCK_STREAM, IPPROTO_TCP);
+ a = ai_find_by_protocol(local_outcome.ai, IPPROTO_UDP);
+ tt_assert(a);
+ test_ai_eq(a, "5.6.7.8", SOCK_DGRAM, IPPROTO_UDP);
+ evutil_freeaddrinfo(local_outcome.ai);
+ local_outcome.ai = NULL;
+
+ /* 1e. nodename is NULL (bind) */
+ memset(&hints, 0, sizeof(hints));
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = EVUTIL_AI_PASSIVE;
+ r = evdns_getaddrinfo(dns_base, NULL, "9090",
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,0);
+ tt_assert(local_outcome.ai);
+ /* we should get a v4 address of 0.0.0.0... */
+ a = ai_find_by_family(local_outcome.ai, PF_INET);
+ tt_assert(a);
+ test_ai_eq(a, "0.0.0.0:9090", SOCK_DGRAM, IPPROTO_UDP);
+ /* ... and a v6 address of ::0 */
+ a = ai_find_by_family(local_outcome.ai, PF_INET6);
+ tt_assert(a);
+ test_ai_eq(a, "[::]:9090", SOCK_DGRAM, IPPROTO_UDP);
+ evutil_freeaddrinfo(local_outcome.ai);
+ local_outcome.ai = NULL;
+
+ /* 1f. nodename is NULL (connect) */
+ memset(&hints, 0, sizeof(hints));
+ memset(&local_outcome, 0, sizeof(local_outcome));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ r = evdns_getaddrinfo(dns_base, NULL, "2",
+ &hints, gai_cb, &local_outcome);
+ tt_int_op(r,==,0);
+ tt_int_op(local_outcome.err,==,0);
+ tt_assert(local_outcome.ai);
+ /* we should get a v4 address of 127.0.0.1 .... */
+ a = ai_find_by_family(local_outcome.ai, PF_INET);
+ tt_assert(a);
+ test_ai_eq(a, "127.0.0.1:2", SOCK_STREAM, IPPROTO_TCP);
+ /* ... and a v6 address of ::1 */
+ a = ai_find_by_family(local_outcome.ai, PF_INET6);
+ tt_assert(a);
+ test_ai_eq(a, "[::1]:2", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(local_outcome.ai);
+ local_outcome.ai = NULL;
+
+ /* 2. Okay, now we can actually test the asynchronous resolver. */
+ /* Start a dummy local dns server... */
+ port = get_generic_server(data->base, &dns_port,
+ be_getaddrinfo_server_cb, &n_dns_questions);
+ tt_assert(port);
+ tt_int_op(dns_port, >=, 0);
+ /* ... and tell the evdns_base about it. */
+ evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", dns_port);
+ evdns_base_nameserver_ip_add(dns_base, buf);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ /* 0: Request for both.example.com should return both addresses. */
+ r = evdns_getaddrinfo(dns_base, "both.example.com", "8000",
+ &hints, gai_cb, &a_out[0]);
+ tt_assert(r);
+
+ /* 1: Request for v4only.example.com should return one address. */
+ r = evdns_getaddrinfo(dns_base, "v4only.example.com", "8001",
+ &hints, gai_cb, &a_out[1]);
+ tt_assert(r);
+
+ /* 2: Request for v6only.example.com should return one address. */
+ hints.ai_flags = 0;
+ r = evdns_getaddrinfo(dns_base, "v6only.example.com", "8002",
+ &hints, gai_cb, &a_out[2]);
+ tt_assert(r);
+
+ /* 3: PF_INET request for v4assert.example.com should not generate a
+ * v6 request. The server will fail the test if it does. */
+ hints.ai_family = PF_INET;
+ r = evdns_getaddrinfo(dns_base, "v4assert.example.com", "8003",
+ &hints, gai_cb, &a_out[3]);
+ tt_assert(r);
+
+ /* 4: PF_INET6 request for v6assert.example.com should not generate a
+ * v4 request. The server will fail the test if it does. */
+ hints.ai_family = PF_INET6;
+ r = evdns_getaddrinfo(dns_base, "v6assert.example.com", "8004",
+ &hints, gai_cb, &a_out[4]);
+ tt_assert(r);
+
+ /* 5: PF_INET request for nosuchplace.example.com should give NEXIST. */
+ hints.ai_family = PF_INET;
+ r = evdns_getaddrinfo(dns_base, "nosuchplace.example.com", "8005",
+ &hints, gai_cb, &a_out[5]);
+ tt_assert(r);
+
+ /* 6: PF_UNSPEC request for nosuchplace.example.com should give NEXIST.
+ */
+ hints.ai_family = PF_UNSPEC;
+ r = evdns_getaddrinfo(dns_base, "nosuchplace.example.com", "8006",
+ &hints, gai_cb, &a_out[6]);
+ tt_assert(r);
+
+ /* 7: PF_UNSPEC request for v6timeout.example.com should give an ipv4
+ * address only. */
+ hints.ai_family = PF_UNSPEC;
+ r = evdns_getaddrinfo(dns_base, "v6timeout.example.com", "8007",
+ &hints, gai_cb, &a_out[7]);
+ tt_assert(r);
+
+ /* 8: PF_UNSPEC request for v6timeout-nonexist.example.com should give
+ * a NEXIST */
+ hints.ai_family = PF_UNSPEC;
+ r = evdns_getaddrinfo(dns_base, "v6timeout-nonexist.example.com",
+ "8008", &hints, gai_cb, &a_out[8]);
+ tt_assert(r);
+
+ /* 9: AI_ADDRCONFIG should at least not crash. Can't test it more
+ * without knowing what kind of internet we have. */
+ hints.ai_flags |= EVUTIL_AI_ADDRCONFIG;
+ r = evdns_getaddrinfo(dns_base, "both.example.com",
+ "8009", &hints, gai_cb, &a_out[9]);
+ tt_assert(r);
+
+ /* XXXXX There are more tests we could do, including:
+
+ - A test to elicit NODATA.
+ - A test of cancelling a request.
+
+ */
+
+ n_gai_results_pending = 10;
+ exit_base_on_no_pending_results = data->base;
+
+ event_base_dispatch(data->base);
+
+ /* 0: both.example.com */
+ tt_int_op(a_out[0].err, ==, 0);
+ tt_assert(a_out[0].ai);
+ tt_assert(a_out[0].ai->ai_next);
+ tt_assert(!a_out[0].ai->ai_next->ai_next);
+ a = ai_find_by_family(a_out[0].ai, PF_INET);
+ tt_assert(a);
+ test_ai_eq(a, "80.80.32.32:8000", SOCK_STREAM, IPPROTO_TCP);
+ a = ai_find_by_family(a_out[0].ai, PF_INET6);
+ tt_assert(a);
+ test_ai_eq(a, "[80ff::bbbb]:8000", SOCK_STREAM, IPPROTO_TCP);
+ tt_assert(a_out[0].ai->ai_canonname);
+ tt_str_op(a_out[0].ai->ai_canonname, ==, "both-canonical.example.com");
+
+ /* 1: v4only.example.com */
+ tt_int_op(a_out[1].err, ==, 0);
+ tt_assert(a_out[1].ai);
+ tt_assert(! a_out[1].ai->ai_next);
+ test_ai_eq(a_out[1].ai, "18.52.86.120:8001", SOCK_STREAM, IPPROTO_TCP);
+ tt_assert(a_out[1].ai->ai_canonname == NULL);
+
+
+ /* 2: v6only.example.com */
+ tt_int_op(a_out[2].err, ==, 0);
+ tt_assert(a_out[2].ai);
+ tt_assert(! a_out[2].ai->ai_next);
+ test_ai_eq(a_out[2].ai, "[b0b::f00d]:8002", SOCK_STREAM, IPPROTO_TCP);
+
+ /* 3: v4assert.example.com */
+ tt_int_op(a_out[3].err, ==, 0);
+ tt_assert(a_out[3].ai);
+ tt_assert(! a_out[3].ai->ai_next);
+ test_ai_eq(a_out[3].ai, "18.52.86.120:8003", SOCK_STREAM, IPPROTO_TCP);
+
+ /* 4: v6assert.example.com */
+ tt_int_op(a_out[4].err, ==, 0);
+ tt_assert(a_out[4].ai);
+ tt_assert(! a_out[4].ai->ai_next);
+ test_ai_eq(a_out[4].ai, "[b0b::f00d]:8004", SOCK_STREAM, IPPROTO_TCP);
+
+ /* 5: nosuchplace.example.com (inet) */
+ tt_int_op(a_out[5].err, ==, EVUTIL_EAI_NONAME);
+ tt_assert(! a_out[5].ai);
+
+ /* 6: nosuchplace.example.com (unspec) */
+ tt_int_op(a_out[6].err, ==, EVUTIL_EAI_NONAME);
+ tt_assert(! a_out[6].ai);
+
+ /* 7: v6timeout.example.com */
+ tt_int_op(a_out[7].err, ==, 0);
+ tt_assert(a_out[7].ai);
+ tt_assert(! a_out[7].ai->ai_next);
+ test_ai_eq(a_out[7].ai, "171.205.239.1:8007", SOCK_STREAM, IPPROTO_TCP);
+
+ /* 8: v6timeout-nonexist.example.com */
+ tt_int_op(a_out[8].err, ==, EVUTIL_EAI_NONAME);
+ tt_assert(! a_out[8].ai);
+
+ /* 9: both (ADDRCONFIG) */
+ tt_int_op(a_out[9].err, ==, 0);
+ tt_assert(a_out[9].ai);
+ a = ai_find_by_family(a_out[9].ai, PF_INET);
+ if (a)
+ test_ai_eq(a, "80.80.32.32:8009", SOCK_STREAM, IPPROTO_TCP);
+ else
+ tt_assert(ai_find_by_family(a_out[9].ai, PF_INET6));
+ a = ai_find_by_family(a_out[9].ai, PF_INET6);
+ if (a)
+ test_ai_eq(a, "[80ff::bbbb]:8009", SOCK_STREAM, IPPROTO_TCP);
+ else
+ tt_assert(ai_find_by_family(a_out[9].ai, PF_INET));
+
+end:
+ if (local_outcome.ai)
+ evutil_freeaddrinfo(local_outcome.ai);
+ for (i=0;i<10;++i) {
+ if (a_out[i].ai)
+ evutil_freeaddrinfo(a_out[i].ai);
+ }
+ if (port)
+ evdns_close_server_port(port);
+ if (dns_base)
+ evdns_base_free(dns_base, 0);
+}
+
+
#define DNS_LEGACY(name, flags) \
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
dns_##name }
{ "bufferevent_connnect_hostname", test_bufferevent_connect_hostname,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
+ { "getaddrinfo_async", test_getaddrinfo_async,
+ TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" },
+
END_OF_TESTCASES
};
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
"/?arg=val") == -1) {
tt_abort_msg("Couldn't make request");
-
}
/* start up a web server one second after the connection tried
}
-static void
-test_evutil_resolve(void *arg)
+struct evutil_addrinfo *
+ai_find_by_family(struct evutil_addrinfo *ai, int family)
+{
+ while (ai) {
+ if (ai->ai_family == family)
+ return ai;
+ ai = ai->ai_next;
+ }
+ return NULL;
+}
+
+struct evutil_addrinfo *
+ai_find_by_protocol(struct evutil_addrinfo *ai, int protocol)
+{
+ while (ai) {
+ if (ai->ai_protocol == protocol)
+ return ai;
+ ai = ai->ai_next;
+ }
+ return NULL;
+}
+
+
+int
+_test_ai_eq(const struct evutil_addrinfo *ai, const char *sockaddr_port,
+ int socktype, int protocol, int line)
{
struct sockaddr_storage ss;
+ int slen = sizeof(ss);
+ int gotport;
+ char buf[128];
+ memset(&ss, 0, sizeof(ss));
+ if (socktype > 0)
+ tt_int_op(ai->ai_socktype, ==, socktype);
+ if (protocol > 0)
+ tt_int_op(ai->ai_protocol, ==, protocol);
+
+ if (evutil_parse_sockaddr_port(
+ sockaddr_port, (struct sockaddr*)&ss, &slen)<0) {
+ TT_FAIL(("Couldn't parse expected address %s on line %d",
+ sockaddr_port, line));
+ return -1;
+ }
+ if (ai->ai_family != ss.ss_family) {
+ TT_FAIL(("Address family %d did not match %d on line %d",
+ ai->ai_family, ss.ss_family, line));
+ return -1;
+ }
+ if (ai->ai_addr->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in*)ai->ai_addr;
+ evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
+ gotport = ntohs(sin->sin_port);
+ if (ai->ai_addrlen != sizeof(struct sockaddr_in)) {
+ TT_FAIL(("Addr size mismatch on line %d", line));
+ return -1;
+ }
+ } else {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr;
+ evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
+ gotport = ntohs(sin6->sin6_port);
+ if (ai->ai_addrlen != sizeof(struct sockaddr_in6)) {
+ TT_FAIL(("Addr size mismatch on line %d", line));
+ return -1;
+ }
+ }
+ if (evutil_sockaddr_cmp(ai->ai_addr, (struct sockaddr*)&ss, 1)) {
+ TT_FAIL(("Wanted %s, got %s:%d on line %d", sockaddr_port,
+ buf, gotport, line));
+ return -1;
+ } else {
+ TT_BLATHER(("Wanted %s, got %s:%d on line %d", sockaddr_port,
+ buf, gotport, line));
+ }
+ return 0;
+end:
+ TT_FAIL(("Test failed on line %d", line));
+ return -1;
+}
+
+static void
+test_evutil_getaddrinfo(void *arg)
+{
+ struct evutil_addrinfo *ai = NULL, *a;
+ struct evutil_addrinfo hints;
+
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
- ev_socklen_t socklen = sizeof(ss);
char buf[128];
const char *cp;
int r;
- memset(&ss, 0xff, sizeof(ss)); /* Make sure it starts out confused.*/
- r = evutil_resolve(AF_INET, "www.google.com", (struct sockaddr*)&ss,
- &socklen, 80);
- if (r<0) {
- TT_BLATHER(("Couldn't resolve www.google.com"));
- tt_skip();
+ /* Try using it as a pton. */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ r = evutil_getaddrinfo("1.2.3.4", "8080", &hints, &ai);
+ tt_int_op(r, ==, 0);
+ tt_assert(ai);
+ tt_ptr_op(ai->ai_next, ==, NULL); /* no ambiguity */
+ test_ai_eq(ai, "1.2.3.4:8080", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_protocol = IPPROTO_UDP;
+ r = evutil_getaddrinfo("1001:b0b::f00f", "4321", &hints, &ai);
+ tt_int_op(r, ==, 0);
+ tt_assert(ai);
+ tt_ptr_op(ai->ai_next, ==, NULL); /* no ambiguity */
+ test_ai_eq(ai, "[1001:b0b::f00f]:4321", SOCK_DGRAM, IPPROTO_UDP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ /* Try out the behavior of nodename=NULL */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = EVUTIL_AI_PASSIVE; /* as if for bind */
+ r = evutil_getaddrinfo(NULL, "9999", &hints, &ai);
+ tt_int_op(r,==,0);
+ tt_assert(ai);
+ tt_ptr_op(ai->ai_next, ==, NULL);
+ test_ai_eq(ai, "0.0.0.0:9999", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+ hints.ai_flags = 0; /* as if for connect */
+ r = evutil_getaddrinfo(NULL, "9998", &hints, &ai);
+ tt_assert(ai);
+ tt_int_op(r,==,0);
+ test_ai_eq(ai, "127.0.0.1:9998", SOCK_STREAM, IPPROTO_TCP);
+ tt_ptr_op(ai->ai_next, ==, NULL);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ hints.ai_flags = 0; /* as if for connect */
+ hints.ai_family = PF_INET6;
+ r = evutil_getaddrinfo(NULL, "9997", &hints, &ai);
+ tt_assert(ai);
+ tt_int_op(r,==,0);
+ tt_ptr_op(ai->ai_next, ==, NULL);
+ test_ai_eq(ai, "[::1]:9997", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ hints.ai_flags = EVUTIL_AI_PASSIVE; /* as if for bind. */
+ hints.ai_family = PF_INET6;
+ r = evutil_getaddrinfo(NULL, "9996", &hints, &ai);
+ tt_assert(ai);
+ tt_int_op(r,==,0);
+ tt_ptr_op(ai->ai_next, ==, NULL);
+ test_ai_eq(ai, "[::]:9996", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ /* Now try an unspec one. We should get a v6 and a v4. */
+ hints.ai_family = PF_UNSPEC;
+ r = evutil_getaddrinfo(NULL, "9996", &hints, &ai);
+ tt_assert(ai);
+ tt_int_op(r,==,0);
+ a = ai_find_by_family(ai, PF_INET6);
+ tt_assert(a);
+ test_ai_eq(a, "[::]:9996", SOCK_STREAM, IPPROTO_TCP);
+ a = ai_find_by_family(ai, PF_INET);
+ tt_assert(a);
+ test_ai_eq(a, "0.0.0.0:9996", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ /* Try out AI_NUMERICHOST: successful case. Also try
+ * multiprotocol. */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_NUMERICHOST;
+ r = evutil_getaddrinfo("1.2.3.4", NULL, &hints, &ai);
+ tt_int_op(r, ==, 0);
+ a = ai_find_by_protocol(ai, IPPROTO_TCP);
+ tt_assert(a);
+ test_ai_eq(a, "1.2.3.4", SOCK_STREAM, IPPROTO_TCP);
+ a = ai_find_by_protocol(ai, IPPROTO_UDP);
+ tt_assert(a);
+ test_ai_eq(a, "1.2.3.4", SOCK_DGRAM, IPPROTO_UDP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
+
+ /* Try the failing case of AI_NUMERICHOST */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_NUMERICHOST;
+ r = evutil_getaddrinfo("www.google.com", "80", &hints, &ai);
+ tt_int_op(r, ==, EVUTIL_EAI_NONAME);
+ tt_int_op(ai, ==, NULL);
+
+ /* Try symbolic service names wit AI_NUMERICSERV */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = EVUTIL_AI_NUMERICSERV;
+ r = evutil_getaddrinfo("1.2.3.4", "http", &hints, &ai);
+ tt_int_op(r,==,EVUTIL_EAI_NONAME);
+
+ /* Try symbolic service names */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ r = evutil_getaddrinfo("1.2.3.4", "http", &hints, &ai);
+ if (r!=0) {
+ TT_GRIPE(("Symbolic service names seem broken."));
+ } else {
+ tt_assert(ai);
+ test_ai_eq(ai, "1.2.3.4:80", SOCK_STREAM, IPPROTO_TCP);
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
}
- tt_int_op(ss.ss_family, ==, AF_INET);
- tt_int_op(socklen, ==, sizeof(struct sockaddr_in));
- sin = (struct sockaddr_in*)&ss;
- tt_int_op(sin->sin_port, ==, htons(80));
- tt_int_op(sin->sin_addr.s_addr, !=, 0xffffffff);
-
- cp = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
- TT_BLATHER(("www.google.com resolved to %s",cp?cp:"<unwriteable>"));
-
- memset(&ss, 0xff, sizeof(ss)); /* Make sure it starts out confused.*/
- socklen = sizeof(ss);
- r = evutil_resolve(AF_INET6, "ipv6.google.com", (struct sockaddr*)&ss,
- &socklen, 80);
- if (r<0) {
- TT_BLATHER(("Couldn't do an ipv6 lookup for ipv6.google.com"));
- goto end;
+
+ /* Now do some actual lookups. */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ r = evutil_getaddrinfo("www.google.com", "80", &hints, &ai);
+ if (r != 0) {
+ TT_GRIPE(("Couldn't resolve www.google.com"));
+ } else {
+ tt_assert(ai);
+ tt_int_op(ai->ai_family, ==, PF_INET);
+ tt_int_op(ai->ai_protocol, ==, IPPROTO_TCP);
+ tt_int_op(ai->ai_socktype, ==, SOCK_STREAM);
+ tt_int_op(ai->ai_addrlen, ==, sizeof(struct sockaddr_in));
+ sin = (struct sockaddr_in*)ai->ai_addr;
+ tt_int_op(sin->sin_family, ==, AF_INET);
+ tt_int_op(sin->sin_port, ==, htons(80));
+ tt_int_op(sin->sin_addr.s_addr, !=, 0xffffffff);
+
+ cp = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
+ TT_BLATHER(("www.google.com resolved to %s",
+ cp?cp:"<unwriteable>"));
+ evutil_freeaddrinfo(ai);
+ ai = NULL;
}
- tt_int_op(ss.ss_family, ==, AF_INET6);
- tt_int_op(socklen, ==, sizeof(struct sockaddr_in6));
- sin6 = (struct sockaddr_in6*)&ss;
- tt_int_op(sin6->sin6_port, ==, htons(80));
- cp = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
- TT_BLATHER(("ipv6.google.com resolved to %s",cp?cp:"<unwriteable>"));
+ hints.ai_family = PF_INET6;
+ r = evutil_getaddrinfo("ipv6.google.com", "80", &hints, &ai);
+ if (r != 0) {
+ TT_BLATHER(("Couldn't do an ipv6 lookup for ipv6.google.com"));
+ } else {
+ tt_assert(ai);
+ tt_int_op(ai->ai_family, ==, PF_INET6);
+ tt_int_op(ai->ai_addrlen, ==, sizeof(struct sockaddr_in6));
+ sin6 = (struct sockaddr_in6*)ai->ai_addr;
+ tt_int_op(sin6->sin6_port, ==, htons(80));
+
+ cp = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf,
+ sizeof(buf));
+ TT_BLATHER(("ipv6.google.com resolved to %s",
+ cp?cp:"<unwriteable>"));
+ }
end:
- ;
+ if (ai)
+ evutil_freeaddrinfo(ai);
}
struct testcase_t util_testcases[] = {
{ "strlcpy", test_evutil_strlcpy, 0, NULL, NULL },
{ "log", test_evutil_log, TT_FORK, NULL, NULL },
{ "upcast", test_evutil_upcast, 0, NULL, NULL },
- { "resolve", test_evutil_resolve, TT_FORK, NULL, NULL },
+ { "getaddrinfo", test_evutil_getaddrinfo, TT_FORK, NULL, NULL },
END_OF_TESTCASES,
};
#define EV_SIZE_MAX ((size_t)-1)
#endif
+/* Internal addrinfo error code. This one is returned from only from
+ * evutil_getaddrinfo_common, when we are sure that we'll have to hit a DNS
+ * server. */
+#define EVUTIL_EAI_NEED_RESOLVE -90002
+
+struct evdns_base;
+struct evdns_getaddrinfo_request;
+typedef struct evdns_getaddrinfo_request* (*evdns_getaddrinfo_fn)(
+ struct evdns_base *base,
+ const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in,
+ void (*cb)(int, struct evutil_addrinfo *, void *), void *arg);
+
+void evutil_set_evdns_getaddrinfo_fn(evdns_getaddrinfo_fn fn);
+
+struct evutil_addrinfo *evutil_new_addrinfo(struct sockaddr *sa,
+ ev_socklen_t socklen, const struct evutil_addrinfo *hints);
+struct evutil_addrinfo *evutil_addrinfo_append(struct evutil_addrinfo *first,
+ struct evutil_addrinfo *append);
+void evutil_adjust_hints_for_addrconfig(struct evutil_addrinfo *hints);
+int evutil_getaddrinfo_common(const char *nodename, const char *servname,
+ struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum);
+
+int
+evutil_getaddrinfo_async(struct evdns_base *dns_base,
+ const char *nodename, const char *servname,
+ const struct evutil_addrinfo *hints_in,
+ void (*cb)(int, struct evutil_addrinfo *, void *), void *arg);
+
#ifdef __cplusplus
}
#endif