Add ares__sortaddrinfo() to support getaddrinfo() sorted results (#239)

This is a port of RFC 6724 compliant sorting function from Android Bionic project:
e919b116d3/libc/netbsd/net/getaddrinfo.c

The latest version is essentially the same, except two additional parameters to test connection with (mark/uid):
https://android.googlesource.com/platform/bionic/+/master/libc/dns/net/getaddrinfo.c

Please note that even that version has some restrictions. It doesn't support some rules from RFC 6724:

Rule 3 (Avoid deprecated addresses)
Rule 4 (Prefer home addresses)
Rule 7 (Prefer native transport)

Submitted By: Andrew Selivanov (@ki11roy)
pull/241/head
Andrew Selivanov 6 years ago committed by Brad House
parent b688805423
commit a9c2068e25
  1. 1
      Makefile.inc
  2. 1
      ares.h
  3. 495
      ares__sortaddrinfo.c
  4. 18
      ares_getaddrinfo.3
  5. 14
      ares_getaddrinfo.c
  6. 7
      ares_ipv6.h
  7. 1
      ares_private.h
  8. 11
      test/ares-test-ai.h
  9. 47
      test/ares-test-live-ai.cc
  10. 286
      test/ares-test-mock-ai.cc
  11. 58
      test/ares-test.cc
  12. 28
      test/ares-test.h

@ -1,6 +1,7 @@
CSOURCES = ares__close_sockets.c \
ares__get_hostent.c \
ares__sortaddrinfo.c \
ares__read_line.c \
ares__timeval.c \
ares_android.c \

@ -309,6 +309,7 @@ typedef int (*ares_sock_config_callback)(ares_socket_t socket_fd,
typedef void (*ares_addr_callback)(void *arg,
int status,
int timeouts,
struct ares_addrinfo *res);
CARES_EXTERN int ares_library_init(int flags);

@ -0,0 +1,495 @@
/*
* Original file name getaddrinfo.c
* Lifted from the 'Android Bionic' project with the BSD license.
*/
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* Copyright (C) 2018 The Android Open Source Project
* Copyright (C) 2019 by Andrew Selivanov
* All rights reserved.
*
* 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. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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.
*/
#include "ares_setup.h"
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <assert.h>
#include <limits.h>
#include "ares.h"
#include "ares_private.h"
struct addrinfo_sort_elem
{
struct ares_addrinfo *ai;
int has_src_addr;
ares_sockaddr src_addr;
int original_order;
};
#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f)
#define IPV6_ADDR_SCOPE_NODELOCAL 0x01
#define IPV6_ADDR_SCOPE_INTFACELOCAL 0x01
#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02
#define IPV6_ADDR_SCOPE_SITELOCAL 0x05
#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08
#define IPV6_ADDR_SCOPE_GLOBAL 0x0e
#define IN_LOOPBACK(a) ((((long int)(a)) & 0xff000000) == 0x7f000000)
/* RFC 4193. */
#define IN6_IS_ADDR_ULA(a) (((a)->s6_addr[0] & 0xfe) == 0xfc)
/* These macros are modelled after the ones in <netinet/in6.h>. */
/* RFC 4380, section 2.6 */
#define IN6_IS_ADDR_TEREDO(a) \
((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == ntohl(0x20010000)))
/* RFC 3056, section 2. */
#define IN6_IS_ADDR_6TO4(a) \
(((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02))
/* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */
#define IN6_IS_ADDR_6BONE(a) \
(((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe))
static int get_scope(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET6)
{
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr))
{
return IPV6_ADDR_MC_SCOPE(&addr6->sin6_addr);
}
else if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) ||
IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr))
{
/*
* RFC 4291 section 2.5.3 says loopback is to be treated as having
* link-local scope.
*/
return IPV6_ADDR_SCOPE_LINKLOCAL;
}
else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr))
{
return IPV6_ADDR_SCOPE_SITELOCAL;
}
else
{
return IPV6_ADDR_SCOPE_GLOBAL;
}
}
else if (addr->sa_family == AF_INET)
{
const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
unsigned long int na = ntohl(addr4->sin_addr.s_addr);
if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */
(na & 0xffff0000) == 0xa9fe0000) /* 169.254.0.0/16 */
{
return IPV6_ADDR_SCOPE_LINKLOCAL;
}
else
{
/*
* RFC 6724 section 3.2. Other IPv4 addresses, including private
* addresses and shared addresses (100.64.0.0/10), are assigned global
* scope.
*/
return IPV6_ADDR_SCOPE_GLOBAL;
}
}
else
{
/*
* This should never happen.
* Return a scope with low priority as a last resort.
*/
return IPV6_ADDR_SCOPE_NODELOCAL;
}
}
static int get_label(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET)
{
return 4;
}
else if (addr->sa_family == AF_INET6)
{
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr))
{
return 0;
}
else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr))
{
return 4;
}
else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr))
{
return 2;
}
else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr))
{
return 5;
}
else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr))
{
return 13;
}
else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr))
{
return 3;
}
else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr))
{
return 11;
}
else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr))
{
return 12;
}
else
{
/* All other IPv6 addresses, including global unicast addresses. */
return 1;
}
}
else
{
/*
* This should never happen.
* Return a semi-random label as a last resort.
*/
return 1;
}
}
/*
* Get the precedence for a given IPv4/IPv6 address.
* RFC 6724, section 2.1.
*/
static int get_precedence(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET)
{
return 35;
}
else if (addr->sa_family == AF_INET6)
{
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr))
{
return 50;
}
else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr))
{
return 35;
}
else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr))
{
return 30;
}
else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr))
{
return 5;
}
else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr))
{
return 3;
}
else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) ||
IN6_IS_ADDR_6BONE(&addr6->sin6_addr))
{
return 1;
}
else
{
/* All other IPv6 addresses, including global unicast addresses. */
return 40;
}
}
else
{
return 1;
}
}
/*
* Find number of matching initial bits between the two addresses a1 and a2.
*/
static int common_prefix_len(const struct in6_addr *a1,
const struct in6_addr *a2)
{
const char *p1 = (const char *)a1;
const char *p2 = (const char *)a2;
unsigned i;
for (i = 0; i < sizeof(*a1); ++i)
{
int x, j;
if (p1[i] == p2[i])
{
continue;
}
x = p1[i] ^ p2[i];
for (j = 0; j < CHAR_BIT; ++j)
{
if (x & (1 << (CHAR_BIT - 1)))
{
return i * CHAR_BIT + j;
}
x <<= 1;
}
}
return sizeof(*a1) * CHAR_BIT;
}
/*
* Compare two source/destination address pairs.
* RFC 6724, section 6.
*/
static int rfc6724_compare(const void *ptr1, const void *ptr2)
{
const struct addrinfo_sort_elem *a1 = (const struct addrinfo_sort_elem *)ptr1;
const struct addrinfo_sort_elem *a2 = (const struct addrinfo_sort_elem *)ptr2;
int scope_src1, scope_dst1, scope_match1;
int scope_src2, scope_dst2, scope_match2;
int label_src1, label_dst1, label_match1;
int label_src2, label_dst2, label_match2;
int precedence1, precedence2;
int prefixlen1, prefixlen2;
/* Rule 1: Avoid unusable destinations. */
if (a1->has_src_addr != a2->has_src_addr)
{
return a2->has_src_addr - a1->has_src_addr;
}
/* Rule 2: Prefer matching scope. */
scope_src1 = get_scope(&a1->src_addr.sa);
scope_dst1 = get_scope(a1->ai->ai_addr);
scope_match1 = (scope_src1 == scope_dst1);
scope_src2 = get_scope(&a2->src_addr.sa);
scope_dst2 = get_scope(a2->ai->ai_addr);
scope_match2 = (scope_src2 == scope_dst2);
if (scope_match1 != scope_match2)
{
return scope_match2 - scope_match1;
}
/* Rule 3: Avoid deprecated addresses. */
/* Rule 4: Prefer home addresses. */
/* Rule 5: Prefer matching label. */
label_src1 = get_label(&a1->src_addr.sa);
label_dst1 = get_label(a1->ai->ai_addr);
label_match1 = (label_src1 == label_dst1);
label_src2 = get_label(&a2->src_addr.sa);
label_dst2 = get_label(a2->ai->ai_addr);
label_match2 = (label_src2 == label_dst2);
if (label_match1 != label_match2)
{
return label_match2 - label_match1;
}
/* Rule 6: Prefer higher precedence. */
precedence1 = get_precedence(a1->ai->ai_addr);
precedence2 = get_precedence(a2->ai->ai_addr);
if (precedence1 != precedence2)
{
return precedence2 - precedence1;
}
/* Rule 7: Prefer native transport. */
/* Rule 8: Prefer smaller scope. */
if (scope_dst1 != scope_dst2)
{
return scope_dst1 - scope_dst2;
}
/* Rule 9: Use longest matching prefix. */
if (a1->has_src_addr && a1->ai->ai_addr->sa_family == AF_INET6 &&
a2->has_src_addr && a2->ai->ai_addr->sa_family == AF_INET6)
{
const struct sockaddr_in6 *a1_src = &a1->src_addr.sa6;
const struct sockaddr_in6 *a1_dst =
(const struct sockaddr_in6 *)a1->ai->ai_addr;
const struct sockaddr_in6 *a2_src = &a2->src_addr.sa6;
const struct sockaddr_in6 *a2_dst =
(const struct sockaddr_in6 *)a2->ai->ai_addr;
prefixlen1 = common_prefix_len(&a1_src->sin6_addr, &a1_dst->sin6_addr);
prefixlen2 = common_prefix_len(&a2_src->sin6_addr, &a2_dst->sin6_addr);
if (prefixlen1 != prefixlen2)
{
return prefixlen2 - prefixlen1;
}
}
/*
* Rule 10: Leave the order unchanged.
* We need this since qsort() is not necessarily stable.
*/
return a1->original_order - a2->original_order;
}
/*
* Find the source address that will be used if trying to connect to the given
* address.
*
* Returns 1 if a source address was found, 0 if the address is unreachable,
* and -1 if a fatal error occurred. If 0 or 1, the contents of src_addr are
* undefined.
*/
static int find_src_addr(ares_channel channel,
const struct sockaddr *addr,
struct sockaddr *src_addr)
{
int sock;
int ret;
socklen_t len;
switch (addr->sa_family)
{
case AF_INET:
len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
default:
/* No known usable source address for non-INET families. */
return 0;
}
sock = channel->sock_funcs->asocket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP, channel->sock_func_cb_data);
if (sock == -1)
{
if (errno == EAFNOSUPPORT)
{
return 0;
}
else
{
return -1;
}
}
do
{
ret = channel->sock_funcs->aconnect(sock, addr, len, channel->sock_func_cb_data);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
channel->sock_funcs->aclose(sock, channel->sock_func_cb_data);
return 0;
}
if (getsockname(sock, src_addr, &len) == -1)
{
channel->sock_funcs->aclose(sock, channel->sock_func_cb_data);
return -1;
}
channel->sock_funcs->aclose(sock, channel->sock_func_cb_data);
return 1;
}
/*
* Sort the linked list starting at sentinel->ai_next in RFC6724 order.
* Will leave the list unchanged if an error occurs.
*/
int ares__sortaddrinfo(ares_channel channel, struct ares_addrinfo *list_sentinel)
{
struct ares_addrinfo *cur;
int nelem = 0, i;
int has_src_addr;
struct addrinfo_sort_elem *elems;
cur = list_sentinel->ai_next;
while (cur)
{
++nelem;
cur = cur->ai_next;
}
elems = (struct addrinfo_sort_elem *)ares_malloc(
nelem * sizeof(struct addrinfo_sort_elem));
if (!elems)
{
return ARES_ENOMEM;
}
/*
* Convert the linked list to an array that also contains the candidate
* source address for each destination address.
*/
for (i = 0, cur = list_sentinel->ai_next; i < nelem; ++i, cur = cur->ai_next)
{
assert(cur != NULL);
elems[i].ai = cur;
elems[i].original_order = i;
has_src_addr = find_src_addr(channel, cur->ai_addr, &elems[i].src_addr.sa);
if (has_src_addr == -1)
{
ares_free(elems);
return ARES_ENOTFOUND;
}
elems[i].has_src_addr = has_src_addr;
}
/* Sort the addresses, and rearrange the linked list so it matches the sorted
* order. */
qsort((void *)elems, nelem, sizeof(struct addrinfo_sort_elem),
rfc6724_compare);
list_sentinel->ai_next = elems[0].ai;
for (i = 0; i < nelem - 1; ++i)
{
elems[i].ai->ai_next = elems[i + 1].ai;
}
elems[nelem - 1].ai->ai_next = NULL;
ares_free(elems);
return ARES_SUCCESS;
}

