mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-18 08:49:57 -04: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 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 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 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:
|
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.c bufferevent_sock.c bufferevent_filter.c \
|
||||||
bufferevent_pair.c listener.c \
|
bufferevent_pair.c listener.c \
|
||||||
evmap.c log.c evutil.c strlcpy.c $(SYS_SRC)
|
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)
|
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
|
strlcpy.obj signal.obj bufferevent_filter.obj
|
||||||
WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj \
|
WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj \
|
||||||
event_iocp.obj bufferevent_async.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)
|
ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
|
||||||
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib
|
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib
|
||||||
|
@ -56,10 +56,16 @@
|
|||||||
#define _EVENT_HAVE_FCNTL_H 1
|
#define _EVENT_HAVE_FCNTL_H 1
|
||||||
|
|
||||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
/* 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. */
|
/* 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 to 1 if you have the `gettimeofday' function. */
|
||||||
/* #define _EVENT_HAVE_GETTIMEOFDAY 1 */
|
/* #define _EVENT_HAVE_GETTIMEOFDAY 1 */
|
||||||
@ -166,6 +172,8 @@
|
|||||||
/* Define to 1 if you have the `strtoll' function. */
|
/* Define to 1 if you have the `strtoll' function. */
|
||||||
/* #define _EVENT_HAVE_STRTOLL 1 */
|
/* #define _EVENT_HAVE_STRTOLL 1 */
|
||||||
|
|
||||||
|
#define _EVENT_HAVE_STRUCT_ADDRINFO 1
|
||||||
|
|
||||||
/* Define to 1 if the system has the type `struct in6_addr'. */
|
/* Define to 1 if the system has the type `struct in6_addr'. */
|
||||||
#define _EVENT_HAVE_STRUCT_IN6_ADDR 1
|
#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); \
|
EVLOCK_UNLOCK(locking->lock, EVTHREAD_WRITE); \
|
||||||
} while(0)
|
} 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int (*_bufferevent_socket_connect_hostname_evdns_fn)(
|
static void
|
||||||
struct bufferevent *, struct evdns_base *, int,
|
bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,
|
||||||
const char *, int) = NULL;
|
void *arg)
|
||||||
|
|
||||||
void _bufferevent_set_socket_connect_hostname_evdns_fn(
|
|
||||||
int (*fn)(struct bufferevent *, struct evdns_base *, int,
|
|
||||||
const char *, int))
|
|
||||||
{
|
{
|
||||||
if (!_bufferevent_socket_connect_hostname_evdns_fn)
|
struct bufferevent *bev = arg;
|
||||||
_bufferevent_socket_connect_hostname_evdns_fn = fn;
|
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
|
int
|
||||||
bufferevent_socket_connect_hostname(struct bufferevent *bev,
|
bufferevent_socket_connect_hostname(struct bufferevent *bev,
|
||||||
struct evdns_base *evdns_base, int family, const char *hostname, int port)
|
struct evdns_base *evdns_base, int family, const char *hostname, int port)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage ss;
|
char portbuf[10];
|
||||||
ev_socklen_t socklen = sizeof(ss);
|
struct evutil_addrinfo hint;
|
||||||
int socklen_int = sizeof(ss);
|
int err;
|
||||||
|
|
||||||
if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
|
if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
|
||||||
return -1;
|
return -1;
|
||||||
if (port < 1 || port > 65535)
|
if (port < 1 || port > 65535)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(&ss, 0, sizeof(ss));
|
evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evdns_base) {
|
memset(&hint, 0, sizeof(hint));
|
||||||
EVUTIL_ASSERT(_bufferevent_socket_connect_hostname_evdns_fn);
|
hint.ai_family = family;
|
||||||
return _bufferevent_socket_connect_hostname_evdns_fn(
|
hint.ai_protocol = IPPROTO_TCP;
|
||||||
bev, evdns_base, family, hostname, port);
|
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,
|
if (err == 0)
|
||||||
&socklen, port)<0) {
|
return 0;
|
||||||
_bufferevent_incref_and_lock(bev);
|
else
|
||||||
_bufferevent_run_eventcb(bev, BEV_EVENT_ERROR);
|
|
||||||
_bufferevent_decref_and_unlock(bev);
|
|
||||||
return -1;
|
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)
|
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.
|
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||||
AC_C_CONST
|
AC_C_CONST
|
||||||
AC_C_INLINE
|
AC_C_INLINE
|
||||||
AC_HEADER_TIME
|
AC_HEADER_TIME
|
||||||
|
|
||||||
dnl Checks for library functions.
|
dnl Checks for library functions.
|
||||||
AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random issetugid geteuid getegid)
|
AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random issetugid geteuid getegid getservbyname getprotobynumber)
|
||||||
|
|
||||||
|
|
||||||
|
# Check for gethostbyname_r in all its glorious incompatible versions.
|
||||||
|
# (This is cut-and-pasted from Tor, which based its logic on
|
||||||
|
# Python's configure.in.)
|
||||||
|
AH_TEMPLATE(HAVE_GETHOSTBYNAME_R,
|
||||||
|
[Define this if you have any gethostbyname_r()])
|
||||||
|
|
||||||
|
AC_CHECK_FUNC(gethostbyname_r, [
|
||||||
|
AC_MSG_CHECKING([how many arguments gethostbyname_r() wants])
|
||||||
|
OLD_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$CFLAGS $MY_CPPFLAGS $MY_THREAD_CPPFLAGS $MY_CFLAGS"
|
||||||
|
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([
|
||||||
|
#include <netdb.h>
|
||||||
|
], [[
|
||||||
|
char *cp1, *cp2;
|
||||||
|
struct hostent *h1, *h2;
|
||||||
|
int i1, i2;
|
||||||
|
(void)gethostbyname_r(cp1,h1,cp2,i1,&h2,&i2);
|
||||||
|
]]),[
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R)
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R_6_ARG, 1,
|
||||||
|
[Define this if gethostbyname_r takes 6 arguments])
|
||||||
|
AC_MSG_RESULT(6)
|
||||||
|
], [
|
||||||
|
AC_TRY_COMPILE([
|
||||||
|
#include <netdb.h>
|
||||||
|
], [
|
||||||
|
char *cp1, *cp2;
|
||||||
|
struct hostent *h1;
|
||||||
|
int i1, i2;
|
||||||
|
(void)gethostbyname_r(cp1,h1,cp2,i1,&i2);
|
||||||
|
], [
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R)
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R_5_ARG, 1,
|
||||||
|
[Define this if gethostbyname_r takes 5 arguments])
|
||||||
|
AC_MSG_RESULT(5)
|
||||||
|
], [
|
||||||
|
AC_TRY_COMPILE([
|
||||||
|
#include <netdb.h>
|
||||||
|
], [
|
||||||
|
char *cp1;
|
||||||
|
struct hostent *h1;
|
||||||
|
struct hostent_data hd;
|
||||||
|
(void) gethostbyname_r(cp1,h1,&hd);
|
||||||
|
], [
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R)
|
||||||
|
AC_DEFINE(HAVE_GETHOSTBYNAME_R_3_ARG, 1,
|
||||||
|
[Define this if gethostbyname_r takes 3 arguments])
|
||||||
|
AC_MSG_RESULT(3)
|
||||||
|
], [
|
||||||
|
AC_MSG_RESULT(0)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
CFLAGS=$OLD_CFLAGS
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
AC_CHECK_SIZEOF(long)
|
AC_CHECK_SIZEOF(long)
|
||||||
|
|
||||||
@ -368,8 +430,9 @@ AC_CHECK_SIZEOF(int)
|
|||||||
AC_CHECK_SIZEOF(short)
|
AC_CHECK_SIZEOF(short)
|
||||||
AC_CHECK_SIZEOF(size_t)
|
AC_CHECK_SIZEOF(size_t)
|
||||||
|
|
||||||
AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , ,
|
AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t, struct addrinfo], , ,
|
||||||
[#include <sys/types.h>
|
[#define _GNU_SOURCE
|
||||||
|
#include <sys/types.h>
|
||||||
#ifdef HAVE_NETINET_IN_H
|
#ifdef HAVE_NETINET_IN_H
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#endif
|
#endif
|
||||||
@ -379,6 +442,9 @@ AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , ,
|
|||||||
#ifdef HAVE_SYS_SOCKET_H
|
#ifdef HAVE_SYS_SOCKET_H
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_NETDB_H
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define WIN32_WINNT 0x400
|
#define WIN32_WINNT 0x400
|
||||||
#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
|
#define close _close
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_ADDRS 32 /* maximum number of addresses from a single packet */
|
/* maximum number of addresses from a single packet */
|
||||||
/* which we bother recording */
|
/* that we bother recording */
|
||||||
|
#define MAX_V4_ADDRS 32
|
||||||
|
#define MAX_V6_ADDRS 32
|
||||||
|
|
||||||
|
|
||||||
#define TYPE_A EVDNS_TYPE_A
|
#define TYPE_A EVDNS_TYPE_A
|
||||||
#define TYPE_CNAME 5
|
#define TYPE_CNAME 5
|
||||||
@ -178,10 +181,10 @@ typedef unsigned int uint;
|
|||||||
|
|
||||||
struct evdns_request {
|
struct evdns_request {
|
||||||
u8 *request; /* the dns packet data */
|
u8 *request; /* the dns packet data */
|
||||||
|
u8 request_type; /* TYPE_PTR or TYPE_A or TYPE_AAAA */
|
||||||
unsigned int request_len;
|
unsigned int request_len;
|
||||||
int reissue_count;
|
int reissue_count;
|
||||||
int tx_count; /* the number of times that this packet has been sent */
|
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 */
|
void *user_pointer; /* the pointer given to us for this request */
|
||||||
evdns_callback_type user_callback;
|
evdns_callback_type user_callback;
|
||||||
struct nameserver *ns; /* the server which we last sent it */
|
struct nameserver *ns; /* the server which we last sent it */
|
||||||
@ -198,23 +201,26 @@ struct evdns_request {
|
|||||||
struct event timeout_event;
|
struct event timeout_event;
|
||||||
|
|
||||||
u16 trans_id; /* the transaction id */
|
u16 trans_id; /* the transaction id */
|
||||||
char request_appended; /* true if the request pointer is data which follows this struct */
|
unsigned request_appended :1; /* true if the request pointer is data which follows this struct */
|
||||||
char transmit_me; /* needs to be transmitted */
|
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 evdns_base *base;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct reply {
|
struct reply {
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned int have_answer;
|
unsigned int have_answer : 1;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
u32 addrcount;
|
u32 addrcount;
|
||||||
u32 addresses[MAX_ADDRS];
|
u32 addresses[MAX_V4_ADDRS];
|
||||||
} a;
|
} a;
|
||||||
struct {
|
struct {
|
||||||
u32 addrcount;
|
u32 addrcount;
|
||||||
struct in6_addr addresses[MAX_ADDRS];
|
struct in6_addr addresses[MAX_V6_ADDRS];
|
||||||
} aaaa;
|
} aaaa;
|
||||||
struct {
|
struct {
|
||||||
char name[HOST_NAME_MAX];
|
char name[HOST_NAME_MAX];
|
||||||
@ -332,7 +338,7 @@ struct evdns_base {
|
|||||||
|
|
||||||
int global_max_requests_inflight;
|
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_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 */
|
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 */
|
/* 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 for global_outgoing_address. 0 if it isn't set. */
|
||||||
ev_socklen_t global_outgoing_addrlen;
|
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;
|
struct search_state *global_search_state;
|
||||||
|
|
||||||
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
|
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
|
||||||
@ -355,6 +368,12 @@ struct evdns_base {
|
|||||||
|
|
||||||
static struct evdns_base *current_base = NULL;
|
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 */
|
/* Given a pointer to an evdns_server_request, get the corresponding */
|
||||||
/* server_request. */
|
/* server_request. */
|
||||||
#define TO_SERVER_REQUEST(base_ptr) \
|
#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. */
|
if ((datalength & 3) != 0) /* not an even number of As. */
|
||||||
goto err;
|
goto err;
|
||||||
addrcount = datalength >> 2;
|
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);
|
ttl_r = MIN(ttl_r, ttl);
|
||||||
/* we only bother with the first four addresses. */
|
/* 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;
|
j += 4*addrtocopy;
|
||||||
reply.data.a.addrcount += addrtocopy;
|
reply.data.a.addrcount += addrtocopy;
|
||||||
reply.have_answer = 1;
|
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) {
|
} else if (type == TYPE_PTR && class == CLASS_INET) {
|
||||||
if (req->request_type != TYPE_PTR) {
|
if (req->request_type != TYPE_PTR) {
|
||||||
j += datalength; continue;
|
j += datalength; continue;
|
||||||
@ -1040,6 +1059,15 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
|
|||||||
ttl_r = MIN(ttl_r, ttl);
|
ttl_r = MIN(ttl_r, ttl);
|
||||||
reply.have_answer = 1;
|
reply.have_answer = 1;
|
||||||
break;
|
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) {
|
} else if (type == TYPE_AAAA && class == CLASS_INET) {
|
||||||
int addrcount, addrtocopy;
|
int addrcount, addrtocopy;
|
||||||
if (req->request_type != TYPE_AAAA) {
|
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. */
|
if ((datalength & 15) != 0) /* not an even number of AAAAs. */
|
||||||
goto err;
|
goto err;
|
||||||
addrcount = datalength >> 4; /* each address is 16 bytes long */
|
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);
|
ttl_r = MIN(ttl_r, ttl);
|
||||||
|
|
||||||
/* we only bother with the first four addresses. */
|
/* 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;
|
reply.data.aaaa.addrcount += addrtocopy;
|
||||||
j += 16*addrtocopy;
|
j += 16*addrtocopy;
|
||||||
reply.have_answer = 1;
|
reply.have_answer = 1;
|
||||||
if (reply.data.aaaa.addrcount == MAX_ADDRS) break;
|
if (reply.data.aaaa.addrcount == MAX_V6_ADDRS) break;
|
||||||
} else {
|
} else {
|
||||||
/* skip over any other type of resource */
|
/* skip over any other type of resource */
|
||||||
j += datalength;
|
j += datalength;
|
||||||
@ -3206,6 +3234,14 @@ evdns_base_set_option_impl(struct evdns_base *base,
|
|||||||
if (!(flags & DNS_OPTION_MISC)) return 0;
|
if (!(flags & DNS_OPTION_MISC)) return 0;
|
||||||
log(EVDNS_LOG_DEBUG, "Setting timeout to %s", val);
|
log(EVDNS_LOG_DEBUG, "Setting timeout to %s", val);
|
||||||
memcpy(&base->global_timeout, &tv, sizeof(struct timeval));
|
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:")) {
|
} else if (str_matches_option(option, "max-timeouts:")) {
|
||||||
const int maxtimeout = strtoint_clipped(val, 1, 255);
|
const int maxtimeout = strtoint_clipped(val, 1, 255);
|
||||||
if (maxtimeout == -1) return -1;
|
if (maxtimeout == -1) return -1;
|
||||||
@ -3605,11 +3641,10 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
|
|||||||
{
|
{
|
||||||
struct evdns_base *base;
|
struct evdns_base *base;
|
||||||
|
|
||||||
/* Give the bufferevent library a hook into its evdns-enabled
|
/* Give the evutil library a hook into its evdns-enabled
|
||||||
* functionality. We can't do this correctly or else libevent-core
|
* functionality. We can't just call evdns_getaddrinfo directly or
|
||||||
* will depend on libevent-extras. */
|
* else libevent-core will depend on libevent-extras. */
|
||||||
_bufferevent_set_socket_connect_hostname_evdns_fn(
|
evutil_set_evdns_getaddrinfo_fn(evdns_getaddrinfo);
|
||||||
_bufferevent_socket_connect_hostname_evdns);
|
|
||||||
|
|
||||||
base = mm_malloc(sizeof(struct evdns_base));
|
base = mm_malloc(sizeof(struct evdns_base));
|
||||||
if (base == NULL)
|
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_max_nameserver_timeout = 3;
|
||||||
base->global_search_state = NULL;
|
base->global_search_state = NULL;
|
||||||
base->global_randomize_case = 1;
|
base->global_randomize_case = 1;
|
||||||
|
base->global_getaddrinfo_allow_skew.tv_sec = 3;
|
||||||
|
base->global_getaddrinfo_allow_skew.tv_usec = 0;
|
||||||
|
|
||||||
if (initialize_nameservers) {
|
if (initialize_nameservers) {
|
||||||
int r;
|
int r;
|
||||||
@ -3757,3 +3794,419 @@ evdns_shutdown(int fail_requests)
|
|||||||
evdns_log_fn = NULL;
|
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"
|
#include "event-config.h"
|
||||||
|
|
||||||
|
#define _REENTRANT
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
@ -59,9 +62,6 @@
|
|||||||
#ifdef _EVENT_HAVE_NETINET_IN6_H
|
#ifdef _EVENT_HAVE_NETINET_IN6_H
|
||||||
#include <netinet/in6.h>
|
#include <netinet/in6.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef _EVENT_HAVE_NETDB_H
|
|
||||||
#include <netdb.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _EVENT_HAVE_GETTIMEOFDAY
|
#ifndef _EVENT_HAVE_GETTIMEOFDAY
|
||||||
#include <sys/timeb.h>
|
#include <sys/timeb.h>
|
||||||
@ -71,6 +71,7 @@
|
|||||||
#include "event2/util.h"
|
#include "event2/util.h"
|
||||||
#include "util-internal.h"
|
#include "util-internal.h"
|
||||||
#include "log-internal.h"
|
#include "log-internal.h"
|
||||||
|
#include "mm-internal.h"
|
||||||
|
|
||||||
#include "strlcpy-internal.h"
|
#include "strlcpy-internal.h"
|
||||||
#include "ipv6-internal.h"
|
#include "ipv6-internal.h"
|
||||||
@ -322,92 +323,683 @@ evutil_socket_finished_connecting(evutil_socket_t fd)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Internal helper: use the host's (blocking) resolver to look up 'hostname',
|
/* We sometimes need to know whether we have an ipv4 address and whether we
|
||||||
* and set the sockaddr pointed to by 'sa' to the answer. Assume we have
|
have an ipv6 address. If 'have_checked_interfaces', then we've already done
|
||||||
* *socklen bytes of storage; adjust *socklen to the number of bytes used.
|
the test. If 'had_ipv4_address', then it turns out we had an ipv4 address.
|
||||||
* Try to return answers of type 'family', unless family is AF_UNSPEC.
|
If 'had_ipv6_address', then it turns out we had an ipv6 address. These are
|
||||||
* Return 0 on success and -1 on failure. If 'port' is nonzero, it is
|
set by evutil_check_interfaces. */
|
||||||
* a port number in host order: set the port in any resulting sockaddr to
|
static int have_checked_interfaces, had_ipv4_address, had_ipv6_address;
|
||||||
* the specified port.
|
|
||||||
|
/* 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
|
int
|
||||||
evutil_resolve(int family, const char *hostname, struct sockaddr *sa,
|
evutil_getaddrinfo_common(const char *nodename, const char *servname,
|
||||||
ev_socklen_t *socklen, int port)
|
struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum)
|
||||||
{
|
{
|
||||||
#ifdef _EVENT_HAVE_GETADDRINFO_XXX
|
int port = 0;
|
||||||
struct addrinfo hint, *hintp=NULL;
|
const char *pname;
|
||||||
struct addrinfo *ai=NULL;
|
|
||||||
int r;
|
|
||||||
memset(&hint, 0, sizeof(hint));
|
|
||||||
|
|
||||||
if (family != AF_UNSPEC) {
|
if (nodename == NULL && servname == NULL)
|
||||||
hint.ai_family = family;
|
return EVUTIL_EAI_NONAME;
|
||||||
hintp = &hint;
|
|
||||||
|
/* 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 we have no node name, then we're supposed to bind to 'any' and
|
||||||
if (!ai)
|
* connect to localhost. */
|
||||||
return -1;
|
if (nodename == NULL) {
|
||||||
if (r || ai->ai_addrlen > *socklen) {
|
struct evutil_addrinfo *res4=NULL, *res6=NULL;
|
||||||
/* log/report error? */
|
if (hints->ai_family != PF_INET) { /* INET6 or UNSPEC. */
|
||||||
freeaddrinfo(ai);
|
struct sockaddr_in6 sin6;
|
||||||
return -1;
|
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 we can, we should try to parse the hostname without resolving
|
||||||
if (port) {
|
* it. */
|
||||||
if (sa->sa_family == AF_INET)
|
/* Try ipv6. */
|
||||||
((struct sockaddr_in*)sa)->sin_port = htons(port);
|
if (hints->ai_family == PF_INET6 || hints->ai_family == PF_UNSPEC){
|
||||||
else if (sa->sa_family == AF_INET6)
|
struct sockaddr_in6 sin6;
|
||||||
((struct sockaddr_in6*)sa)->sin6_port = htons(port);
|
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);
|
/* Try ipv4. */
|
||||||
return 0;
|
if (hints->ai_family == PF_INET || hints->ai_family == PF_UNSPEC) {
|
||||||
#else
|
struct sockaddr_in sin;
|
||||||
/* XXXX use gethostbyname_r/gethostbyname2_r where available */
|
memset(&sin, 0, sizeof(sin));
|
||||||
struct hostent *he;
|
if (1==evutil_inet_pton(AF_INET, nodename, &sin.sin_addr)) {
|
||||||
struct sockaddr *sa_ptr;
|
/* 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_in sin;
|
||||||
struct sockaddr_in6 sin6;
|
struct sockaddr_in6 sin6;
|
||||||
ev_socklen_t slen;
|
struct sockaddr *sa;
|
||||||
he = gethostbyname(hostname);
|
int socklen;
|
||||||
if (!he || !he->h_length) {
|
struct evutil_addrinfo *res=NULL, *ai;
|
||||||
return -1;
|
void *addrp;
|
||||||
}
|
|
||||||
/* XXX handle multiple return values better. */
|
if (ent->h_addrtype == PF_INET) {
|
||||||
if (he->h_addrtype == AF_INET) {
|
|
||||||
if (family != AF_INET && family != AF_UNSPEC)
|
|
||||||
return -1;
|
|
||||||
memset(&sin, 0, sizeof(sin));
|
memset(&sin, 0, sizeof(sin));
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_port = htons(port);
|
sin.sin_port = htons(port);
|
||||||
memcpy(&sin.sin_addr, he->h_addr_list[0], 4);
|
sa = (struct sockaddr *)&sin;
|
||||||
sa_ptr = (struct sockaddr*)&sin;
|
socklen = sizeof(struct sockaddr_in);
|
||||||
slen = sizeof(struct sockaddr_in);
|
addrp = &sin.sin_addr;
|
||||||
} else if (he->h_addrtype == AF_INET6) {
|
if (ent->h_length != sizeof(sin.sin_addr)) {
|
||||||
if (family != AF_INET6 && family != AF_UNSPEC)
|
event_warnx("Weird h_length from gethostbyname");
|
||||||
return -1;
|
return NULL;
|
||||||
|
}
|
||||||
|
} else if (ent->h_addrtype == PF_INET6) {
|
||||||
|
memset(&sin6, 0, sizeof(sin6));
|
||||||
sin6.sin6_family = AF_INET6;
|
sin6.sin6_family = AF_INET6;
|
||||||
sin6.sin6_port = htons(port);
|
sin6.sin6_port = htons(port);
|
||||||
memset(&sin6, 0, sizeof(sin6));
|
sa = (struct sockaddr *)&sin6;
|
||||||
memcpy(sin6.sin6_addr.s6_addr, &he->h_addr_list[1], 16);
|
socklen = sizeof(struct sockaddr_in);
|
||||||
sa_ptr = (struct sockaddr*)&sin6;
|
addrp = &sin6.sin6_addr;
|
||||||
slen = sizeof(struct sockaddr_in6);
|
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 {
|
} else {
|
||||||
event_warnx("gethostbyname returned unknown family %d",
|
memset(&hints, 0, sizeof(hints));
|
||||||
he->h_addrtype);
|
hints.ai_family = PF_UNSPEC;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
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;
|
return 0;
|
||||||
#endif
|
#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
|
#ifdef WIN32
|
||||||
#define E(code, s) { code, (s " [" #code " ]") }
|
#define E(code, s) { code, (s " [" #code " ]") }
|
||||||
static struct { int code; const char *msg; } windows_socket_errors[] = {
|
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/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#else
|
||||||
|
#include <ws2tcpip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
@ -100,8 +102,13 @@
|
|||||||
#define NI_MAXSERV 32
|
#define NI_MAXSERV 32
|
||||||
#define NI_MAXHOST 1025
|
#define NI_MAXHOST 1025
|
||||||
|
|
||||||
|
#ifndef NI_NUMERICHOST
|
||||||
#define NI_NUMERICHOST 1
|
#define NI_NUMERICHOST 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NI_NUMERICSERV
|
||||||
#define NI_NUMERICSERV 2
|
#define NI_NUMERICSERV 2
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
|
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
|
#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
|
#ifndef MIN
|
||||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||||
#endif
|
#endif
|
||||||
@ -193,7 +156,7 @@ fake_freeaddrinfo(struct addrinfo *ai)
|
|||||||
extern int debug;
|
extern int debug;
|
||||||
|
|
||||||
static int socket_connect(evutil_socket_t kefd, const char *address, unsigned short port);
|
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 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 void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
|
||||||
static int evhttp_associate_new_request_with_connection(
|
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
|
* Network helper functions that we do not want to export to the rest of
|
||||||
* the world.
|
* 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
|
static void
|
||||||
name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
|
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);
|
NI_NUMERICHOST|NI_NUMERICSERV);
|
||||||
|
|
||||||
if (ni_result != 0) {
|
if (ni_result != 0) {
|
||||||
|
#ifdef EAI_SYSTEM
|
||||||
|
/* Windows doesn't have an EAI_SYSTEM. */
|
||||||
if (ni_result == EAI_SYSTEM)
|
if (ni_result == EAI_SYSTEM)
|
||||||
event_err(1, "getnameinfo failed");
|
event_err(1, "getnameinfo failed");
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
|
event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3041,7 +2981,7 @@ name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
|
|||||||
/* Create a non-blocking socket and bind it */
|
/* Create a non-blocking socket and bind it */
|
||||||
/* todo: rename this function */
|
/* todo: rename this function */
|
||||||
static evutil_socket_t
|
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;
|
evutil_socket_t fd;
|
||||||
|
|
||||||
@ -3084,49 +3024,40 @@ bind_socket_ai(struct addrinfo *ai, int reuse)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct addrinfo *
|
static struct evutil_addrinfo *
|
||||||
make_addrinfo(const char *address, ev_uint16_t port)
|
make_addrinfo(const char *address, ev_uint16_t port)
|
||||||
{
|
{
|
||||||
struct addrinfo *aitop = NULL;
|
struct evutil_addrinfo *ai = NULL;
|
||||||
|
|
||||||
#ifdef _EVENT_HAVE_GETADDRINFO
|
struct evutil_addrinfo hints;
|
||||||
struct addrinfo ai;
|
|
||||||
char strport[NI_MAXSERV];
|
char strport[NI_MAXSERV];
|
||||||
int ai_result;
|
int ai_result;
|
||||||
|
|
||||||
memset(&ai, 0, sizeof(ai));
|
memset(&hints, 0, sizeof(hints));
|
||||||
ai.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
ai.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
ai.ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */
|
/* 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);
|
evutil_snprintf(strport, sizeof(strport), "%d", port);
|
||||||
if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) {
|
if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai))
|
||||||
if ( ai_result == EAI_SYSTEM )
|
!= 0) {
|
||||||
|
if (ai_result == EVUTIL_EAI_SYSTEM)
|
||||||
event_warn("getaddrinfo");
|
event_warn("getaddrinfo");
|
||||||
else
|
else
|
||||||
event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
|
event_warnx("getaddrinfo: %s",
|
||||||
|
evutil_gai_strerror(ai_result));
|
||||||
return (NULL);
|
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) {
|
return (ai);
|
||||||
event_warn("fake_getaddrinfo");
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
aitop = &ai[cur];
|
|
||||||
((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (aitop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static evutil_socket_t
|
static evutil_socket_t
|
||||||
bind_socket(const char *address, ev_uint16_t port, int reuse)
|
bind_socket(const char *address, ev_uint16_t port, int reuse)
|
||||||
{
|
{
|
||||||
evutil_socket_t fd;
|
evutil_socket_t fd;
|
||||||
struct addrinfo *aitop = NULL;
|
struct evutil_addrinfo *aitop = NULL;
|
||||||
|
|
||||||
/* just create an unbound socket */
|
/* just create an unbound socket */
|
||||||
if (address == NULL && port == 0)
|
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);
|
fd = bind_socket_ai(aitop, reuse);
|
||||||
|
|
||||||
#ifdef _EVENT_HAVE_GETADDRINFO
|
evutil_freeaddrinfo(aitop);
|
||||||
freeaddrinfo(aitop);
|
|
||||||
#else
|
|
||||||
fake_freeaddrinfo(aitop);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (fd);
|
return (fd);
|
||||||
}
|
}
|
||||||
@ -3151,7 +3078,7 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
|
|||||||
static int
|
static int
|
||||||
socket_connect(evutil_socket_t fd, const char *address, unsigned short port)
|
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;
|
int res = -1;
|
||||||
|
|
||||||
if (ai == NULL) {
|
if (ai == NULL) {
|
||||||
@ -3170,11 +3097,7 @@ socket_connect(evutil_socket_t fd, const char *address, unsigned short port)
|
|||||||
res = 0;
|
res = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
#ifdef _EVENT_HAVE_GETADDRINFO
|
evutil_freeaddrinfo(ai);
|
||||||
freeaddrinfo(ai);
|
|
||||||
#else
|
|
||||||
fake_freeaddrinfo(ai);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (res);
|
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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,6 +67,16 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
int evdns_init(void);
|
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.
|
Shut down the asynchronous DNS resolver and terminate all active requests.
|
||||||
|
|
||||||
|
@ -56,6 +56,10 @@ extern "C" {
|
|||||||
#include <BaseTsd.h>
|
#include <BaseTsd.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stdarg.h>
|
#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
|
/* Integer type definitions for types that are supposed to be defined in the
|
||||||
* C99-specified stdint.h. Shamefully, some platforms do not include
|
* 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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,6 +57,37 @@ main_callback(int result, char type, int count, int ttl,
|
|||||||
fflush(stdout);
|
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
|
static void
|
||||||
evdns_server_callback(struct evdns_server_request *req, void *data)
|
evdns_server_callback(struct evdns_server_request *req, void *data)
|
||||||
{
|
{
|
||||||
@ -101,7 +132,7 @@ logfn(int is_warn, const char *msg) {
|
|||||||
int
|
int
|
||||||
main(int c, char **v) {
|
main(int c, char **v) {
|
||||||
int idx;
|
int idx;
|
||||||
int reverse = 0, servertest = 0;
|
int reverse = 0, servertest = 0, use_getaddrinfo = 0;
|
||||||
struct event_base *event_base = NULL;
|
struct event_base *event_base = NULL;
|
||||||
struct evdns_base *evdns_base = NULL;
|
struct evdns_base *evdns_base = NULL;
|
||||||
if (c<2) {
|
if (c<2) {
|
||||||
@ -115,6 +146,8 @@ main(int c, char **v) {
|
|||||||
reverse = 1;
|
reverse = 1;
|
||||||
else if (!strcmp(v[idx], "-v"))
|
else if (!strcmp(v[idx], "-v"))
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
|
else if (!strcmp(v[idx], "-g"))
|
||||||
|
use_getaddrinfo = 1;
|
||||||
else if (!strcmp(v[idx], "-servertest"))
|
else if (!strcmp(v[idx], "-servertest"))
|
||||||
servertest = 1;
|
servertest = 1;
|
||||||
else
|
else
|
||||||
@ -148,15 +181,26 @@ main(int c, char **v) {
|
|||||||
"/etc/resolv.conf");
|
"/etc/resolv.conf");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("EVUTIL_AI_CANONNAME in example = %d\n", EVUTIL_AI_CANONNAME);
|
||||||
for (; idx < c; ++idx) {
|
for (; idx < c; ++idx) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
struct in_addr addr;
|
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]);
|
fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "resolving %s...\n",v[idx]);
|
fprintf(stderr, "resolving %s...\n",v[idx]);
|
||||||
evdns_base_resolve_reverse(evdns_base, &addr, 0, main_callback, 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 {
|
} else {
|
||||||
fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
|
fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
|
||||||
evdns_base_resolve_ipv4(evdns_base, v[idx], 0, main_callback, 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, \
|
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
|
||||||
test_## name }
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,8 @@ static int dns_ok = 0;
|
|||||||
static int dns_got_cancel = 0;
|
static int dns_got_cancel = 0;
|
||||||
static int dns_err = 0;
|
static int dns_err = 0;
|
||||||
|
|
||||||
|
static int get_socket_port(evutil_socket_t fd);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dns_gethostbyname_cb(int result, char type, int count, int ttl,
|
dns_gethostbyname_cb(int result, char type, int count, int ttl,
|
||||||
void *addresses, void *arg)
|
void *addresses, void *arg)
|
||||||
@ -480,7 +482,7 @@ end:
|
|||||||
|
|
||||||
static struct evdns_server_port *
|
static struct evdns_server_port *
|
||||||
get_generic_server(struct event_base *base,
|
get_generic_server(struct event_base *base,
|
||||||
ev_uint16_t portnum,
|
ev_uint16_t *portnum,
|
||||||
evdns_request_callback_fn_type cb,
|
evdns_request_callback_fn_type cb,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
@ -497,12 +499,14 @@ get_generic_server(struct event_base *base,
|
|||||||
|
|
||||||
memset(&my_addr, 0, sizeof(my_addr));
|
memset(&my_addr, 0, sizeof(my_addr));
|
||||||
my_addr.sin_family = AF_INET;
|
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);
|
my_addr.sin_addr.s_addr = htonl(0x7f000001UL);
|
||||||
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) {
|
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) {
|
||||||
tt_abort_perror("bind");
|
tt_abort_perror("bind");
|
||||||
}
|
}
|
||||||
port = evdns_add_server_port_with_base(base, sock, 0, cb, arg);
|
port = evdns_add_server_port_with_base(base, sock, 0, cb, arg);
|
||||||
|
if (!*portnum)
|
||||||
|
*portnum = get_socket_port(sock);
|
||||||
|
|
||||||
return port;
|
return port;
|
||||||
end:
|
end:
|
||||||
@ -571,10 +575,11 @@ dns_search_test(void *arg)
|
|||||||
struct event_base *base = data->base;
|
struct event_base *base = data->base;
|
||||||
struct evdns_server_port *port = NULL;
|
struct evdns_server_port *port = NULL;
|
||||||
struct evdns_base *dns = 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;
|
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);
|
search_table);
|
||||||
tt_assert(port);
|
tt_assert(port);
|
||||||
|
|
||||||
@ -655,10 +660,11 @@ dns_retry_test(void *arg)
|
|||||||
struct evdns_server_port *port = NULL;
|
struct evdns_server_port *port = NULL;
|
||||||
struct evdns_base *dns = NULL;
|
struct evdns_base *dns = NULL;
|
||||||
int drop_count = 2;
|
int drop_count = 2;
|
||||||
|
ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
|
||||||
|
|
||||||
struct generic_dns_callback_result r1;
|
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);
|
&drop_count);
|
||||||
tt_assert(port);
|
tt_assert(port);
|
||||||
|
|
||||||
@ -743,11 +749,12 @@ dns_reissue_test(void *arg)
|
|||||||
struct evdns_server_port *port1 = NULL, *port2 = NULL;
|
struct evdns_server_port *port1 = NULL, *port2 = NULL;
|
||||||
struct evdns_base *dns = NULL;
|
struct evdns_base *dns = NULL;
|
||||||
struct generic_dns_callback_result r1;
|
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);
|
internal_error_table);
|
||||||
tt_assert(port1);
|
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);
|
reissue_table);
|
||||||
tt_assert(port2);
|
tt_assert(port2);
|
||||||
|
|
||||||
@ -802,11 +809,12 @@ dns_inflight_test(void *arg)
|
|||||||
struct event_base *base = data->base;
|
struct event_base *base = data->base;
|
||||||
struct evdns_server_port *port = NULL;
|
struct evdns_server_port *port = NULL;
|
||||||
struct evdns_base *dns = 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];
|
struct generic_dns_callback_result r[20];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
port = get_generic_server(base, 53900, generic_dns_server_cb,
|
port = get_generic_server(base, &portnum, generic_dns_server_cb,
|
||||||
reissue_table);
|
reissue_table);
|
||||||
tt_assert(port);
|
tt_assert(port);
|
||||||
|
|
||||||
@ -845,9 +853,10 @@ end:
|
|||||||
static int total_connected_or_failed = 0;
|
static int total_connected_or_failed = 0;
|
||||||
static struct event_base *be_connect_hostname_base = NULL;
|
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
|
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 i;
|
||||||
int *n_got_p=data;
|
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 int qclass = req->questions[i]->dns_question_class;
|
||||||
const char *qname = req->questions[i]->name;
|
const char *qname = req->questions[i]->name;
|
||||||
struct in_addr ans;
|
struct in_addr ans;
|
||||||
|
struct in6_addr ans6;
|
||||||
|
memset(&ans6, 0, sizeof(ans6));
|
||||||
|
|
||||||
if (qtype == EVDNS_TYPE_A &&
|
if (qtype == EVDNS_TYPE_A &&
|
||||||
qclass == EVDNS_CLASS_INET &&
|
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,
|
} else if (!evutil_ascii_strcasecmp(qname,
|
||||||
"nosuchplace.example.com")) {
|
"nosuchplace.example.com")) {
|
||||||
/* ok, just say notfound. */
|
/* 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 {
|
} else {
|
||||||
TT_GRIPE(("Got weird request for %s",qname));
|
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)) {
|
if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) {
|
||||||
++total_connected_or_failed;
|
++total_connected_or_failed;
|
||||||
|
TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed));
|
||||||
if (total_connected_or_failed >= 5)
|
if (total_connected_or_failed >= 5)
|
||||||
event_base_loopexit(be_connect_hostname_base,
|
event_base_loopexit(be_connect_hostname_base,
|
||||||
NULL);
|
NULL);
|
||||||
@ -940,7 +1018,8 @@ test_bufferevent_connect_hostname(void *arg)
|
|||||||
struct evdns_server_port *port=NULL;
|
struct evdns_server_port *port=NULL;
|
||||||
evutil_socket_t server_fd=-1;
|
evutil_socket_t server_fd=-1;
|
||||||
struct sockaddr_in sin;
|
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;
|
int n_accept=0, n_dns=0;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
@ -957,6 +1036,7 @@ test_bufferevent_connect_hostname(void *arg)
|
|||||||
-1, (struct sockaddr *)&sin, sizeof(sin));
|
-1, (struct sockaddr *)&sin, sizeof(sin));
|
||||||
listener_port = get_socket_port(evconnlistener_get_fd(listener));
|
listener_port = get_socket_port(evconnlistener_get_fd(listener));
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* Start an evdns server that resolves nobodaddy.example.com to
|
/* Start an evdns server that resolves nobodaddy.example.com to
|
||||||
* 127.0.0.1 */
|
* 127.0.0.1 */
|
||||||
memset(&sin, 0, sizeof(sin));
|
memset(&sin, 0, sizeof(sin));
|
||||||
@ -971,7 +1051,12 @@ test_bufferevent_connect_hostname(void *arg)
|
|||||||
evutil_make_socket_nonblocking(server_fd);
|
evutil_make_socket_nonblocking(server_fd);
|
||||||
dns_port = get_socket_port(server_fd);
|
dns_port = get_socket_port(server_fd);
|
||||||
port = evdns_add_server_port_with_base(data->base, server_fd, 0,
|
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. */
|
/* Start an evdns_base that uses the server as its resolver. */
|
||||||
dns = evdns_base_new(data->base, 0);
|
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,
|
tt_assert(!bufferevent_socket_connect_hostname(be4, NULL, AF_INET,
|
||||||
"localhost", listener_port));
|
"localhost", listener_port));
|
||||||
/* Use the blocking resolver with a nonexistent hostname. */
|
/* Use the blocking resolver with a nonexistent hostname. */
|
||||||
tt_assert(bufferevent_socket_connect_hostname(be5, NULL, AF_INET,
|
tt_assert(!bufferevent_socket_connect_hostname(be5, NULL, AF_INET,
|
||||||
"nonesuch.nowhere.example.com", 80) < 0);
|
"nonesuch.nowhere.example.com", 80));
|
||||||
|
|
||||||
event_base_dispatch(data->base);
|
event_base_dispatch(data->base);
|
||||||
|
|
||||||
@ -1047,6 +1132,326 @@ end:
|
|||||||
bufferevent_free(be5);
|
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) \
|
#define DNS_LEGACY(name, flags) \
|
||||||
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
|
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
|
||||||
dns_##name }
|
dns_##name }
|
||||||
@ -1064,6 +1469,9 @@ struct testcase_t dns_testcases[] = {
|
|||||||
{ "bufferevent_connnect_hostname", test_bufferevent_connect_hostname,
|
{ "bufferevent_connnect_hostname", test_bufferevent_connect_hostname,
|
||||||
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
|
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
|
||||||
|
|
||||||
|
{ "getaddrinfo_async", test_getaddrinfo_async,
|
||||||
|
TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" },
|
||||||
|
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2042,7 +2042,6 @@ http_connection_retry_test(void)
|
|||||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
|
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
|
||||||
"/?arg=val") == -1) {
|
"/?arg=val") == -1) {
|
||||||
tt_abort_msg("Couldn't make request");
|
tt_abort_msg("Couldn't make request");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start up a web server one second after the connection tried
|
/* start up a web server one second after the connection tried
|
||||||
|
@ -489,51 +489,267 @@ end:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
struct evutil_addrinfo *
|
||||||
test_evutil_resolve(void *arg)
|
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;
|
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_in6 *sin6;
|
||||||
struct sockaddr_in *sin;
|
struct sockaddr_in *sin;
|
||||||
ev_socklen_t socklen = sizeof(ss);
|
|
||||||
char buf[128];
|
char buf[128];
|
||||||
const char *cp;
|
const char *cp;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
memset(&ss, 0xff, sizeof(ss)); /* Make sure it starts out confused.*/
|
/* Try using it as a pton. */
|
||||||
r = evutil_resolve(AF_INET, "www.google.com", (struct sockaddr*)&ss,
|
memset(&hints, 0, sizeof(hints));
|
||||||
&socklen, 80);
|
hints.ai_family = PF_UNSPEC;
|
||||||
if (r<0) {
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
TT_BLATHER(("Couldn't resolve www.google.com"));
|
r = evutil_getaddrinfo("1.2.3.4", "8080", &hints, &ai);
|
||||||
tt_skip();
|
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));
|
/* Now do some actual lookups. */
|
||||||
TT_BLATHER(("www.google.com resolved to %s",cp?cp:"<unwriteable>"));
|
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.*/
|
cp = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
|
||||||
socklen = sizeof(ss);
|
TT_BLATHER(("www.google.com resolved to %s",
|
||||||
r = evutil_resolve(AF_INET6, "ipv6.google.com", (struct sockaddr*)&ss,
|
cp?cp:"<unwriteable>"));
|
||||||
&socklen, 80);
|
evutil_freeaddrinfo(ai);
|
||||||
if (r<0) {
|
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"));
|
TT_BLATHER(("Couldn't do an ipv6 lookup for ipv6.google.com"));
|
||||||
goto end;
|
} else {
|
||||||
}
|
tt_assert(ai);
|
||||||
tt_int_op(ss.ss_family, ==, AF_INET6);
|
tt_int_op(ai->ai_family, ==, PF_INET6);
|
||||||
tt_int_op(socklen, ==, sizeof(struct sockaddr_in6));
|
tt_int_op(ai->ai_addrlen, ==, sizeof(struct sockaddr_in6));
|
||||||
sin6 = (struct sockaddr_in6*)&ss;
|
sin6 = (struct sockaddr_in6*)ai->ai_addr;
|
||||||
tt_int_op(sin6->sin6_port, ==, htons(80));
|
tt_int_op(sin6->sin6_port, ==, htons(80));
|
||||||
|
|
||||||
cp = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
|
cp = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf,
|
||||||
TT_BLATHER(("ipv6.google.com resolved to %s",cp?cp:"<unwriteable>"));
|
sizeof(buf));
|
||||||
|
TT_BLATHER(("ipv6.google.com resolved to %s",
|
||||||
|
cp?cp:"<unwriteable>"));
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
;
|
if (ai)
|
||||||
|
evutil_freeaddrinfo(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct testcase_t util_testcases[] = {
|
struct testcase_t util_testcases[] = {
|
||||||
@ -546,7 +762,7 @@ struct testcase_t util_testcases[] = {
|
|||||||
{ "strlcpy", test_evutil_strlcpy, 0, NULL, NULL },
|
{ "strlcpy", test_evutil_strlcpy, 0, NULL, NULL },
|
||||||
{ "log", test_evutil_log, TT_FORK, NULL, NULL },
|
{ "log", test_evutil_log, TT_FORK, NULL, NULL },
|
||||||
{ "upcast", test_evutil_upcast, 0, 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,
|
END_OF_TESTCASES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,6 +205,35 @@ const char *evutil_getenv(const char *name);
|
|||||||
#define EV_SIZE_MAX ((size_t)-1)
|
#define EV_SIZE_MAX ((size_t)-1)
|
||||||
#endif
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user