mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
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:
parent
72bafc175a
commit
86f5742015
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
72
configure.in
72
configure.in
@ -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
489
evdns.c
@ -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
726
evutil.c
@ -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
139
http.c
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user