@ -21,7 +21,7 @@ ares_getaddrinfo \- Initiate a host query by name
.B #include <ares.h>
.PP
.B typedef void (*ares_addr_callback)(void *\fIarg\fP, int \fIstatus\fP,
.B struct ares_addrinfo *\fIresult\fP)
.B int \fItimeouts\fP, struct ares_addrinfo *\fIresult\fP)
.PP
.B void ares_getaddrinfo(ares_channel \fIchannel\fP, const char *\fIname\fP,
.B const char* \fIservice\fP, const struct ares_addrinfo *\fIhints\fP,
@ -98,17 +98,25 @@ On successful completion of the query, the callback argument
points to a
.B struct addrinfo
which is a linked list, where each item contains family and address of
the requested name. The list is not sorted. The reserved memory has to be
deleted by
the requested name. The reserved memory has to be deleted by
.B ares_freeaddrinfo.
The result is sorted according to RFC6724 except:
- Rule 3 (Avoid deprecated addresses)
- Rule 4 (Prefer home addresses)
- Rule 7 (Prefer native transport)
Please note that the function will attempt a connection
on each of the resolved addresses as per RFC6724.
.SH SEE ALSO
.BR ares_freeaddrinfo (3)
.SH AUTHOR
Christian Ammer
.br
Andrew Selivanov <andrew.selivanov@gmail.com>
.SH CAVEATS
This function is under development. It only supports a minimum feature set
of the function
.B getaddrinfo
defined in RFC-3493. It also does not support the destination address selection
algorithm defined in RFC-6724.
defined in RFC-3493
.br

@ -91,20 +91,21 @@ void ares_getaddrinfo(ares_channel channel,
const char* node, const char* service,
const struct ares_addrinfo* hints,
ares_addr_callback callback, void* arg) {
struct ares_addrinfo sentinel;
struct host_query *hquery;
char *single = NULL;
int ai_family;
ai_family = hints ? hints->ai_family : AF_UNSPEC;
if (!is_implemented(ai_family)) {
callback(arg, ARES_ENOTIMP, NULL);
callback(arg, ARES_ENOTIMP, 0, NULL);
return;
}
/* Allocate and fill in the host query structure. */
hquery = ares_malloc(sizeof(struct host_query));
if (!hquery) {
callback(arg, ARES_ENOMEM, NULL);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->ai = NULL;
@ -115,7 +116,7 @@ void ares_getaddrinfo(ares_channel channel,
hquery->sent_family = -1; /* nothing is sent yet */
if (!hquery->name) {
ares_free(hquery);
callback(arg, ARES_ENOMEM, NULL);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->callback = callback;
@ -126,6 +127,9 @@ void ares_getaddrinfo(ares_channel channel,
/* Host file lookup */
if (file_lookup(hquery->name, ai_family, &hquery->ai) == ARES_SUCCESS) {
sentinel.ai_next = hquery->ai;
ares__sortaddrinfo(channel, &sentinel);
hquery->ai = sentinel.ai_next;
end_hquery(hquery, ARES_SUCCESS);
}
else {
@ -251,6 +255,7 @@ static int add_to_addrinfo(struct ares_addrinfo** ai,
front->ai_family = AF_INET;
front->ai_addr = ares_malloc(sizeof(struct sockaddr_in));
if (!front->ai_addr) goto nomem;
memset(front->ai_addr, 0, sizeof(struct sockaddr_in));
memcpy(&((struct sockaddr_in*)(front->ai_addr))->sin_addr, *p,
host->h_length);
}
@ -259,6 +264,7 @@ static int add_to_addrinfo(struct ares_addrinfo** ai,
front->ai_family = AF_INET6;
front->ai_addr = ares_malloc(sizeof(struct sockaddr_in6));
if (!front->ai_addr) goto nomem;
memset(front->ai_addr, 0, sizeof(struct sockaddr_in6));
memcpy(&((struct sockaddr_in6*)(front->ai_addr))->sin6_addr, *p,
host->h_length);
}
@ -310,7 +316,7 @@ static void next_dns_lookup(struct host_query *hquery) {
}
static void end_hquery(struct host_query *hquery, int status) {
hquery->callback(hquery->arg, status, hquery->ai);
hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai);
ares_free(hquery->name);
ares_free(hquery);
}

@ -32,6 +32,13 @@ struct sockaddr_in6
};
#endif
typedef union
{
struct sockaddr sa;
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} ares_sockaddr;
#ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo
{

@ -358,6 +358,7 @@ void ares__destroy_servers_state(ares_channel channel);
int ares__parse_qtype_reply(const unsigned char* abuf, int alen, int* qtype);
int ares__single_domain(ares_channel channel, const char *name, char **s);
int ares__cat_domain(const char *name, const char *domain, char **s);
int ares__sortaddrinfo(ares_channel channel, struct ares_addrinfo *ai);
#if 0 /* Not used */
long ares__tvdiff(struct timeval t1, struct timeval t2);

@ -51,17 +51,6 @@ class DefaultChannelTestAI : public LibraryTest {
ares_channel channel_;
};
// Structure that describes the result of an ares_addr_callback invocation.
struct AIResult {
AIResult() : done(), status(), airesult() {}
// Whether the callback has been invoked.
bool done;
// Explicitly provided result information.
int status;
// Contents of the ares_addrinfo structure, if provided.
struct ares_addrinfo* airesult;
};
}
}

@ -15,20 +15,20 @@ namespace test {
MATCHER_P(IncludesAtLeastNumAddresses, n, "") {
int cnt = 0;
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next)
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next)
cnt++;
return cnt >= n;
}
MATCHER_P(OnlyIncludesAddrType, addrtype, "") {
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next)
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next)
if (ai->ai_family != addrtype)
return false;
return true;
}
MATCHER_P(IncludesAddrType, addrtype, "") {
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next)
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next)
if (ai->ai_family == addrtype)
return true;
return false;
@ -44,41 +44,38 @@ void DefaultChannelTestAI::Process() {
VIRT_NONVIRT_TEST_F(DefaultChannelTestAI, LiveGetHostByNameV4) {
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
AIResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
AddrInfoResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_SUCCESS, result.status);
EXPECT_THAT(result.airesult, IncludesAtLeastNumAddresses(1));
EXPECT_THAT(result.airesult, OnlyIncludesAddrType(AF_INET));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesAtLeastNumAddresses(1));
EXPECT_THAT(result.ai_, OnlyIncludesAddrType(AF_INET));
}
VIRT_NONVIRT_TEST_F(DefaultChannelTestAI, LiveGetHostByNameV6) {
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET6;
AIResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
AddrInfoResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_SUCCESS, result.status);
EXPECT_THAT(result.airesult, IncludesAtLeastNumAddresses(1));
EXPECT_THAT(result.airesult, OnlyIncludesAddrType(AF_INET6));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesAtLeastNumAddresses(1));
EXPECT_THAT(result.ai_, OnlyIncludesAddrType(AF_INET6));
}
VIRT_NONVIRT_TEST_F(DefaultChannelTestAI, LiveGetHostByNameV4AndV6) {
struct ares_addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
AIResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
AddrInfoResult result;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_SUCCESS, result.status);
EXPECT_THAT(result.airesult, IncludesAtLeastNumAddresses(2));
EXPECT_THAT(result.airesult, IncludesAddrType(AF_INET6));
EXPECT_THAT(result.airesult, IncludesAddrType(AF_INET));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesAtLeastNumAddresses(2));
EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET6));
EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET));
}
} // namespace test

