Add two implementations of getaddrinfo: one blocking and one nonblocking.

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
This commit is contained in:
Nick Mathewson 2009-11-16 22:25:46 +00:00
parent 72bafc175a
commit 86f5742015
20 changed files with 2208 additions and 480 deletions

View File

@ -47,6 +47,8 @@ Changes in 2.0.3-alpha:
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:

View File

@ -111,7 +111,7 @@ CORE_SRC = event.c buffer.c \
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)

View File

@ -17,7 +17,7 @@ CORE_OBJS=event.obj buffer.obj bufferevent.obj bufferevent_sock.obj \
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

View File

@ -56,10 +56,16 @@
#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 */
@ -166,6 +172,8 @@
/* 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

View File

@ -238,17 +238,6 @@ void _bufferevent_generic_adj_timeouts(struct bufferevent *bev);
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

View File

@ -1,174 +0,0 @@
/*
* 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;
}

View File

@ -380,71 +380,57 @@ done:
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);
}
/*

View File

@ -170,13 +170,75 @@ die horribly
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)
@ -368,8 +430,9 @@ AC_CHECK_SIZEOF(int)
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
@ -379,6 +442,9 @@ AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , ,
#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

489
evdns.c
View File

@ -166,8 +166,11 @@ typedef unsigned int uint;
#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
@ -178,10 +181,10 @@ typedef unsigned int uint;
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 */
@ -198,23 +201,26 @@ struct evdns_request {
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];
@ -332,7 +338,7 @@ struct evdns_base {
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 */
@ -345,6 +351,13 @@ struct evdns_base {
/** 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
@ -355,6 +368,12 @@ struct evdns_base {
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) \
@ -1019,7 +1038,7 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
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. */
@ -1029,7 +1048,7 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
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;
@ -1040,6 +1059,15 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
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) {
@ -1048,7 +1076,7 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
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. */
@ -1058,7 +1086,7 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
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;
@ -3206,6 +3234,14 @@ evdns_base_set_option_impl(struct evdns_base *base,
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;
@ -3605,11 +3641,10 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
{
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)
@ -3637,6 +3672,8 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
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;
@ -3757,3 +3794,419 @@ evdns_shutdown(int fail_requests)
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);
}

726
evutil.c
View File

@ -26,6 +26,9 @@
#include "event-config.h"
#define _REENTRANT
#define _GNU_SOURCE
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@ -59,9 +62,6 @@
#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>
@ -71,6 +71,7 @@
#include "event2/util.h"
#include "util-internal.h"
#include "log-internal.h"
#include "mm-internal.h"
#include "strlcpy-internal.h"
#include "ipv6-internal.h"
@ -322,92 +323,683 @@ evutil_socket_finished_connecting(evutil_socket_t fd)
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.
/* 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)
{
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;
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 (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;
}
/* 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 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);
}
}
#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;
}
#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_resolve(int family, const char *hostname, struct sockaddr *sa,
ev_socklen_t *socklen, int port)
evutil_getaddrinfo_common(const char *nodename, const char *servname,
struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum)
{
#ifdef _EVENT_HAVE_GETADDRINFO_XXX
struct addrinfo hint, *hintp=NULL;
struct addrinfo *ai=NULL;
int r;
memset(&hint, 0, sizeof(hint));
int port = 0;
const char *pname;
if (family != AF_UNSPEC) {
hint.ai_family = family;
hintp = &hint;
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;
}
}
r = getaddrinfo(hostname, NULL, hintp, &ai);
if (!ai)
return -1;
if (r || ai->ai_addrlen > *socklen) {
/* log/report error? */
freeaddrinfo(ai);
return -1;
/* 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;
}
/* 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);
/* 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;
}
}
*socklen = ai->ai_addrlen;
freeaddrinfo(ai);
return 0;
#else
/* XXXX use gethostbyname_r/gethostbyname2_r where available */
struct hostent *he;
struct sockaddr *sa_ptr;
/* 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[] = {

139
http.c
View File

@ -48,6 +48,8 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#else
#include <ws2tcpip.h>
#endif
#include <sys/queue.h>
@ -100,8 +102,13 @@
#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,
@ -142,50 +149,6 @@ 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
@ -193,7 +156,7 @@ fake_freeaddrinfo(struct addrinfo *ai)
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(
@ -2979,32 +2942,6 @@ evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
* 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,
@ -3020,9 +2957,12 @@ 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;
}
@ -3041,7 +2981,7 @@ name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
/* 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;
@ -3084,49 +3024,40 @@ bind_socket_ai(struct addrinfo *ai, int reuse)
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)
@ -3139,11 +3070,7 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
fd = bind_socket_ai(aitop, reuse);
#ifdef _EVENT_HAVE_GETADDRINFO
freeaddrinfo(aitop);
#else
fake_freeaddrinfo(aitop);
#endif
evutil_freeaddrinfo(aitop);
return (fd);
}
@ -3151,7 +3078,7 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
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) {
@ -3170,11 +3097,7 @@ socket_connect(evutil_socket_t fd, const char *address, unsigned short port)
res = 0;
out:
#ifdef _EVENT_HAVE_GETADDRINFO
freeaddrinfo(ai);
#else
fake_freeaddrinfo(ai);
#endif
evutil_freeaddrinfo(ai);
return (res);
}

View File

@ -597,6 +597,36 @@ struct sockaddr;
*/
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

View File

@ -67,6 +67,16 @@ extern "C" {
*/
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.

View File

@ -56,6 +56,10 @@ extern "C" {
#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
@ -324,6 +328,138 @@ int evutil_ascii_strcasecmp(const char *str1, const char *str2);
*/
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

View File

@ -57,6 +57,37 @@ main_callback(int result, char type, int count, int ttl,
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)
{
@ -101,7 +132,7 @@ logfn(int is_warn, const char *msg) {
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) {
@ -115,6 +146,8 @@ main(int c, char **v) {
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
@ -148,15 +181,26 @@ main(int c, char **v) {
"/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]);

View File

@ -97,6 +97,17 @@ void run_legacy_test_fn(void *ptr);
{ #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
}

View File

@ -71,6 +71,8 @@ static int dns_ok = 0;
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)
@ -480,7 +482,7 @@ end:
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)
{
@ -497,12 +499,14 @@ get_generic_server(struct event_base *base,
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:
@ -571,10 +575,11 @@ dns_search_test(void *arg)
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);
@ -655,10 +660,11 @@ dns_retry_test(void *arg)
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);
@ -743,11 +749,12 @@ dns_reissue_test(void *arg)
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);
@ -802,11 +809,12 @@ dns_inflight_test(void *arg)
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);
@ -845,9 +853,10 @@ end:
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;
@ -859,6 +868,8 @@ be_connect_hostname_server_cb(struct evdns_server_request *req, void *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 &&
@ -870,6 +881,72 @@ be_connect_hostname_server_cb(struct evdns_server_request *req, void *data)
} 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));
}
@ -918,6 +995,7 @@ be_connect_hostname_event_cb(struct bufferevent *bev, short what, void *ctx)
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);
@ -940,7 +1018,8 @@ test_bufferevent_connect_hostname(void *arg)
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];
@ -957,6 +1036,7 @@ test_bufferevent_connect_hostname(void *arg)
-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));
@ -971,7 +1051,12 @@ test_bufferevent_connect_hostname(void *arg)
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);
@ -1012,8 +1097,8 @@ test_bufferevent_connect_hostname(void *arg)
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);
@ -1047,6 +1132,326 @@ end:
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 }
@ -1064,6 +1469,9 @@ struct testcase_t dns_testcases[] = {
{ "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
};

View File

@ -2042,7 +2042,6 @@ http_connection_retry_test(void)
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

View File

@ -489,51 +489,267 @@ end:
}
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>"));
/* 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);
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) {
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;
}
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"));
goto end;
}
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));
} 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>"));
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[] = {
@ -546,7 +762,7 @@ 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,
};

View File

@ -205,6 +205,35 @@ const char *evutil_getenv(const char *name);
#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