SHA256
1
0
forked from pool/c-ares
c-ares/0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch

1473 lines
50 KiB
Diff
Raw Normal View History

From b565626751ea6bb69fbe1642c89c8b634b064911 Mon Sep 17 00:00:00 2001
From: Andrew Selivanov <andrew.selivanov@gmail.com>
Date: Wed, 23 Jan 2019 17:09:33 +0300
Subject: [PATCH 04/10] Add ares__sortaddrinfo() to support getaddrinfo()
sorted results (#239)
This is a port of RFC 6724 compliant sorting function from Android Bionic project:
https://android.googlesource.com/platform/bionic/+/e919b116d35aa7deb24ddece69c491e24c3b0d6f/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)
---
Makefile.inc | 1 +
ares.h | 1 +
ares__sortaddrinfo.c | 495 ++++++++++++++++++++++++++++++++++++++++++++++
ares_getaddrinfo.3 | 18 +-
ares_getaddrinfo.c | 14 +-
ares_ipv6.h | 7 +
ares_private.h | 1 +
test/ares-test-ai.h | 11 --
test/ares-test-live-ai.cc | 47 +++--
test/ares-test-mock-ai.cc | 286 +++++++++++++--------------
test/ares-test.cc | 58 +++++-
test/ares-test.h | 28 ++-
12 files changed, 759 insertions(+), 208 deletions(-)
create mode 100644 ares__sortaddrinfo.c
diff --git a/Makefile.inc b/Makefile.inc
index 381cc75..de165cf 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -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 \
diff --git a/ares.h b/ares.h
index 99e3e0b..af82141 100644
--- a/ares.h
+++ b/ares.h
@@ -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);
diff --git a/ares__sortaddrinfo.c b/ares__sortaddrinfo.c
new file mode 100644
index 0000000..4d3c8d6
--- /dev/null
+++ b/ares__sortaddrinfo.c
@@ -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;
+}
diff --git a/ares_getaddrinfo.3 b/ares_getaddrinfo.3
index 42a43fc..0089227 100644
--- a/ares_getaddrinfo.3
+++ b/ares_getaddrinfo.3
@@ -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
diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c
index b89a29c..ebaeda8 100644
--- a/ares_getaddrinfo.c
+++ b/ares_getaddrinfo.c
@@ -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);
}
diff --git a/ares_ipv6.h b/ares_ipv6.h
index b0017f1..fdbc21f 100644
--- a/ares_ipv6.h
+++ b/ares_ipv6.h
@@ -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
{
diff --git a/ares_private.h b/ares_private.h
index 8e16256..dcbdf0e 100644
--- a/ares_private.h
+++ b/ares_private.h
@@ -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);
diff --git a/test/ares-test-ai.h b/test/ares-test-ai.h
index d558489..7cf27e3 100644
--- a/test/ares-test-ai.h
+++ b/test/ares-test-ai.h
@@ -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;
-};
-
}
}
diff --git a/test/ares-test-live-ai.cc b/test/ares-test-live-ai.cc
index 96260fb..ab93587 100644
--- a/test/ares-test-live-ai.cc
+++ b/test/ares-test-live-ai.cc
@@ -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
diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc
index a67f811..c293102 100644
--- a/test/ares-test-mock-ai.cc
+++ b/test/ares-test-mock-ai.cc
@@ -16,17 +16,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 (!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 ==
@@ -37,11 +41,13 @@ MATCHER_P(IncludesV4Address, address, "") {
}
MATCHER_P(IncludesV6Address, address, "") {
+ if(!arg)
+ return false;
in6_addr addressnum = {};
if (!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(
@@ -69,31 +75,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
@@ -109,16 +112,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
@@ -127,14 +129,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) {
@@ -144,15 +145,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) {
@@ -162,16 +162,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) {
@@ -181,16 +180,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) {
@@ -200,16 +198,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
@@ -221,14 +218,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
@@ -263,17 +259,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
@@ -304,15 +299,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) {
@@ -322,15 +316,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) {
@@ -340,15 +333,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) {
@@ -360,17 +352,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) {
@@ -380,17 +370,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) {
@@ -401,18 +389,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) {
@@ -430,18 +416,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 {
@@ -460,16 +444,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) {
@@ -490,16 +473,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) {
@@ -513,7 +494,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));
@@ -525,7 +506,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));
@@ -536,17 +517,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
@@ -556,16 +535,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"));
}
};
diff --git a/test/ares-test.cc b/test/ares-test.cc
index 7776548..1128e99 100644
--- a/test/ares-test.cc
+++ b/test/ares-test.cc
@@ -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) {
diff --git a/test/ares-test.h b/test/ares-test.h
index 03e15ec..ae675aa 100644
--- a/test/ares-test.h
+++ b/test/ares-test.h
@@ -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);
--
2.16.4