@ -15,17 +15,21 @@ namespace ares {
namespace test {
MATCHER_P(IncludesNumAddresses, n, "") {
if(!arg)
return false;
int cnt = 0;
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next)
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next)
cnt++;
return n == cnt;
}
MATCHER_P(IncludesV4Address, address, "") {
if(!arg)
return false;
in_addr addressnum = {};
if (!ares_inet_pton(AF_INET, address, &addressnum))
return false; // wrong number format?
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next) {
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next) {
if (ai->ai_family != AF_INET)
continue;
if (reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr.s_addr ==
@ -36,11 +40,13 @@ MATCHER_P(IncludesV4Address, address, "") {
}
MATCHER_P(IncludesV6Address, address, "") {
if(!arg)
return false;
in6_addr addressnum = {};
if (!ares_inet_pton(AF_INET6, address, &addressnum)) {
return false; // wrong number format?
}
for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next) {
for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next) {
if (ai->ai_family != AF_INET6)
continue;
if (!memcmp(
@ -68,31 +74,28 @@ TEST_P(MockUDPChannelTestAI, ParallelLookups) {
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
AIResult result1;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result1);
AIResult result2;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AICallback, &result2);
AIResult result3;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result3);
AddrInfoResult result1;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result1);
AddrInfoResult result2;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result2);
AddrInfoResult result3;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result3);
Process();
EXPECT_TRUE(result1.done);
EXPECT_EQ(result1.status, ARES_SUCCESS);
EXPECT_THAT(result1.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result1.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result1.airesult);
EXPECT_TRUE(result2.done);
EXPECT_EQ(result2.status, ARES_SUCCESS);
EXPECT_THAT(result2.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result2.airesult, IncludesV4Address("1.2.3.4"));
ares_freeaddrinfo(result2.airesult);
EXPECT_TRUE(result3.done);
EXPECT_EQ(result3.status, ARES_SUCCESS);
EXPECT_THAT(result3.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result3.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result3.airesult);
EXPECT_TRUE(result1.done_);
EXPECT_EQ(result1.status_, ARES_SUCCESS);
EXPECT_THAT(result1.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result1.ai_, IncludesV4Address("2.3.4.5"));
EXPECT_TRUE(result2.done_);
EXPECT_EQ(result2.status_, ARES_SUCCESS);
EXPECT_THAT(result2.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result2.ai_, IncludesV4Address("1.2.3.4"));
EXPECT_TRUE(result3.done_);
EXPECT_EQ(result3.status_, ARES_SUCCESS);
EXPECT_THAT(result3.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result3.ai_, IncludesV4Address("2.3.4.5"));
}
// UDP to TCP specific test
@ -108,16 +111,15 @@ TEST_P(MockUDPChannelTestAI, TruncationRetry) {
.WillOnce(SetReply(&server_, &rsptruncated))
.WillOnce(SetReply(&server_, &rspok));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("1.2.3.4"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
}
// TCP only to prevent retries
@ -126,14 +128,13 @@ TEST_P(MockTCPChannelTestAI, MalformedResponse) {
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReplyData(&server_, one));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_ETIMEOUT, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ETIMEOUT, result.status_);
}
TEST_P(MockTCPChannelTestAI, FormErrResponse) {
@ -143,15 +144,14 @@ TEST_P(MockTCPChannelTestAI, FormErrResponse) {
rsp.set_rcode(ns_r_formerr);
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_EFORMERR, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_EFORMERR, result.status_);
}
TEST_P(MockTCPChannelTestAI, ServFailResponse) {
@ -161,16 +161,15 @@ TEST_P(MockTCPChannelTestAI, ServFailResponse) {
rsp.set_rcode(ns_r_servfail);
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_TRUE(result.done_);
// ARES_FLAG_NOCHECKRESP not set, so SERVFAIL consumed
EXPECT_EQ(ARES_ECONNREFUSED, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
}
TEST_P(MockTCPChannelTestAI, NotImplResponse) {
@ -180,16 +179,15 @@ TEST_P(MockTCPChannelTestAI, NotImplResponse) {
rsp.set_rcode(ns_r_notimpl);
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_TRUE(result.done_);
// ARES_FLAG_NOCHECKRESP not set, so NOTIMPL consumed
EXPECT_EQ(ARES_ECONNREFUSED, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
}
TEST_P(MockTCPChannelTestAI, RefusedResponse) {
@ -199,16 +197,15 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) {
rsp.set_rcode(ns_r_refused);
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_TRUE(result.done_);
// ARES_FLAG_NOCHECKRESP not set, so REFUSED consumed
EXPECT_EQ(ARES_ECONNREFUSED, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
}
// TODO: make it work
@ -220,14 +217,13 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) {
// EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
// .WillOnce(SetReply(&server_, &rsp));
//
// AIResult result;
// AddrInfoResult result;
// struct ares_addrinfo hints = {};
// hints.ai_family = AF_INET;
// ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
// ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
// Process();
// EXPECT_TRUE(result.done);
// EXPECT_EQ(ARES_ENODATA, result.status);
// ares_freeaddrinfo(result.airesult);
// EXPECT_TRUE(result.done_);
// EXPECT_EQ(ARES_ENODATA, result.status_);
//}
class MockExtraOptsTestAI
@ -262,17 +258,16 @@ TEST_P(MockExtraOptsTestAI, SimpleQuery) {
.add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_SUCCESS, result.status);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
class MockFlagsChannelOptsTestAI
@ -303,15 +298,14 @@ TEST_P(MockNoCheckRespChannelTestAI, ServFailResponse) {
rsp.set_rcode(ns_r_servfail);
ON_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_ESERVFAIL, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ESERVFAIL, result.status_);
}
TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) {
@ -321,15 +315,14 @@ TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) {
rsp.set_rcode(ns_r_notimpl);
ON_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_ENOTIMP, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ENOTIMP, result.status_);
}
TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) {
@ -339,15 +332,14 @@ TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) {
rsp.set_rcode(ns_r_refused);
ON_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_EREFUSED, result.status);
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_EREFUSED, result.status_);
}
TEST_P(MockChannelTestAI, FamilyV6) {
@ -359,17 +351,15 @@ TEST_P(MockChannelTestAI, FamilyV6) {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03}));
ON_CALL(server_, OnRequest("example.com", ns_t_aaaa))
.WillByDefault(SetReply(&server_, &rsp6));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET6;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AICallback, &result);
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
}
TEST_P(MockChannelTestAI, FamilyV4) {
@ -379,17 +369,15 @@ TEST_P(MockChannelTestAI, FamilyV4) {
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4));
AIResult result = {};
AddrInfoResult result = {};
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AICallback, &result);
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) {
@ -400,18 +388,16 @@ TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) {
.add_answer(new DNSARR("example.com", 100, {7, 8, 9, 0}));
ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4));
AIResult result = {};
AddrInfoResult result = {};
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AICallback, &result);
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(2));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
EXPECT_THAT(result.airesult, IncludesV4Address("7.8.9.0"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(2));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
EXPECT_THAT(result.ai_, IncludesV4Address("7.8.9.0"));
}
TEST_P(MockChannelTestAI, FamilyUnspecified) {
@ -429,18 +415,16 @@ TEST_P(MockChannelTestAI, FamilyUnspecified) {
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AICallback, &result);
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(2));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
EXPECT_THAT(result.airesult, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(2));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
EXPECT_THAT(result.ai_, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
}
class MockEDNSChannelTestAI : public MockFlagsChannelOptsTestAI {
@ -459,16 +443,15 @@ TEST_P(MockEDNSChannelTestAI, RetryWithoutEDNS) {
EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
.WillOnce(SetReply(&server_, &rspfail))
.WillOnce(SetReply(&server_, &rspok));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(ARES_SUCCESS, result.status);
EXPECT_THAT(result.airesult, IncludesV4Address("1.2.3.4"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
}
TEST_P(MockChannelTestAI, SearchDomains) {
@ -489,16 +472,14 @@ TEST_P(MockChannelTestAI, SearchDomains) {
ON_CALL(server_, OnRequest("www.third.gov", ns_t_a))
.WillByDefault(SetReply(&server_, &yesthird));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
@ -512,7 +493,7 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
.add_question(new DNSQuestion("www.first.com", ns_t_a));
ON_CALL(server_, OnRequest("www.first.com", ns_t_a))
.WillByDefault(SetReply(&server_, &nofirst4));
DNSPacket nosecond;
nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain)
.add_question(new DNSQuestion("www.second.org", ns_t_aaaa));
@ -524,7 +505,7 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
.add_answer(new DNSARR("www.second.org", 0x0200, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.second.org", ns_t_a))
.WillByDefault(SetReply(&server_, &yessecond4));
DNSPacket failthird;
failthird.set_response().set_aa().set_rcode(ns_r_servfail)
.add_question(new DNSQuestion("www.third.gov", ns_t_aaaa));
@ -535,17 +516,15 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
.add_question(new DNSQuestion("www.third.gov", ns_t_a));
ON_CALL(server_, OnRequest("www.third.gov", ns_t_a))
.WillByDefault(SetReply(&server_, &failthird4));
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
ares_getaddrinfo(channel_, "www", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
class MockMultiServerChannelTestAI
@ -555,16 +534,15 @@ class MockMultiServerChannelTestAI
MockMultiServerChannelTestAI(bool rotate)
: MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {}
void CheckExample() {
AIResult result;
AddrInfoResult result;
struct ares_addrinfo hints = {};
hints.ai_family = AF_INET;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AICallback, &result);
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done);
EXPECT_EQ(result.status, ARES_SUCCESS);
EXPECT_THAT(result.airesult, IncludesNumAddresses(1));
EXPECT_THAT(result.airesult, IncludesV4Address("2.3.4.5"));
ares_freeaddrinfo(result.airesult);
EXPECT_TRUE(result.done_);
EXPECT_EQ(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
};

@ -565,14 +565,58 @@ void HostCallback(void *data, int status, int timeouts,
if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl;
}
void AICallback(void *data, int status,
struct ares_addrinfo *res) {
std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) {
os << '{';
if (result.done_) {
os << StatusToString(result.status_) << " " << result.ai_;
} else {
os << "(incomplete)";
}
os << '}';
return os;
}
std::ostream& operator<<(std::ostream& os, const AddrInfo& ai) {
os << '{';
struct ares_addrinfo *next = ai.get();
while(next) {
if(next->ai_canonname) {
os << "'" << next->ai_canonname << "' ";
}
unsigned short port = 0;
os << "addr=[";
if(next->ai_family == AF_INET) {
sockaddr_in* sin = (sockaddr_in*)next->ai_addr;
port = ntohs(sin->sin_port);
os << AddressToString(&sin->sin_addr, 4);
}
else if (next->ai_family == AF_INET6) {
sockaddr_in6* sin = (sockaddr_in6*)next->ai_addr;
port = ntohs(sin->sin6_port);
os << "[" << AddressToString(&sin->sin6_addr, 16) << "]";
}
else
os << "unknown family";
if(port) {
os << ":" << port;
}
os << "]";
if((next = next->ai_next))
os << ", ";
}
os << '}';
return os;
}
void AddrInfoCallback(void *data, int status, int timeouts,
struct ares_addrinfo *ai) {
EXPECT_NE(nullptr, data);
AIResult* result = reinterpret_cast<AIResult*>(data);
result->done = true;
result->status = status;
result->airesult = res;
//if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl;
AddrInfoResult* result = reinterpret_cast<AddrInfoResult*>(data);
result->done_ = true;
result->status_ = status;
result->timeouts_= timeouts;
result->ai_ = AddrInfo(ai);
if (verbose) std::cerr << "AddrInfoCallback(" << *result << ")" << std::endl;
}
std::ostream& operator<<(std::ostream& os, const SearchResult& result) {

@ -279,6 +279,30 @@ struct NameInfoResult {
};
std::ostream& operator<<(std::ostream& os, const NameInfoResult& result);
struct AddrInfoDeleter {
void operator() (ares_addrinfo *ptr) {
if (ptr) ares_freeaddrinfo(ptr);
}
};
// C++ wrapper for struct ares_addrinfo.
using AddrInfo = std::unique_ptr<ares_addrinfo, AddrInfoDeleter>;
std::ostream& operator<<(std::ostream& os, const AddrInfo& result);
// Structure that describes the result of an ares_addrinfo_callback invocation.
struct AddrInfoResult {
AddrInfoResult() : done_(false), status_(-1), timeouts_(0) {}
// Whether the callback has been invoked.
bool done_;
// Explicitly provided result information.
int status_;
int timeouts_;
// Contents of the ares_addrinfo structure, if provided.
AddrInfo ai_;
};
std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result);
// Standard implementation of ares callbacks that fill out the corresponding
// structures.
void HostCallback(void *data, int status, int timeouts,
@ -287,8 +311,8 @@ void SearchCallback(void *data, int status, int timeouts,
unsigned char *abuf, int alen);
void NameInfoCallback(void *data, int status, int timeouts,
char *node, char *service);
void AICallback(void *data, int status,
struct ares_addrinfo *res);
void AddrInfoCallback(void *data, int status, int timeouts,
struct ares_addrinfo *res);
// Retrieve the name servers used by a channel.
std::vector<std::string> GetNameServers(ares_channel channel);

Loading…
Cancel
Save