From 42b6c9750c6d94fa212955b2f1df4447afd378d9449642bd98153adc7274c48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Fri, 8 Nov 2019 14:58:09 +0000 Subject: [PATCH] Accepting request 746633 from home:adamm:node_test Previous set of patches broke NodeJS 12.x unit tests. With the complete upstream snapshot, the tests pass as the regressions are fixed. - Update to upstream snapshot 20191108 * getaddrinfo - avoid infinite loop in case of NXDOMAIN * ares_getenv - return NULL in all cases * implement ares_getaddrinfo - onion-crash.patch: removed, upstreamed. - removed upstream patches that are part of the snapshot: 0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch 0002-Remaining-queries-counter-fix-additional-unit-tests-.patch 0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch 0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch 0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch 0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch 0007-getaddrinfo-enhancements-257.patch 0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch 0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch 0010-Disable-failing-test.patch - disable-live-tests.patch - updated OBS-URL: https://build.opensuse.org/request/show/746633 OBS-URL: https://build.opensuse.org/package/show/devel:libraries:c_c++/c-ares?expand=0&rev=11 --- ...lementation-for-ares_getaddrinfo-112.patch | 1189 ------ ...s-counter-fix-additional-unit-tests-.patch | 280 -- ...getaddrinfo-and-additional-unit-test.patch | 781 ---- ...drinfo-to-support-getaddrinfo-sorted.patch | 1472 ------- ...d-infinite-loop-in-case-of-NXDOMAIN-.patch | 55 - ...back-must-be-called-on-bad-domain-24.patch | 37 - 0007-getaddrinfo-enhancements-257.patch | 3591 ----------------- ...ts.h-include-from-ares_getaddrinfo.c.patch | 35 - ...tability-of-ares-test-mock-ai.cc-235.patch | 70 - 0010-Disable-failing-test.patch | 36 - c-ares-1.15.0-20191108.tar.gz | 3 + c-ares-1.15.0.tar.gz | 3 - c-ares-1.15.0.tar.gz.asc | 11 - c-ares.changes | 22 + c-ares.spec | 31 +- disable-live-tests.patch | 17 +- onion-crash.patch | 35 - 17 files changed, 41 insertions(+), 7627 deletions(-) delete mode 100644 0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch delete mode 100644 0002-Remaining-queries-counter-fix-additional-unit-tests-.patch delete mode 100644 0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch delete mode 100644 0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch delete mode 100644 0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch delete mode 100644 0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch delete mode 100644 0007-getaddrinfo-enhancements-257.patch delete mode 100644 0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch delete mode 100644 0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch delete mode 100644 0010-Disable-failing-test.patch create mode 100644 c-ares-1.15.0-20191108.tar.gz delete mode 100644 c-ares-1.15.0.tar.gz delete mode 100644 c-ares-1.15.0.tar.gz.asc delete mode 100644 onion-crash.patch diff --git a/0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch b/0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch deleted file mode 100644 index 67add7b..0000000 --- a/0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch +++ /dev/null @@ -1,1189 +0,0 @@ -From 63ceb27adae045aa7e137663253d98060b5f05be Mon Sep 17 00:00:00 2001 -From: Christian Ammer -Date: Tue, 6 Nov 2018 14:47:05 +0100 -Subject: [PATCH 01/10] Add initial implementation for ares_getaddrinfo (#112) - -Initial implementation for ares_getaddrinfo(). It is NOT compliant with RFC6724, though -it is expected to come closer to conformance prior to the next release. - -Features not supported include sorted addresses and honoring of service and hints -parameters. - -Implementation by: Christian Ammer (@ChristianAmmer) ---- - Makefile.inc | 12 +- - ares.h | 22 +++ - ares_freeaddrinfo.3 | 35 ++++ - ares_getaddrinfo.3 | 114 +++++++++++ - ares_getaddrinfo.c | 465 +++++++++++++++++++++++++++++++++++++++++++++ - ares_parse_a_reply.c | 38 ++++ - ares_private.h | 4 + - ares_search.c | 12 +- - test/Makefile.inc | 5 +- - test/ares-test-ai.cc | 0 - test/ares-test-ai.h | 32 ++++ - test/ares-test-internal.cc | 8 +- - test/ares-test-mock-ai.cc | 154 +++++++++++++++ - test/ares-test.cc | 11 ++ - test/ares-test.h | 2 + - 15 files changed, 898 insertions(+), 16 deletions(-) - create mode 100644 ares_freeaddrinfo.3 - create mode 100644 ares_getaddrinfo.3 - create mode 100644 ares_getaddrinfo.c - create mode 100644 test/ares-test-ai.cc - create mode 100644 test/ares-test-ai.h - create mode 100644 test/ares-test-mock-ai.cc - -diff --git a/Makefile.inc b/Makefile.inc -index 30e0046..381cc75 100644 ---- a/Makefile.inc -+++ b/Makefile.inc -@@ -48,7 +48,8 @@ CSOURCES = ares__close_sockets.c \ - bitncmp.c \ - inet_net_pton.c \ - inet_ntop.c \ -- windows_port.c -+ windows_port.c \ -+ ares_getaddrinfo.c - - HHEADERS = ares.h \ - ares_android.h \ -@@ -129,7 +130,8 @@ MANPAGES = ares_cancel.3 \ - ares_set_sortlist.3 \ - ares_strerror.3 \ - ares_timeout.3 \ -- ares_version.3 -+ ares_version.3 \ -+ ares_getaddrinfo.3 - - HTMLPAGES = ares_cancel.html \ - ares_create_query.html \ -@@ -184,7 +186,8 @@ HTMLPAGES = ares_cancel.html \ - ares_set_sortlist.html \ - ares_strerror.html \ - ares_timeout.html \ -- ares_version.html -+ ares_version.html \ -+ ares_getaddrinfo.html - - PDFPAGES = ares_cancel.pdf \ - ares_create_query.pdf \ -@@ -239,7 +242,8 @@ PDFPAGES = ares_cancel.pdf \ - ares_set_sortlist.pdf \ - ares_strerror.pdf \ - ares_timeout.pdf \ -- ares_version.pdf -+ ares_version.pdf \ -+ ares_getaddrinfo.pdf - - SAMPLESOURCES = ares_getopt.c \ - ares_nowarn.c \ -diff --git a/ares.h b/ares.h -index 06f60b3..99e3e0b 100644 ---- a/ares.h -+++ b/ares.h -@@ -278,6 +278,7 @@ struct hostent; - struct timeval; - struct sockaddr; - struct ares_channeldata; -+struct ares_addrinfo; - - typedef struct ares_channeldata *ares_channel; - -@@ -306,6 +307,10 @@ typedef int (*ares_sock_config_callback)(ares_socket_t socket_fd, - int type, - void *data); - -+typedef void (*ares_addr_callback)(void *arg, -+ int status, -+ struct ares_addrinfo *res); -+ - CARES_EXTERN int ares_library_init(int flags); - - CARES_EXTERN int ares_library_init_mem(int flags, -@@ -369,6 +374,12 @@ CARES_EXTERN void ares_set_socket_configure_callback(ares_channel channel, - CARES_EXTERN int ares_set_sortlist(ares_channel channel, - const char *sortstr); - -+CARES_EXTERN void ares_getaddrinfo(ares_channel channel, -+ const char* node, const char* service, -+ const struct ares_addrinfo* hints, -+ ares_addr_callback callback, void* arg); -+CARES_EXTERN void ares_freeaddrinfo(struct ares_addrinfo* ai); -+ - /* - * Virtual function set to have user-managed socket IO. - * Note that all functions need to be defined, and when -@@ -558,6 +569,17 @@ struct ares_soa_reply { - unsigned int minttl; - }; - -+struct ares_addrinfo { -+ int ai_flags; -+ int ai_family; -+ int ai_socktype; -+ int ai_protocol; -+ ares_socklen_t ai_addrlen; -+ char *ai_canonname; -+ struct sockaddr *ai_addr; -+ struct ares_addrinfo *ai_next; -+}; -+ - /* - ** Parse the buffer, starting at *abuf and of length alen bytes, previously - ** obtained from an ares_search call. Put the results in *host, if nonnull. -diff --git a/ares_freeaddrinfo.3 b/ares_freeaddrinfo.3 -new file mode 100644 -index 0000000..8143299 ---- /dev/null -+++ b/ares_freeaddrinfo.3 -@@ -0,0 +1,35 @@ -+.\" -+.\" Copyright 1998 by the Massachusetts Institute of Technology. -+.\" -+.\" Permission to use, copy, modify, and distribute this -+.\" software and its documentation for any purpose and without -+.\" fee is hereby granted, provided that the above copyright -+.\" notice appear in all copies and that both that copyright -+.\" notice and this permission notice appear in supporting -+.\" documentation, and that the name of M.I.T. not be used in -+.\" advertising or publicity pertaining to distribution of the -+.\" software without specific, written prior permission. -+.\" M.I.T. makes no representations about the suitability of -+.\" this software for any purpose. It is provided "as is" -+.\" without express or implied warranty. -+.\" -+.TH ARES_FREEADDRINFO 3 "31 October 2018" -+.SH NAME -+ares_freeaddrinfo \- Free addrinfo structure allocated by ares functions -+.SH SYNOPSIS -+.nf -+.B #include -+.PP -+.B void ares_freeaddrinfo(struct addrinfo *\fIai\fP) -+.fi -+.SH DESCRIPTION -+The -+.B ares_freeaddrinfo -+function frees a -+.B struct addrinfo -+returned in \fIresult\fP of -+.B ares_addr_callback -+.SH SEE ALSO -+.BR ares_getaddrinfo (3), -+.SH AUTHOR -+Christian Ammer -diff --git a/ares_getaddrinfo.3 b/ares_getaddrinfo.3 -new file mode 100644 -index 0000000..42a43fc ---- /dev/null -+++ b/ares_getaddrinfo.3 -@@ -0,0 +1,114 @@ -+.\" -+.\" Copyright 1998 by the Massachusetts Institute of Technology. -+.\" -+.\" Permission to use, copy, modify, and distribute this -+.\" software and its documentation for any purpose and without -+.\" fee is hereby granted, provided that the above copyright -+.\" notice appear in all copies and that both that copyright -+.\" notice and this permission notice appear in supporting -+.\" documentation, and that the name of M.I.T. not be used in -+.\" advertising or publicity pertaining to distribution of the -+.\" software without specific, written prior permission. -+.\" M.I.T. makes no representations about the suitability of -+.\" this software for any purpose. It is provided "as is" -+.\" without express or implied warranty. -+.\" -+.TH ARES_GETADDRINFO 3 "4 November 2018" -+.SH NAME -+ares_getaddrinfo \- Initiate a host query by name -+.SH SYNOPSIS -+.nf -+.B #include -+.PP -+.B typedef void (*ares_addr_callback)(void *\fIarg\fP, int \fIstatus\fP, -+.B 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, -+.B ares_addr_callback \fIcallback\fP, void *\fIarg\fP) -+.PP -+.B struct ares_addrinfo { -+.B int \fIai_flags\fP; -+.B int \fIai_family\fP; -+.B int \fIai_socktype\fP; -+.B int \fIai_protocol\fP; -+.B ares_socklen_t \fIai_addrlen\fP; -+.B char \fI*ai_canonname\fP; -+.B struct sockaddr \fI*ai_addr\fP; -+.B struct ares_addrinfo \fI*ai_next\fP; -+.B }; -+.fi -+.SH DESCRIPTION -+The -+.B ares_getaddrinfo -+function initiates a host query by name on the name service channel -+identified by -+.IR channel . -+The parameter -+.I name -+gives the hostname as a NUL-terminated C string, and -+.I hints->ai_family -+gives the desired type of address for the resulting addrinfo result list. -+The parameter -+.I service -+and the other properties from the -+.I hints -+parameter are ignored. When the -+query is complete or has failed, the ares library will invoke \fIcallback\fP. -+Completion or failure of the query may happen immediately, or may happen -+during a later call to \fIares_process(3)\fP, \fIares_destroy(3)\fP or -+\fIares_cancel(3)\fP. -+.PP -+The callback argument -+.I arg -+is copied from the -+.B ares_getaddrinfo -+argument -+.IR arg . -+The callback argument -+.I status -+indicates whether the query succeeded and, if not, how it failed. It -+may have any of the following values: -+.TP 19 -+.B ARES_SUCCESS -+The host lookup completed successfully. -+.TP 19 -+.B ARES_ENOTIMP -+The ares library does not know how to find addresses of type -+.IR family . -+.TP 19 -+.B ARES_ENOTFOUND -+The name -+.I name -+was not found. -+.TP 19 -+.B ARES_ENOMEM -+Memory was exhausted. -+.TP 19 -+.B ARES_ECANCELLED -+The query was cancelled. -+.TP 19 -+.B ARES_EDESTRUCTION -+The name service channel -+.I channel -+is being destroyed; the query will not be completed. -+.PP -+On successful completion of the query, the callback argument -+.I result -+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 -+.B ares_freeaddrinfo. -+.SH SEE ALSO -+.BR ares_freeaddrinfo (3) -+.SH AUTHOR -+Christian Ammer -+.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. -+.br -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -new file mode 100644 -index 0000000..be936ff ---- /dev/null -+++ b/ares_getaddrinfo.c -@@ -0,0 +1,465 @@ -+ -+/* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology. -+ * Copyright (C) 2017 - 2018 by Christian Ammer -+ * -+ * Permission to use, copy, modify, and distribute this -+ * software and its documentation for any purpose and without -+ * fee is hereby granted, provided that the above copyright -+ * notice appear in all copies and that both that copyright -+ * notice and this permission notice appear in supporting -+ * documentation, and that the name of M.I.T. not be used in -+ * advertising or publicity pertaining to distribution of the -+ * software without specific, written prior permission. -+ * M.I.T. makes no representations about the suitability of -+ * this software for any purpose. It is provided "as is" -+ * without express or implied warranty. -+ */ -+ -+#include "ares_setup.h" -+ -+#ifdef HAVE_NETINET_IN_H -+# include -+#endif -+#ifdef HAVE_NETDB_H -+# include -+#endif -+#ifdef HAVE_ARPA_INET_H -+# include -+#endif -+#ifdef HAVE_ARPA_NAMESER_H -+# include -+#else -+# include "nameser.h" -+#endif -+#ifdef HAVE_ARPA_NAMESER_COMPAT_H -+# include -+#endif -+ -+#ifdef HAVE_STRINGS_H -+#include -+#endif -+#include -+ -+#include "ares.h" -+#include "bitncmp.h" -+#include "ares_private.h" -+ -+#ifdef WATT32 -+#undef WIN32 -+#endif -+#ifdef WIN32 -+# include "ares_platform.h" -+#endif -+ -+struct host_query { -+ /* Arguments passed to ares_getaddrinfo */ -+ ares_channel channel; -+ char *name; -+ ares_addr_callback callback; -+ void *arg; -+ int sent_family; /* this family is what was is being used */ -+ int ai_family; /* this family is what is asked for in the API */ -+ int timeouts; /* number of timeouts we saw for this request */ -+ int next_domain; /* next search domain to try */ -+ int single_domain; /* do not check other domains */ -+ int status; -+ int remaining; -+ struct ares_addrinfo* ai; -+}; -+ -+static void host_callback(void *arg, int status, int timeouts, -+ unsigned char *abuf, int alen); -+static void end_hquery(struct host_query *hquery, int status); -+static int file_lookup(const char *name, int family, -+ struct ares_addrinfo **ai); -+static void sort_addresses(struct hostent *host, -+ const struct apattern *sortlist, int nsort); -+static void sort6_addresses(struct hostent *host, -+ const struct apattern *sortlist, int nsort); -+static int get_address_index(const struct in_addr *addr, -+ const struct apattern *sortlist, int nsort); -+static int get6_address_index(const struct ares_in6_addr *addr, -+ const struct apattern *sortlist, int nsort); -+static int as_is_first(const struct host_query *hquery); -+static void add_to_addrinfo(struct ares_addrinfo** ai, -+ const struct hostent* host); -+static void next_dns_lookup(struct host_query *hquery); -+static int is_implemented(const int family); -+ -+ -+void ares_getaddrinfo(ares_channel channel, -+ const char* node, const char* service, -+ const struct ares_addrinfo* hints, -+ ares_addr_callback callback, void* arg) { -+ 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); -+ return; -+ } -+ -+ /* Allocate and fill in the host query structure. */ -+ hquery = ares_malloc(sizeof(struct host_query)); -+ if (!hquery) { -+ callback(arg, ARES_ENOMEM, NULL); -+ return; -+ } -+ hquery->ai = NULL; -+ hquery->channel = channel; -+ hquery->name = single != NULL ? single : ares_strdup(node); -+ hquery->single_domain = single != NULL; -+ hquery->ai_family = ai_family; -+ hquery->sent_family = -1; /* nothing is sent yet */ -+ if (!hquery->name) { -+ ares_free(hquery); -+ callback(arg, ARES_ENOMEM, NULL); -+ return; -+ } -+ hquery->callback = callback; -+ hquery->arg = arg; -+ hquery->timeouts = 0; -+ hquery->next_domain = 0; -+ hquery->remaining = ai_family == AF_UNSPEC ? 2 : 1; -+ -+ /* Host file lookup */ -+ if (file_lookup(hquery->name, ai_family, &hquery->ai) == ARES_SUCCESS) { -+ end_hquery(hquery, ARES_SUCCESS); -+ } -+ else { -+ next_dns_lookup(hquery); -+ } -+} -+ -+void ares_freeaddrinfo(struct ares_addrinfo* ai) { -+ struct ares_addrinfo* ai_free; -+ while (ai) { -+ ai_free = ai; -+ ai = ai->ai_next; -+ ares_free(ai_free->ai_addr); -+ ares_free(ai_free); -+ } -+} -+ -+static int is_implemented(const int family) { -+ return -+ family == AF_INET || -+ family == AF_INET6 || -+ family == AF_UNSPEC; -+} -+ -+static int file_lookup(const char *name, int family, struct ares_addrinfo **ai) { -+ FILE *fp; -+ char **alias; -+ int status; -+ int error; -+ struct hostent *host = NULL; -+ -+#ifdef WIN32 -+ char PATH_HOSTS[MAX_PATH]; -+ win_platform platform; -+ -+ PATH_HOSTS[0] = '\0'; -+ -+ platform = ares__getplatform(); -+ -+ if (platform == WIN_NT) { -+ char tmp[MAX_PATH]; -+ HKEY hkeyHosts; -+ -+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, -+ &hkeyHosts) == ERROR_SUCCESS) { -+ DWORD dwLength = MAX_PATH; -+ RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, -+ &dwLength); -+ ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); -+ RegCloseKey(hkeyHosts); -+ } -+ } -+ else if (platform == WIN_9X) { -+ GetWindowsDirectory(PATH_HOSTS, MAX_PATH); -+ } -+ else { -+ return ARES_ENOTFOUND; -+ } -+ -+ strcat(PATH_HOSTS, WIN_PATH_HOSTS); -+ -+#elif defined(WATT32) -+ extern const char *_w32_GetHostsFile (void); -+ const char *PATH_HOSTS = _w32_GetHostsFile(); -+ -+ if (!PATH_HOSTS) { -+ return ARES_ENOTFOUND; -+ } -+#endif -+ -+ fp = fopen(PATH_HOSTS, "r"); -+ if (!fp) { -+ error = ERRNO; -+ switch(error) { -+ case ENOENT: -+ case ESRCH: -+ return ARES_ENOTFOUND; -+ default: -+ DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", -+ error, strerror(error))); -+ DEBUGF(fprintf(stderr, "Error opening file: %s\n", -+ PATH_HOSTS)); -+ host = NULL; -+ return ARES_EFILE; -+ } -+ } -+ status = ARES_ENOTFOUND; -+ while (ares__get_hostent(fp, family, &host) == ARES_SUCCESS) { -+ if (strcasecmp(host->h_name, name) == 0) { -+ add_to_addrinfo(ai, host); -+ status = ARES_SUCCESS; -+ } -+ else { -+ for (alias = host->h_aliases; *alias; alias++) { -+ if (strcasecmp(*alias, name) == 0) { -+ add_to_addrinfo(ai, host); -+ status = ARES_SUCCESS; -+ break; -+ } -+ } -+ } -+ ares_free_hostent(host); -+ } -+ fclose(fp); -+ return status; -+} -+ -+static void add_to_addrinfo(struct ares_addrinfo** ai, -+ const struct hostent* host) { -+ static const struct ares_addrinfo EmptyAddrinfo; -+ struct ares_addrinfo* next_ai; -+ char** p; -+ if (!host || (host->h_addrtype != AF_INET && host->h_addrtype != AF_INET6)) { -+ return; -+ } -+ for (p = host->h_addr_list; *p; ++p) { -+ next_ai = ares_malloc(sizeof(struct ares_addrinfo)); -+ *next_ai = EmptyAddrinfo; -+ if (*ai) { -+ (*ai)->ai_next = next_ai; -+ } -+ else { -+ *ai = next_ai; -+ } -+ if (host->h_addrtype == AF_INET) { -+ next_ai->ai_protocol = IPPROTO_UDP; -+ next_ai->ai_family = AF_INET; -+ next_ai->ai_addr = ares_malloc(sizeof(struct sockaddr_in)); -+ memcpy(&((struct sockaddr_in*)(next_ai->ai_addr))->sin_addr, *p, -+ host->h_length); -+ } -+ else { -+ next_ai->ai_protocol = IPPROTO_UDP; -+ next_ai->ai_family = AF_INET6; -+ next_ai->ai_addr = ares_malloc(sizeof(struct sockaddr_in6)); -+ memcpy(&((struct sockaddr_in6*)(next_ai->ai_addr))->sin6_addr, *p, -+ host->h_length); -+ } -+ } -+} -+ -+static void next_dns_lookup(struct host_query *hquery) { -+ char *s = NULL; -+ int is_s_allocated = 0; -+ int status; -+ -+ if (( as_is_first(hquery) && hquery->next_domain == 0) || -+ (!as_is_first(hquery) && hquery->next_domain == -+ hquery->channel->ndomains)) { -+ s = hquery->name; -+ } -+ -+ if (!s && hquery->next_domain < hquery->channel->ndomains) { -+ status = ares__cat_domain( -+ hquery->name, -+ hquery->channel->domains[hquery->next_domain++], -+ &s); -+ if (status == ARES_SUCCESS) { -+ is_s_allocated = 1; -+ } -+ } -+ -+ if (s) { -+ if (hquery->ai_family == AF_INET || hquery->ai_family == AF_UNSPEC) { -+ ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); -+ } -+ if (hquery->ai_family == AF_INET6 || hquery->ai_family == AF_UNSPEC) { -+ ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); -+ } -+ if (is_s_allocated) { -+ ares_free(s); -+ } -+ } -+ else { -+ assert(!hquery->ai); -+ end_hquery(hquery, ARES_ENOTFOUND); -+ } -+} -+ -+static void end_hquery(struct host_query *hquery, int status) { -+ if (hquery->ai) { -+ hquery->callback(hquery->arg, status, hquery->ai); -+ } -+ ares_free(hquery->name); -+ ares_free(hquery); -+} -+ -+static void host_callback(void *arg, int status, int timeouts, -+ unsigned char *abuf, int alen) { -+ struct host_query *hquery = (struct host_query*)arg; -+ ares_channel channel = hquery->channel; -+ struct hostent *host = NULL; -+ int qtype; -+ hquery->timeouts += timeouts; -+ -+ if (status == ARES_SUCCESS) { -+ status = ares__parse_qtype_reply(abuf, alen, &qtype); -+ if (status == ARES_SUCCESS && qtype == T_A) { -+ /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ -+ ares_parse_a_reply(abuf, alen, &host, NULL, NULL); -+ if (host && channel->nsort) { -+ sort_addresses(host, channel->sortlist, channel->nsort); -+ } -+ add_to_addrinfo(&hquery->ai, host); -+ ares_free_hostent(host); -+ if (!--hquery->remaining) { -+ end_hquery(hquery, ARES_SUCCESS); -+ } -+ } -+ else if (status == ARES_SUCCESS && qtype == T_AAAA) { -+ /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ -+ ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); -+ if (host && channel->nsort) { -+ sort6_addresses(host, channel->sortlist, channel->nsort); -+ } -+ add_to_addrinfo(&hquery->ai, host); -+ ares_free_hostent(host); -+ if (!--hquery->remaining) { -+ end_hquery(hquery, ARES_SUCCESS); -+ } -+ } -+ else { -+ assert(!hquery->ai); -+ end_hquery(hquery, status); -+ } -+ } -+ else { -+ next_dns_lookup(hquery); -+ } -+} -+ -+static void sort_addresses(struct hostent *host, -+ const struct apattern *sortlist, int nsort) { -+ struct in_addr a1, a2; -+ int i1, i2, ind1, ind2; -+ -+ /* This is a simple insertion sort, not optimized at all. i1 walks -+ * through the address list, with the loop invariant that everything -+ * to the left of i1 is sorted. In the loop body, the value at i1 is moved -+ * back through the list (via i2) until it is in sorted order. -+ */ -+ for (i1 = 0; host->h_addr_list[i1]; i1++) { -+ memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr)); -+ ind1 = get_address_index(&a1, sortlist, nsort); -+ for (i2 = i1 - 1; i2 >= 0; i2--) { -+ memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr)); -+ ind2 = get_address_index(&a2, sortlist, nsort); -+ if (ind2 <= ind1) { -+ break; -+ } -+ memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr)); -+ } -+ memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr)); -+ } -+} -+ -+/* Find the first entry in sortlist which matches addr. Return nsort -+ * if none of them match. -+ */ -+static int get_address_index(const struct in_addr *addr, -+ const struct apattern *sortlist, -+ int nsort) { -+ int i; -+ -+ for (i = 0; i < nsort; i++) { -+ if (sortlist[i].family != AF_INET) { -+ continue; -+ } -+ if (sortlist[i].type == PATTERN_MASK) { -+ if ((addr->s_addr & sortlist[i].mask.addr4.s_addr) == -+ sortlist[i].addrV4.s_addr) { -+ break; -+ } -+ } -+ else { -+ if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr, -+ sortlist[i].mask.bits)) { -+ break; -+ } -+ } -+ } -+ return i; -+} -+ -+static void sort6_addresses(struct hostent *host, -+ const struct apattern *sortlist, int nsort) { -+ struct ares_in6_addr a1, a2; -+ int i1, i2, ind1, ind2; -+ -+ /* This is a simple insertion sort, not optimized at all. i1 walks -+ * through the address list, with the loop invariant that everything -+ * to the left of i1 is sorted. In the loop body, the value at i1 is moved -+ * back through the list (via i2) until it is in sorted order. -+ */ -+ for (i1 = 0; host->h_addr_list[i1]; i1++) { -+ memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr)); -+ ind1 = get6_address_index(&a1, sortlist, nsort); -+ for (i2 = i1 - 1; i2 >= 0; i2--) { -+ memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr)); -+ ind2 = get6_address_index(&a2, sortlist, nsort); -+ if (ind2 <= ind1) { -+ break; -+ } -+ memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr)); -+ } -+ memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr)); -+ } -+} -+ -+/* Find the first entry in sortlist which matches addr. Return nsort -+ * if none of them match. -+ */ -+static int get6_address_index(const struct ares_in6_addr *addr, -+ const struct apattern *sortlist, -+ int nsort) { -+ int i; -+ -+ for (i = 0; i < nsort; i++) { -+ if (sortlist[i].family != AF_INET6) -+ continue; -+ if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits)) -+ break; -+ } -+ return i; -+} -+ -+static int as_is_first(const struct host_query* hquery) { -+ char* p; -+ int ndots = 0; -+ for (p = hquery->name; *p; p++) { -+ if (*p == '.') { -+ ndots++; -+ } -+ } -+ return ndots >= hquery->channel->ndots; -+} -+ -diff --git a/ares_parse_a_reply.c b/ares_parse_a_reply.c -index 0422bd3..4fb6d14 100644 ---- a/ares_parse_a_reply.c -+++ b/ares_parse_a_reply.c -@@ -262,3 +262,41 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, - ares_free(hostname); - return status; - } -+ -+/* returned size includes terminating 0. */ -+static long encoded_name_size(const unsigned char *begin, const unsigned char *end) -+{ -+ const unsigned char* it = begin; -+ for (; *it && it != end; ++it); -+ return it == end ? -1 : (long)((it + 1) - begin); -+} -+ -+int ares__parse_qtype_reply(const unsigned char* abuf, int alen, int* qtype) -+{ -+ unsigned int qdcount, ancount; -+ const unsigned char* aptr; -+ long len; -+ -+ /* Give up if abuf doesn't have room for a header. */ -+ if (alen < HFIXEDSZ) -+ return ARES_EBADRESP; -+ -+ /* Fetch the question and answer count from the header. */ -+ qdcount = DNS_HEADER_QDCOUNT(abuf); -+ ancount = DNS_HEADER_ANCOUNT(abuf); -+ if (qdcount != 1) -+ return ARES_EBADRESP; -+ -+ /* Expand the name from the question, and skip past the question. */ -+ aptr = abuf + HFIXEDSZ; -+ len = encoded_name_size(aptr, abuf + alen); -+ if (len == -1) -+ return ARES_EBADRESP; -+ if (aptr + len + QFIXEDSZ > abuf + alen) -+ return ARES_EBADRESP; -+ aptr += len; -+ if (!ancount) -+ return ARES_ENODATA; -+ *qtype = DNS__16BIT(aptr); -+ return ARES_SUCCESS; -+} -diff --git a/ares_private.h b/ares_private.h -index 1990f69..8e16256 100644 ---- a/ares_private.h -+++ b/ares_private.h -@@ -355,6 +355,10 @@ int ares__expand_name_for_response(const unsigned char *encoded, - char **s, long *enclen); - void ares__init_servers_state(ares_channel channel); - 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); -+ - #if 0 /* Not used */ - long ares__tvdiff(struct timeval t1, struct timeval t2); - #endif -diff --git a/ares_search.c b/ares_search.c -index 001c348..c4b0424 100644 ---- a/ares_search.c -+++ b/ares_search.c -@@ -43,8 +43,6 @@ static void search_callback(void *arg, int status, int timeouts, - unsigned char *abuf, int alen); - static void end_squery(struct search_query *squery, int status, - unsigned char *abuf, int alen); --static int cat_domain(const char *name, const char *domain, char **s); --STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s); - - void ares_search(ares_channel channel, const char *name, int dnsclass, - int type, ares_callback callback, void *arg) -@@ -64,7 +62,7 @@ void ares_search(ares_channel channel, const char *name, int dnsclass, - /* If name only yields one domain to search, then we don't have - * to keep extra state, so just do an ares_query(). - */ -- status = single_domain(channel, name, &s); -+ status = ares__single_domain(channel, name, &s); - if (status != ARES_SUCCESS) - { - callback(arg, status, 0, NULL, 0); -@@ -126,7 +124,7 @@ void ares_search(ares_channel channel, const char *name, int dnsclass, - /* Try the name as-is last; start with the first search domain. */ - squery->next_domain = 1; - squery->trying_as_is = 0; -- status = cat_domain(name, channel->domains[0], &s); -+ status = ares__cat_domain(name, channel->domains[0], &s); - if (status == ARES_SUCCESS) - { - ares_query(channel, s, dnsclass, type, search_callback, squery); -@@ -174,7 +172,7 @@ static void search_callback(void *arg, int status, int timeouts, - if (squery->next_domain < channel->ndomains) - { - /* Try the next domain. */ -- status = cat_domain(squery->name, -+ status = ares__cat_domain(squery->name, - channel->domains[squery->next_domain], &s); - if (status != ARES_SUCCESS) - end_squery(squery, status, NULL, 0); -@@ -213,7 +211,7 @@ static void end_squery(struct search_query *squery, int status, - } - - /* Concatenate two domains. */ --static int cat_domain(const char *name, const char *domain, char **s) -+int ares__cat_domain(const char *name, const char *domain, char **s) - { - size_t nlen = strlen(name); - size_t dlen = strlen(domain); -@@ -232,7 +230,7 @@ static int cat_domain(const char *name, const char *domain, char **s) - * the string we should query, in an allocated buffer. If not, set *s - * to NULL. - */ --STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s) -+int ares__single_domain(ares_channel channel, const char *name, char **s) - { - size_t len = strlen(name); - const char *hostaliases; -diff --git a/test/Makefile.inc b/test/Makefile.inc -index b25f2e2..7952b4c 100644 ---- a/test/Makefile.inc -+++ b/test/Makefile.inc -@@ -1,6 +1,7 @@ - TESTSOURCES = ares-test-main.cc \ - ares-test-init.cc \ - ares-test.cc \ -+ ares-test-ai.cc \ - ares-test-ns.cc \ - ares-test-parse.cc \ - ares-test-parse-a.cc \ -@@ -14,12 +15,14 @@ TESTSOURCES = ares-test-main.cc \ - ares-test-parse-txt.cc \ - ares-test-misc.cc \ - ares-test-mock.cc \ -+ ares-test-mock-ai.cc \ - ares-test-internal.cc \ - dns-proto.cc \ - dns-proto-test.cc - - TESTHEADERS = ares-test.h \ -- dns-proto.h -+ dns-proto.h \ -+ ares-test-ai.h - - FUZZSOURCES = ares-test-fuzz.c \ - ares-fuzz.c -diff --git a/test/ares-test-ai.cc b/test/ares-test-ai.cc -new file mode 100644 -index 0000000..e69de29 -diff --git a/test/ares-test-ai.h b/test/ares-test-ai.h -new file mode 100644 -index 0000000..e4c4403 ---- /dev/null -+++ b/test/ares-test-ai.h -@@ -0,0 +1,32 @@ -+#ifndef ARES_TEST_AI_H -+#define ARES_TEST_AI_H -+ -+#include -+#include "gtest/gtest.h" -+#include "gmock/gmock.h" -+#include "ares-test.h" -+ -+namespace ares { -+namespace test { -+ -+class MockChannelTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface< std::pair > { -+ public: -+ MockChannelTestAI() : MockChannelOptsTest(1, GetParam().first, GetParam().second, nullptr, 0) {} -+}; -+ -+// Structure that describes the result of an ares_addr_callback invocation. -+struct 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; -+}; -+ -+} -+} -+ -+#endif -diff --git a/test/ares-test-internal.cc b/test/ares-test-internal.cc -index a021033..a2d31c5 100644 ---- a/test/ares-test-internal.cc -+++ b/test/ares-test-internal.cc -@@ -383,23 +383,23 @@ TEST_F(LibraryTest, Striendstr) { - const char *str = "plugh"; - EXPECT_NE(nullptr, ares_striendstr(str, str)); - } --extern "C" int single_domain(ares_channel, const char*, char**); -+extern "C" int ares__single_domain(ares_channel, const char*, char**); - TEST_F(DefaultChannelTest, SingleDomain) { - TempFile aliases("www www.google.com\n"); - EnvValue with_env("HOSTALIASES", aliases.filename()); - - SetAllocSizeFail(128); - char *ptr = nullptr; -- EXPECT_EQ(ARES_ENOMEM, single_domain(channel_, "www", &ptr)); -+ EXPECT_EQ(ARES_ENOMEM, ares__single_domain(channel_, "www", &ptr)); - - channel_->flags |= ARES_FLAG_NOSEARCH|ARES_FLAG_NOALIASES; -- EXPECT_EQ(ARES_SUCCESS, single_domain(channel_, "www", &ptr)); -+ EXPECT_EQ(ARES_SUCCESS, ares__single_domain(channel_, "www", &ptr)); - EXPECT_EQ("www", std::string(ptr)); - ares_free(ptr); - ptr = nullptr; - - SetAllocFail(1); -- EXPECT_EQ(ARES_ENOMEM, single_domain(channel_, "www", &ptr)); -+ EXPECT_EQ(ARES_ENOMEM, ares__single_domain(channel_, "www", &ptr)); - EXPECT_EQ(nullptr, ptr); - } - #endif -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -new file mode 100644 -index 0000000..8ba1611 ---- /dev/null -+++ b/test/ares-test-mock-ai.cc -@@ -0,0 +1,154 @@ -+#include "ares-test-ai.h" -+#include "dns-proto.h" -+ -+#ifdef HAVE_NETDB_H -+#include -+#endif -+ -+#include -+#include -+#include -+ -+using testing::InvokeWithoutArgs; -+using testing::DoAll; -+ -+namespace ares { -+namespace test { -+ -+MATCHER_P(IncludesNumAddresses, n, "") { -+ int cnt = 0; -+ for (const ares_addrinfo* ai = arg; ai != NULL; ai = ai->ai_next) -+ cnt++; -+ return n == cnt; -+} -+ -+MATCHER_P(IncludesV4Address, address, "") { -+ 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) { -+ if (ai->ai_family != AF_INET) -+ continue; -+ if (reinterpret_cast(ai->ai_addr)->sin_addr.s_addr == -+ addressnum.s_addr) -+ return true; // found -+ } -+ return false; -+} -+ -+MATCHER_P(IncludesV6Address, address, "") { -+ 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) { -+ if (ai->ai_family != AF_INET6) -+ continue; -+ if (!memcmp( -+ reinterpret_cast(ai->ai_addr)->sin6_addr.s6_addr, -+ addressnum.s6_addr, sizeof(addressnum.s6_addr))) -+ return true; // found -+ } -+ return false; -+} -+ -+TEST_P(MockChannelTestAI, FamilyV6) { -+ DNSPacket rsp6; -+ rsp6.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_aaaa)) -+ .add_answer(new DNSAaaaRR("example.com", 100, -+ {0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03})); -+ ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) -+ .WillByDefault(SetReply(&server_, &rsp6)); -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET6; -+ ares_getaddrinfo(channel_, "example.com.", NULL, &hints, -+ AICallback, &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); -+} -+ -+ -+TEST_P(MockChannelTestAI, FamilyV4) { -+ DNSPacket rsp4; -+ rsp4.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_a)) -+ .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 = {}; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "example.com.", NULL, &hints, -+ AICallback, &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); -+} -+ -+TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) { -+ DNSPacket rsp4; -+ rsp4.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_a)) -+ .add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5})) -+ .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 = {}; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "example.com.", NULL, &hints, -+ AICallback, &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); -+} -+ -+TEST_P(MockChannelTestAI, FamilyUnspecified) { -+ DNSPacket rsp6; -+ rsp6.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_aaaa)) -+ .add_answer(new DNSAaaaRR("example.com", 100, -+ {0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03})); -+ ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) -+ .WillByDefault(SetReply(&server_, &rsp6)); -+ DNSPacket rsp4; -+ rsp4.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_a)) -+ .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; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_UNSPEC; -+ ares_getaddrinfo(channel_, "example.com.", NULL, &hints, -+ AICallback, &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); -+} -+ -+INSTANTIATE_TEST_CASE_P(AddressFamilies, MockChannelTestAI, -+ ::testing::Values(std::make_pair(AF_INET, false))); -+ -+ -+} // namespace test -+} // namespace ares -diff --git a/test/ares-test.cc b/test/ares-test.cc -index 594320d..7776548 100644 ---- a/test/ares-test.cc -+++ b/test/ares-test.cc -@@ -1,4 +1,5 @@ - #include "ares-test.h" -+#include "ares-test-ai.h" - #include "dns-proto.h" - - // Include ares internal files for DNS protocol details -@@ -564,6 +565,16 @@ 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) { -+ EXPECT_NE(nullptr, data); -+ AIResult* result = reinterpret_cast(data); -+ result->done = true; -+ result->status = status; -+ result->airesult = res; -+ //if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl; -+} -+ - std::ostream& operator<<(std::ostream& os, const SearchResult& result) { - os << '{'; - if (result.done_) { -diff --git a/test/ares-test.h b/test/ares-test.h -index abcc508..03e15ec 100644 ---- a/test/ares-test.h -+++ b/test/ares-test.h -@@ -287,6 +287,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); - - // Retrieve the name servers used by a channel. - std::vector GetNameServers(ares_channel channel); --- -2.16.4 - diff --git a/0002-Remaining-queries-counter-fix-additional-unit-tests-.patch b/0002-Remaining-queries-counter-fix-additional-unit-tests-.patch deleted file mode 100644 index c78fe35..0000000 --- a/0002-Remaining-queries-counter-fix-additional-unit-tests-.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 6697ef495521ffd80386b6ccf162db286b36375f Mon Sep 17 00:00:00 2001 -From: Christian Ammer -Date: Sun, 11 Nov 2018 23:25:38 +0100 -Subject: [PATCH 02/10] Remaining queries counter fix, additional unit tests - for `ares_getaddrinfo` (#233) - -Remaining queries counter fix, added tests (ParallelLookups, -SearchDomains, SearchDomainsServFailOnAAAA). Removed unnecessary -if and commented code in test. - -Fix By: Christian Ammer (@ChristianAmmer) ---- - ares_getaddrinfo.c | 39 ++++++++------- - test/ares-test-ai.h | 7 +++ - test/ares-test-mock-ai.cc | 125 +++++++++++++++++++++++++++++++++++++++++++++- - 3 files changed, 153 insertions(+), 18 deletions(-) - -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index be936ff..36f29b5 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -122,7 +122,7 @@ void ares_getaddrinfo(ares_channel channel, - hquery->arg = arg; - hquery->timeouts = 0; - hquery->next_domain = 0; -- hquery->remaining = ai_family == AF_UNSPEC ? 2 : 1; -+ hquery->remaining = 0; - - /* Host file lookup */ - if (file_lookup(hquery->name, ai_family, &hquery->ai) == ARES_SUCCESS) { -@@ -291,9 +291,11 @@ static void next_dns_lookup(struct host_query *hquery) { - if (s) { - if (hquery->ai_family == AF_INET || hquery->ai_family == AF_UNSPEC) { - ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); -+ hquery->remaining++; - } - if (hquery->ai_family == AF_INET6 || hquery->ai_family == AF_UNSPEC) { - ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); -+ hquery->remaining++; - } - if (is_s_allocated) { - ares_free(s); -@@ -306,9 +308,7 @@ static void next_dns_lookup(struct host_query *hquery) { - } - - static void end_hquery(struct host_query *hquery, int status) { -- if (hquery->ai) { -- hquery->callback(hquery->arg, status, hquery->ai); -- } -+ hquery->callback(hquery->arg, status, hquery->ai); - ares_free(hquery->name); - ares_free(hquery); - } -@@ -319,11 +319,14 @@ static void host_callback(void *arg, int status, int timeouts, - ares_channel channel = hquery->channel; - struct hostent *host = NULL; - int qtype; -+ int qtypestatus; - hquery->timeouts += timeouts; - -+ hquery->remaining--; -+ - if (status == ARES_SUCCESS) { -- status = ares__parse_qtype_reply(abuf, alen, &qtype); -- if (status == ARES_SUCCESS && qtype == T_A) { -+ qtypestatus = ares__parse_qtype_reply(abuf, alen, &qtype); -+ if (qtypestatus == ARES_SUCCESS && qtype == T_A) { - /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ - ares_parse_a_reply(abuf, alen, &host, NULL, NULL); - if (host && channel->nsort) { -@@ -331,11 +334,8 @@ static void host_callback(void *arg, int status, int timeouts, - } - add_to_addrinfo(&hquery->ai, host); - ares_free_hostent(host); -- if (!--hquery->remaining) { -- end_hquery(hquery, ARES_SUCCESS); -- } - } -- else if (status == ARES_SUCCESS && qtype == T_AAAA) { -+ else if (qtypestatus == ARES_SUCCESS && qtype == T_AAAA) { - /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ - ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); - if (host && channel->nsort) { -@@ -343,18 +343,23 @@ static void host_callback(void *arg, int status, int timeouts, - } - add_to_addrinfo(&hquery->ai, host); - ares_free_hostent(host); -- if (!--hquery->remaining) { -- end_hquery(hquery, ARES_SUCCESS); -- } -+ } -+ } -+ -+ if (!hquery->remaining) { -+ if (hquery->ai) { -+ // at least one query ended with ARES_SUCCESS -+ end_hquery(hquery, ARES_SUCCESS); -+ } -+ else if (status == ARES_ENOTFOUND) { -+ next_dns_lookup(hquery); - } - else { -- assert(!hquery->ai); - end_hquery(hquery, status); - } - } -- else { -- next_dns_lookup(hquery); -- } -+ -+ // at this point we keep on waiting for the next query to finish - } - - static void sort_addresses(struct hostent *host, -diff --git a/test/ares-test-ai.h b/test/ares-test-ai.h -index e4c4403..a7a6a73 100644 ---- a/test/ares-test-ai.h -+++ b/test/ares-test-ai.h -@@ -16,6 +16,13 @@ class MockChannelTestAI - MockChannelTestAI() : MockChannelOptsTest(1, GetParam().first, GetParam().second, nullptr, 0) {} - }; - -+class MockUDPChannelTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface { -+ public: -+ MockUDPChannelTestAI() : MockChannelOptsTest(1, GetParam(), false, nullptr, 0) {} -+}; -+ - // Structure that describes the result of an ares_addr_callback invocation. - struct AIResult { - // Whether the callback has been invoked. -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -index 8ba1611..28a01be 100644 ---- a/test/ares-test-mock-ai.cc -+++ b/test/ares-test-mock-ai.cc -@@ -52,6 +52,50 @@ MATCHER_P(IncludesV6Address, address, "") { - return false; - } - -+// UDP only so mock server doesn't get confused by concatenated requests -+TEST_P(MockUDPChannelTestAI, ParallelLookups) { -+ DNSPacket rsp1; -+ rsp1.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)) -+ .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_, &rsp1)); -+ DNSPacket rsp2; -+ rsp2.set_response().set_aa() -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)) -+ .add_answer(new DNSARR("www.example.com", 100, {1, 2, 3, 4})); -+ ON_CALL(server_, OnRequest("www.example.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &rsp2)); -+ -+ 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); -+ 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); -+} -+ - TEST_P(MockChannelTestAI, FamilyV6) { - DNSPacket rsp6; - rsp6.set_response().set_aa() -@@ -146,9 +190,88 @@ TEST_P(MockChannelTestAI, FamilyUnspecified) { - ares_freeaddrinfo(result.airesult); - } - --INSTANTIATE_TEST_CASE_P(AddressFamilies, MockChannelTestAI, -+TEST_P(MockChannelTestAI, SearchDomains) { -+ DNSPacket nofirst; -+ nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) -+ .add_question(new DNSQuestion("www.first.com", ns_t_a)); -+ ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &nofirst)); -+ DNSPacket nosecond; -+ nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) -+ .add_question(new DNSQuestion("www.second.org", ns_t_a)); -+ ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &nosecond)); -+ DNSPacket yesthird; -+ yesthird.set_response().set_aa() -+ .add_question(new DNSQuestion("www.third.gov", ns_t_a)) -+ .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); -+ ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &yesthird)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www", NULL, &hints, AICallback, &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); -+} -+ -+TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) { -+ DNSPacket nofirst; -+ nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) -+ .add_question(new DNSQuestion("www.first.com", ns_t_aaaa)); -+ ON_CALL(server_, OnRequest("www.first.com", ns_t_aaaa)) -+ .WillByDefault(SetReply(&server_, &nofirst)); -+ DNSPacket nofirst4; -+ nofirst4.set_response().set_aa().set_rcode(ns_r_nxdomain) -+ .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)); -+ ON_CALL(server_, OnRequest("www.second.org", ns_t_aaaa)) -+ .WillByDefault(SetReply(&server_, &nosecond)); -+ DNSPacket yessecond4; -+ yessecond4.set_response().set_aa() -+ .add_question(new DNSQuestion("www.second.org", ns_t_a)) -+ .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)); -+ ON_CALL(server_, OnRequest("www.third.gov", ns_t_aaaa)) -+ .WillByDefault(SetReply(&server_, &failthird)); -+ DNSPacket failthird4; -+ failthird4.set_response().set_aa().set_rcode(ns_r_servfail) -+ .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; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_UNSPEC; -+ ares_getaddrinfo(channel_, "www", NULL, &hints, AICallback, &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); -+} -+ -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI, - ::testing::Values(std::make_pair(AF_INET, false))); - -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockUDPChannelTestAI, -+ ::testing::ValuesIn(ares::test::families)); - - } // namespace test - } // namespace ares --- -2.16.4 - diff --git a/0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch b/0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch deleted file mode 100644 index fa246e4..0000000 --- a/0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch +++ /dev/null @@ -1,781 +0,0 @@ -From 7442846941cb7a552485f139308a0004c27fa567 Mon Sep 17 00:00:00 2001 -From: Christian Ammer -Date: Sun, 25 Nov 2018 02:59:42 +0100 -Subject: [PATCH 03/10] Bugfix for `ares_getaddrinfo` and additional unit tests - (#234) - -This PullRequest fixes a bug in the function add_to_addrinfo which task is to add new addrinfo items to the ai_next linked list. Also additional unit tests for testing ares_getaddrinfo will be added: - -Additional mock server test classes (ares-test-mock-ai.cc): -MockTCPChannelTestAI -MockExtraOptsTestAI -MockNoCheckRespChannelTestAI -MockEDNSChannelTestAI -RotateMultiMockTestAI -NoRotateMultiMockTestAI - -Additional live tests (ares-test-live-ai.cc): -LiveGetHostByNameV4 -LiveGetHostByNameV6 -LiveGetHostByNameV4AndV6 - -Fix By: Christian Ammer (@ChristianAmmer) ---- - ares_getaddrinfo.c | 69 ++++---- - test/ares-test-ai.h | 29 ++++ - test/ares-test-live-ai.cc | 85 +++++++++ - test/ares-test-mock-ai.cc | 434 +++++++++++++++++++++++++++++++++++++++++++++- - 4 files changed, 584 insertions(+), 33 deletions(-) - create mode 100644 test/ares-test-live-ai.cc - -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index 36f29b5..b89a29c 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -81,8 +81,8 @@ static int get_address_index(const struct in_addr *addr, - static int get6_address_index(const struct ares_in6_addr *addr, - const struct apattern *sortlist, int nsort); - static int as_is_first(const struct host_query *hquery); --static void add_to_addrinfo(struct ares_addrinfo** ai, -- const struct hostent* host); -+static int add_to_addrinfo(struct ares_addrinfo** ai, -+ const struct hostent* host); - static void next_dns_lookup(struct host_query *hquery); - static int is_implemented(const int family); - -@@ -213,16 +213,15 @@ static int file_lookup(const char *name, int family, struct ares_addrinfo **ai) - } - } - status = ARES_ENOTFOUND; -- while (ares__get_hostent(fp, family, &host) == ARES_SUCCESS) { -+ while (status != ARES_ENOMEM && -+ ares__get_hostent(fp, family, &host) == ARES_SUCCESS) { - if (strcasecmp(host->h_name, name) == 0) { -- add_to_addrinfo(ai, host); -- status = ARES_SUCCESS; -+ status = add_to_addrinfo(ai, host); - } - else { - for (alias = host->h_aliases; *alias; alias++) { - if (strcasecmp(*alias, name) == 0) { -- add_to_addrinfo(ai, host); -- status = ARES_SUCCESS; -+ status = add_to_addrinfo(ai, host); - break; - } - } -@@ -233,38 +232,41 @@ static int file_lookup(const char *name, int family, struct ares_addrinfo **ai) - return status; - } - --static void add_to_addrinfo(struct ares_addrinfo** ai, -+static int add_to_addrinfo(struct ares_addrinfo** ai, - const struct hostent* host) { - static const struct ares_addrinfo EmptyAddrinfo; -- struct ares_addrinfo* next_ai; -+ struct ares_addrinfo* front; - char** p; - if (!host || (host->h_addrtype != AF_INET && host->h_addrtype != AF_INET6)) { -- return; -+ return ARES_SUCCESS; - } - for (p = host->h_addr_list; *p; ++p) { -- next_ai = ares_malloc(sizeof(struct ares_addrinfo)); -- *next_ai = EmptyAddrinfo; -- if (*ai) { -- (*ai)->ai_next = next_ai; -- } -- else { -- *ai = next_ai; -- } -+ front = ares_malloc(sizeof(struct ares_addrinfo)); -+ if (!front) goto nomem; -+ *front = EmptyAddrinfo; -+ front->ai_next = *ai; /* insert at front */ -+ *ai = front; - if (host->h_addrtype == AF_INET) { -- next_ai->ai_protocol = IPPROTO_UDP; -- next_ai->ai_family = AF_INET; -- next_ai->ai_addr = ares_malloc(sizeof(struct sockaddr_in)); -- memcpy(&((struct sockaddr_in*)(next_ai->ai_addr))->sin_addr, *p, -+ front->ai_protocol = IPPROTO_UDP; -+ front->ai_family = AF_INET; -+ front->ai_addr = ares_malloc(sizeof(struct sockaddr_in)); -+ if (!front->ai_addr) goto nomem; -+ memcpy(&((struct sockaddr_in*)(front->ai_addr))->sin_addr, *p, - host->h_length); - } - else { -- next_ai->ai_protocol = IPPROTO_UDP; -- next_ai->ai_family = AF_INET6; -- next_ai->ai_addr = ares_malloc(sizeof(struct sockaddr_in6)); -- memcpy(&((struct sockaddr_in6*)(next_ai->ai_addr))->sin6_addr, *p, -+ front->ai_protocol = IPPROTO_UDP; -+ front->ai_family = AF_INET6; -+ front->ai_addr = ares_malloc(sizeof(struct sockaddr_in6)); -+ if (!front->ai_addr) goto nomem; -+ memcpy(&((struct sockaddr_in6*)(front->ai_addr))->sin6_addr, *p, - host->h_length); - } - } -+ return ARES_SUCCESS; -+nomem: -+ ares_freeaddrinfo(*ai); -+ return ARES_ENOMEM; - } - - static void next_dns_lookup(struct host_query *hquery) { -@@ -320,6 +322,7 @@ static void host_callback(void *arg, int status, int timeouts, - struct hostent *host = NULL; - int qtype; - int qtypestatus; -+ int addinfostatus = ARES_SUCCESS; - hquery->timeouts += timeouts; - - hquery->remaining--; -@@ -332,7 +335,7 @@ static void host_callback(void *arg, int status, int timeouts, - if (host && channel->nsort) { - sort_addresses(host, channel->sortlist, channel->nsort); - } -- add_to_addrinfo(&hquery->ai, host); -+ addinfostatus = add_to_addrinfo(&hquery->ai, host); - ares_free_hostent(host); - } - else if (qtypestatus == ARES_SUCCESS && qtype == T_AAAA) { -@@ -341,14 +344,18 @@ static void host_callback(void *arg, int status, int timeouts, - if (host && channel->nsort) { - sort6_addresses(host, channel->sortlist, channel->nsort); - } -- add_to_addrinfo(&hquery->ai, host); -+ addinfostatus = add_to_addrinfo(&hquery->ai, host); - ares_free_hostent(host); - } - } - - if (!hquery->remaining) { -- if (hquery->ai) { -- // at least one query ended with ARES_SUCCESS -+ if (addinfostatus != ARES_SUCCESS) { -+ /* no memory */ -+ end_hquery(hquery, addinfostatus); -+ } -+ else if (hquery->ai) { -+ /* at least one query ended with ARES_SUCCESS */ - end_hquery(hquery, ARES_SUCCESS); - } - else if (status == ARES_ENOTFOUND) { -@@ -359,7 +366,7 @@ static void host_callback(void *arg, int status, int timeouts, - } - } - -- // at this point we keep on waiting for the next query to finish -+ /* at this point we keep on waiting for the next query to finish */ - } - - static void sort_addresses(struct hostent *host, -diff --git a/test/ares-test-ai.h b/test/ares-test-ai.h -index a7a6a73..d558489 100644 ---- a/test/ares-test-ai.h -+++ b/test/ares-test-ai.h -@@ -23,8 +23,37 @@ class MockUDPChannelTestAI - MockUDPChannelTestAI() : MockChannelOptsTest(1, GetParam(), false, nullptr, 0) {} - }; - -+class MockTCPChannelTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface { -+ public: -+ MockTCPChannelTestAI() : MockChannelOptsTest(1, GetParam(), true, nullptr, 0) {} -+}; -+ -+ -+// Test fixture that uses a default channel. -+class DefaultChannelTestAI : public LibraryTest { -+ public: -+ DefaultChannelTestAI() : channel_(nullptr) { -+ EXPECT_EQ(ARES_SUCCESS, ares_init(&channel_)); -+ EXPECT_NE(nullptr, channel_); -+ } -+ -+ ~DefaultChannelTestAI() { -+ ares_destroy(channel_); -+ channel_ = nullptr; -+ } -+ -+ // Process all pending work on ares-owned file descriptors. -+ void Process(); -+ -+ protected: -+ 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. -diff --git a/test/ares-test-live-ai.cc b/test/ares-test-live-ai.cc -new file mode 100644 -index 0000000..96260fb ---- /dev/null -+++ b/test/ares-test-live-ai.cc -@@ -0,0 +1,85 @@ -+// This file includes tests that attempt to do real lookups -+// of DNS names using the local machine's live infrastructure. -+// As a result, we don't check the results very closely, to allow -+// for varying local configurations. -+ -+#include "ares-test.h" -+#include "ares-test-ai.h" -+ -+#ifdef HAVE_NETDB_H -+#include -+#endif -+ -+namespace ares { -+namespace test { -+ -+MATCHER_P(IncludesAtLeastNumAddresses, n, "") { -+ int cnt = 0; -+ for (const ares_addrinfo* ai = arg; 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) -+ 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) -+ if (ai->ai_family == addrtype) -+ return true; -+ return false; -+} -+ -+void DefaultChannelTestAI::Process() { -+ ProcessWork(channel_, NoExtraFDs, nullptr); -+} -+ -+// Use the address of Google's public DNS servers as example addresses that are -+// likely to be accessible everywhere/everywhen. -+ -+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); -+ 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); -+} -+ -+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); -+ 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); -+} -+ -+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); -+ 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); -+} -+ -+} // namespace test -+} // namespace ares -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -index 28a01be..a67f811 100644 ---- a/test/ares-test-mock-ai.cc -+++ b/test/ares-test-mock-ai.cc -@@ -96,6 +96,261 @@ TEST_P(MockUDPChannelTestAI, ParallelLookups) { - ares_freeaddrinfo(result3.airesult); - } - -+// UDP to TCP specific test -+TEST_P(MockUDPChannelTestAI, TruncationRetry) { -+ DNSPacket rsptruncated; -+ rsptruncated.set_response().set_aa().set_tc() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ DNSPacket rspok; -+ rspok.set_response() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)) -+ .add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4})); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsptruncated)) -+ .WillOnce(SetReply(&server_, &rspok)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &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); -+} -+ -+// TCP only to prevent retries -+TEST_P(MockTCPChannelTestAI, MalformedResponse) { -+ std::vector one = {0x01}; -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReplyData(&server_, one)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ EXPECT_EQ(ARES_ETIMEOUT, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockTCPChannelTestAI, FormErrResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_formerr); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ EXPECT_EQ(ARES_EFORMERR, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockTCPChannelTestAI, ServFailResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_servfail); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ // ARES_FLAG_NOCHECKRESP not set, so SERVFAIL consumed -+ EXPECT_EQ(ARES_ECONNREFUSED, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockTCPChannelTestAI, NotImplResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_notimpl); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ // ARES_FLAG_NOCHECKRESP not set, so NOTIMPL consumed -+ EXPECT_EQ(ARES_ECONNREFUSED, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockTCPChannelTestAI, RefusedResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_refused); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ // ARES_FLAG_NOCHECKRESP not set, so REFUSED consumed -+ EXPECT_EQ(ARES_ECONNREFUSED, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+// TODO: make it work -+//TEST_P(MockTCPChannelTestAI, YXDomainResponse) { -+// DNSPacket rsp; -+// rsp.set_response().set_aa() -+// .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+// rsp.set_rcode(ns_r_yxdomain); -+// EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+// .WillOnce(SetReply(&server_, &rsp)); -+// -+// AIResult result; -+// struct ares_addrinfo hints = {}; -+// hints.ai_family = AF_INET; -+// ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+// Process(); -+// EXPECT_TRUE(result.done); -+// EXPECT_EQ(ARES_ENODATA, result.status); -+// ares_freeaddrinfo(result.airesult); -+//} -+ -+class MockExtraOptsTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface< std::pair > { -+ public: -+ MockExtraOptsTestAI() -+ : MockChannelOptsTest(1, GetParam().first, GetParam().second, -+ FillOptions(&opts_), -+ ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} -+ static struct ares_options* FillOptions(struct ares_options * opts) { -+ memset(opts, 0, sizeof(struct ares_options)); -+ // Set a few options that affect socket communications -+ opts->socket_send_buffer_size = 514; -+ opts->socket_receive_buffer_size = 514; -+ return opts; -+ } -+ private: -+ struct ares_options opts_; -+}; -+ -+TEST_P(MockExtraOptsTestAI, SimpleQuery) { -+ ares_set_local_ip4(channel_, 0x7F000001); -+ byte addr6[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; -+ ares_set_local_ip6(channel_, addr6); -+ ares_set_local_dev(channel_, "dummy"); -+ -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)) -+ .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; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &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); -+} -+ -+class MockFlagsChannelOptsTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface< std::pair > { -+ public: -+ MockFlagsChannelOptsTestAI(int flags) -+ : MockChannelOptsTest(1, GetParam().first, GetParam().second, -+ FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} -+ static struct ares_options* FillOptions(struct ares_options * opts, int flags) { -+ memset(opts, 0, sizeof(struct ares_options)); -+ opts->flags = flags; -+ return opts; -+ } -+ private: -+ struct ares_options opts_; -+}; -+ -+class MockNoCheckRespChannelTestAI : public MockFlagsChannelOptsTestAI { -+ public: -+ MockNoCheckRespChannelTestAI() : MockFlagsChannelOptsTestAI(ARES_FLAG_NOCHECKRESP) {} -+}; -+ -+TEST_P(MockNoCheckRespChannelTestAI, ServFailResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_servfail); -+ ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ EXPECT_EQ(ARES_ESERVFAIL, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_notimpl); -+ ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ EXPECT_EQ(ARES_ENOTIMP, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ -+TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_refused); -+ ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &rsp)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done); -+ EXPECT_EQ(ARES_EREFUSED, result.status); -+ ares_freeaddrinfo(result.airesult); -+} -+ - TEST_P(MockChannelTestAI, FamilyV6) { - DNSPacket rsp6; - rsp6.set_response().set_aa() -@@ -118,7 +373,6 @@ TEST_P(MockChannelTestAI, FamilyV6) { - ares_freeaddrinfo(result.airesult); - } - -- - TEST_P(MockChannelTestAI, FamilyV4) { - DNSPacket rsp4; - rsp4.set_response().set_aa() -@@ -190,6 +444,34 @@ TEST_P(MockChannelTestAI, FamilyUnspecified) { - ares_freeaddrinfo(result.airesult); - } - -+class MockEDNSChannelTestAI : public MockFlagsChannelOptsTestAI { -+ public: -+ MockEDNSChannelTestAI() : MockFlagsChannelOptsTestAI(ARES_FLAG_EDNS) {} -+}; -+ -+TEST_P(MockEDNSChannelTestAI, RetryWithoutEDNS) { -+ DNSPacket rspfail; -+ rspfail.set_response().set_aa().set_rcode(ns_r_servfail) -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ DNSPacket rspok; -+ rspok.set_response() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)) -+ .add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4})); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rspfail)) -+ .WillOnce(SetReply(&server_, &rspok)); -+ -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AICallback, &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); -+} -+ - TEST_P(MockChannelTestAI, SearchDomains) { - DNSPacket nofirst; - nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) -@@ -267,11 +549,159 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) { - ares_freeaddrinfo(result.airesult); - } - -+class MockMultiServerChannelTestAI -+ : public MockChannelOptsTest, -+ public ::testing::WithParamInterface< std::pair > { -+ public: -+ MockMultiServerChannelTestAI(bool rotate) -+ : MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {} -+ void CheckExample() { -+ AIResult result; -+ struct ares_addrinfo hints = {}; -+ hints.ai_family = AF_INET; -+ ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AICallback, &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); -+ } -+}; -+ -+class RotateMultiMockTestAI : public MockMultiServerChannelTestAI { -+ public: -+ RotateMultiMockTestAI() : MockMultiServerChannelTestAI(true) {} -+}; -+ -+class NoRotateMultiMockTestAI : public MockMultiServerChannelTestAI { -+ public: -+ NoRotateMultiMockTestAI() : MockMultiServerChannelTestAI(false) {} -+}; -+ -+ -+TEST_P(RotateMultiMockTestAI, ThirdServer) { -+ struct ares_options opts = {0}; -+ int optmask = 0; -+ EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel_, &opts, &optmask)); -+ EXPECT_EQ(0, (optmask & ARES_OPT_NOROTATE)); -+ ares_destroy_options(&opts); -+ -+ DNSPacket servfailrsp; -+ servfailrsp.set_response().set_aa().set_rcode(ns_r_servfail) -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)); -+ DNSPacket notimplrsp; -+ notimplrsp.set_response().set_aa().set_rcode(ns_r_notimpl) -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)); -+ DNSPacket okrsp; -+ okrsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)) -+ .add_answer(new DNSARR("www.example.com", 100, {2,3,4,5})); -+ -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), &okrsp)); -+ CheckExample(); -+ -+ // Second time around, starts from server [1]. -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), &okrsp)); -+ CheckExample(); -+ -+ // Third time around, starts from server [2]. -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), &okrsp)); -+ CheckExample(); -+} -+ -+TEST_P(NoRotateMultiMockTestAI, ThirdServer) { -+ struct ares_options opts = {0}; -+ int optmask = 0; -+ EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel_, &opts, &optmask)); -+ EXPECT_EQ(ARES_OPT_NOROTATE, (optmask & ARES_OPT_NOROTATE)); -+ ares_destroy_options(&opts); -+ -+ DNSPacket servfailrsp; -+ servfailrsp.set_response().set_aa().set_rcode(ns_r_servfail) -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)); -+ DNSPacket notimplrsp; -+ notimplrsp.set_response().set_aa().set_rcode(ns_r_notimpl) -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)); -+ DNSPacket okrsp; -+ okrsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.example.com", ns_t_a)) -+ .add_answer(new DNSARR("www.example.com", 100, {2,3,4,5})); -+ -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), &okrsp)); -+ CheckExample(); -+ -+ // Second time around, still starts from server [0]. -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), &okrsp)); -+ CheckExample(); -+ -+ // Third time around, still starts from server [0]. -+ EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); -+ EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); -+ EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) -+ .WillOnce(SetReply(servers_[2].get(), &okrsp)); -+ CheckExample(); -+} -+ -+// force-tcp does currently not work, possibly test DNS server swallows -+// bytes from second query -+//INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI, -+// ::testing::ValuesIn(ares::test::families_modes)); -+//const std::vector> both_families_udponly = { -+// std::make_pair(AF_INET, false), -+// std::make_pair(AF_INET6, false) -+//}; - INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI, -- ::testing::Values(std::make_pair(AF_INET, false))); -+ ::testing::Values(std::make_pair(AF_INET, false))); - - INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockUDPChannelTestAI, - ::testing::ValuesIn(ares::test::families)); - -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockTCPChannelTestAI, -+ ::testing::ValuesIn(ares::test::families)); -+ -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockExtraOptsTestAI, -+ ::testing::ValuesIn(ares::test::families_modes)); -+ -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockNoCheckRespChannelTestAI, -+ ::testing::ValuesIn(ares::test::families_modes)); -+ -+INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockEDNSChannelTestAI, -+ ::testing::ValuesIn(ares::test::families_modes)); -+ -+INSTANTIATE_TEST_CASE_P(TransportModesAI, RotateMultiMockTestAI, -+ ::testing::ValuesIn(ares::test::families_modes)); -+ -+INSTANTIATE_TEST_CASE_P(TransportModesAI, NoRotateMultiMockTestAI, -+ ::testing::ValuesIn(ares::test::families_modes)); -+ -+ - } // namespace test - } // namespace ares --- -2.16.4 - diff --git a/0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch b/0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch deleted file mode 100644 index 1ce794b..0000000 --- a/0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch +++ /dev/null @@ -1,1472 +0,0 @@ -From b565626751ea6bb69fbe1642c89c8b634b064911 Mon Sep 17 00:00:00 2001 -From: Andrew Selivanov -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 -+#endif -+#ifdef HAVE_NETDB_H -+# include -+#endif -+#ifdef HAVE_STRINGS_H -+# include -+#endif -+ -+#include -+#include -+ -+#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 . */ -+/* 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 - .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 - .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(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(data); -- result->done = true; -- result->status = status; -- result->airesult = res; -- //if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl; -+ AddrInfoResult* result = reinterpret_cast(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; -+ -+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 GetNameServers(ares_channel channel); --- -2.16.4 - diff --git a/0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch b/0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch deleted file mode 100644 index 303d4cf..0000000 --- a/0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 1dc228c872974d7eafc34b53816e973e00224351 Mon Sep 17 00:00:00 2001 -From: kedixa <1204837541@qq.com> -Date: Tue, 9 Apr 2019 07:37:43 +0800 -Subject: [PATCH 05/10] getaddrinfo: avoid infinite loop in case of - NXDOMAIN(#240) (#242) - -There are two possible causes for infinite loops fo NXDOMAIN, based on how many dots are in the domain name (one for < ARES_OPT_NDOTS and one for >= ARES_OPT_NDOTS), where it will repeat the same query over and over as the hquery->next_domain doesn't increment. - -Fix By: @kedixa ---- - ares_getaddrinfo.c | 21 +++++++++++++++------ - 1 file changed, 15 insertions(+), 6 deletions(-) - -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index ebaeda8..16c8b38 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -122,7 +122,7 @@ void ares_getaddrinfo(ares_channel channel, - hquery->callback = callback; - hquery->arg = arg; - hquery->timeouts = 0; -- hquery->next_domain = 0; -+ hquery->next_domain = -1; /* see next_dns_lookup for more info */ - hquery->remaining = 0; - - /* Host file lookup */ -@@ -279,11 +279,20 @@ static void next_dns_lookup(struct host_query *hquery) { - char *s = NULL; - int is_s_allocated = 0; - int status; -- -- if (( as_is_first(hquery) && hquery->next_domain == 0) || -- (!as_is_first(hquery) && hquery->next_domain == -- hquery->channel->ndomains)) { -- s = hquery->name; -+ /* if next_domain == -1 and as_is_first is true, try hquery->name */ -+ if(hquery->next_domain == -1) { -+ if(as_is_first(hquery)) { -+ s = hquery->name; -+ } -+ hquery->next_domain = 0; -+ } -+ /* if as_is_first is false, try hquery->name at last */ -+ if(!s && hquery->next_domain == hquery->channel->ndomains) { -+ if(!as_is_first(hquery)) { -+ s = hquery->name; -+ } -+ /* avoid infinite loop */ -+ hquery->next_domain++; - } - - if (!s && hquery->next_domain < hquery->channel->ndomains) { --- -2.16.4 - diff --git a/0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch b/0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch deleted file mode 100644 index 70d6f2e..0000000 --- a/0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 36348062003d1ef1a36148dd6d6a225f0ad65f5f Mon Sep 17 00:00:00 2001 -From: kedixa <1204837541@qq.com> -Date: Thu, 2 May 2019 21:08:41 +0800 -Subject: [PATCH 06/10] getaddrinfo: callback must be called on bad domain - (#249) - -Due to an order of incrementing the remaining queries and calling ares_query, on a bad domain -the registered callback wouldn't be called. - -Bug: #248 -Fixed-By: @kedixa ---- - ares_getaddrinfo.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index 16c8b38..c9db8a2 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -307,12 +307,12 @@ static void next_dns_lookup(struct host_query *hquery) { - - if (s) { - if (hquery->ai_family == AF_INET || hquery->ai_family == AF_UNSPEC) { -- ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); - hquery->remaining++; -+ ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); - } - if (hquery->ai_family == AF_INET6 || hquery->ai_family == AF_UNSPEC) { -- ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); - hquery->remaining++; -+ ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); - } - if (is_s_allocated) { - ares_free(s); --- -2.16.4 - diff --git a/0007-getaddrinfo-enhancements-257.patch b/0007-getaddrinfo-enhancements-257.patch deleted file mode 100644 index f5e3de3..0000000 --- a/0007-getaddrinfo-enhancements-257.patch +++ /dev/null @@ -1,3591 +0,0 @@ -From 7658a2d81cd69c207c411319101ce8a9b245f088 Mon Sep 17 00:00:00 2001 -From: Andrew Selivanov -Date: Tue, 18 Jun 2019 22:56:44 +0300 -Subject: [PATCH 07/10] getaddrinfo enhancements (#257) - -* Service support has been added to getaddrinfo. -* ares_parse_a/aaaa_record now share code with the addrinfo parser. -* Private ares_addrinfo structure with useful extensions such as ttls (including cname ttls), - as well as the ability to list multiple cnames in chain of lookups - -Work By: Andrew Selivanov @ki11roy ---- - CMakeLists.txt | 14 + - Makefile.inc | 22 +- - ares.h | 60 ++- - ares__parse_into_addrinfo.c | 266 +++++++++++++ - ares__readaddrinfo.c | 261 +++++++++++++ - ares__sortaddrinfo.c | 6 +- - ares_config.h.cmake | 6 + - ares_freeaddrinfo.3 | 8 +- - ares_freeaddrinfo.c | 57 +++ - ares_getaddrinfo.3 | 4 +- - ares_getaddrinfo.c | 933 ++++++++++++++++++++++++++------------------ - ares_parse_a_reply.3 | 2 + - ares_parse_a_reply.c | 325 ++++++--------- - ares_parse_aaaa_reply.3 | 2 + - ares_parse_aaaa_reply.c | 291 ++++++-------- - ares_private.h | 31 +- - m4/cares-functions.m4 | 140 +++++++ - test/ares-test-internal.cc | 101 +++++ - test/ares-test-live-ai.cc | 82 ---- - test/ares-test-live.cc | 64 +++ - test/ares-test-mock-ai.cc | 125 ++++-- - test/ares-test-mock.cc | 2 +- - test/ares-test.cc | 24 +- - test/ares-test.h | 1 - - 24 files changed, 1899 insertions(+), 928 deletions(-) - create mode 100644 ares__parse_into_addrinfo.c - create mode 100644 ares__readaddrinfo.c - create mode 100644 ares_freeaddrinfo.c - delete mode 100644 test/ares-test-live-ai.cc - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index dd028ab..139418d 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -323,6 +323,7 @@ CHECK_SYMBOL_EXISTS (gethostbyname "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOST - CHECK_SYMBOL_EXISTS (gethostname "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOSTNAME) - CHECK_SYMBOL_EXISTS (getnameinfo "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETNAMEINFO) - CHECK_SYMBOL_EXISTS (getservbyport_r "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETSERVBYPORT_R) -+CHECK_SYMBOL_EXISTS (getservbyname_r "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETSERVBYNAME_R) - CHECK_SYMBOL_EXISTS (gettimeofday "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETTIMEOFDAY) - CHECK_SYMBOL_EXISTS (if_indextoname "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_IF_INDEXTONAME) - CHECK_SYMBOL_EXISTS (inet_net_pton "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_INET_NET_PTON) -@@ -492,6 +493,19 @@ IF (HAVE_GETSERVBYPORT_R) - ENDIF () - ENDIF () - -+IF (HAVE_GETSERVBYNAME_R) -+ # TODO : Should probably autodetect -+ IF (CMAKE_SYSTEM_NAME STREQUAL "SunOS") -+ SET (GETSERVBYNAME_R_ARGS 5) -+ ELSEIF (CMAKE_SYSTEM_NAME STREQUAL "AIX" OR -+ CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") -+ SET (GETSERVBYNAME_R_ARGS 4) -+ ELSE () -+ # Probably linux -+ SET (GETSERVBYNAME_R_ARGS 6) -+ ENDIF () -+ENDIF () -+ - # Set some aliases used for ares_build.h - IF (HAVE_SYS_TYPES_H) - SET (CARES_HAVE_SYS_TYPES_H 1) -diff --git a/Makefile.inc b/Makefile.inc -index de165cf..f65df1f 100644 ---- a/Makefile.inc -+++ b/Makefile.inc -@@ -1,6 +1,8 @@ - - CSOURCES = ares__close_sockets.c \ - ares__get_hostent.c \ -+ ares__parse_into_addrinfo.c \ -+ ares__readaddrinfo.c \ - ares__sortaddrinfo.c \ - ares__read_line.c \ - ares__timeval.c \ -@@ -13,6 +15,8 @@ CSOURCES = ares__close_sockets.c \ - ares_fds.c \ - ares_free_hostent.c \ - ares_free_string.c \ -+ ares_freeaddrinfo.c \ -+ ares_getaddrinfo.c \ - ares_getenv.c \ - ares_gethostbyaddr.c \ - ares_gethostbyname.c \ -@@ -49,8 +53,7 @@ CSOURCES = ares__close_sockets.c \ - bitncmp.c \ - inet_net_pton.c \ - inet_ntop.c \ -- windows_port.c \ -- ares_getaddrinfo.c -+ windows_port.c - - HHEADERS = ares.h \ - ares_android.h \ -@@ -88,8 +91,10 @@ MANPAGES = ares_cancel.3 \ - ares_free_data.3 \ - ares_free_hostent.3 \ - ares_free_string.3 \ -+ ares_freeaddrinfo.3 \ - ares_get_servers.3 \ - ares_get_servers_ports.3 \ -+ ares_getaddrinfo.3 \ - ares_gethostbyaddr.3 \ - ares_gethostbyname.3 \ - ares_gethostbyname_file.3 \ -@@ -131,8 +136,7 @@ MANPAGES = ares_cancel.3 \ - ares_set_sortlist.3 \ - ares_strerror.3 \ - ares_timeout.3 \ -- ares_version.3 \ -- ares_getaddrinfo.3 -+ ares_version.3 - - HTMLPAGES = ares_cancel.html \ - ares_create_query.html \ -@@ -145,8 +149,10 @@ HTMLPAGES = ares_cancel.html \ - ares_free_data.html \ - ares_free_hostent.html \ - ares_free_string.html \ -+ ares_freeaddrinfo.html \ - ares_get_servers.html \ - ares_get_servers_ports.html \ -+ ares_getaddrinfo.html \ - ares_gethostbyaddr.html \ - ares_gethostbyname.html \ - ares_gethostbyname_file.html \ -@@ -187,8 +193,7 @@ HTMLPAGES = ares_cancel.html \ - ares_set_sortlist.html \ - ares_strerror.html \ - ares_timeout.html \ -- ares_version.html \ -- ares_getaddrinfo.html -+ ares_version.html - - PDFPAGES = ares_cancel.pdf \ - ares_create_query.pdf \ -@@ -201,8 +206,10 @@ PDFPAGES = ares_cancel.pdf \ - ares_free_data.pdf \ - ares_free_hostent.pdf \ - ares_free_string.pdf \ -+ ares_freeaddrinfo.pdf \ - ares_get_servers.pdf \ - ares_get_servers_ports.pdf \ -+ ares_getaddrinfo.pdf \ - ares_gethostbyaddr.pdf \ - ares_gethostbyname.pdf \ - ares_gethostbyname_file.pdf \ -@@ -243,8 +250,7 @@ PDFPAGES = ares_cancel.pdf \ - ares_set_sortlist.pdf \ - ares_strerror.pdf \ - ares_timeout.pdf \ -- ares_version.pdf \ -- ares_getaddrinfo.pdf -+ ares_version.pdf - - SAMPLESOURCES = ares_getopt.c \ - ares_nowarn.c \ -diff --git a/ares.h b/ares.h -index af82141..91dc754 100644 ---- a/ares.h -+++ b/ares.h -@@ -135,6 +135,9 @@ extern "C" { - /* More error codes */ - #define ARES_ECANCELLED 24 /* introduced in 1.7.0 */ - -+/* More ares_getaddrinfo error codes */ -+#define ARES_ESERVICE 25 /* introduced in 1.?.0 */ -+ - /* Flag values */ - #define ARES_FLAG_USEVC (1 << 0) - #define ARES_FLAG_PRIMARY (1 << 1) -@@ -192,6 +195,8 @@ extern "C" { - #define ARES_AI_V4MAPPED (1 << 4) - #define ARES_AI_ALL (1 << 5) - #define ARES_AI_ADDRCONFIG (1 << 6) -+#define ARES_AI_NOSORT (1 << 7) -+#define ARES_AI_ENVHOSTS (1 << 8) - /* Reserved for future use */ - #define ARES_AI_IDN (1 << 10) - #define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11) -@@ -279,6 +284,7 @@ struct timeval; - struct sockaddr; - struct ares_channeldata; - struct ares_addrinfo; -+struct ares_addrinfo_hints; - - typedef struct ares_channeldata *ares_channel; - -@@ -307,7 +313,7 @@ typedef int (*ares_sock_config_callback)(ares_socket_t socket_fd, - int type, - void *data); - --typedef void (*ares_addr_callback)(void *arg, -+typedef void (*ares_addrinfo_callback)(void *arg, - int status, - int timeouts, - struct ares_addrinfo *res); -@@ -376,9 +382,12 @@ CARES_EXTERN int ares_set_sortlist(ares_channel channel, - const char *sortstr); - - CARES_EXTERN void ares_getaddrinfo(ares_channel channel, -- const char* node, const char* service, -- const struct ares_addrinfo* hints, -- ares_addr_callback callback, void* arg); -+ const char* node, -+ const char* service, -+ const struct ares_addrinfo_hints* hints, -+ ares_addrinfo_callback callback, -+ void* arg); -+ - CARES_EXTERN void ares_freeaddrinfo(struct ares_addrinfo* ai); - - /* -@@ -570,15 +579,42 @@ struct ares_soa_reply { - unsigned int minttl; - }; - -+/* -+ * Similar to addrinfo, but with extra ttl and missing canonname. -+ */ -+struct ares_addrinfo_node { -+ int ai_ttl; -+ int ai_flags; -+ int ai_family; -+ int ai_socktype; -+ int ai_protocol; -+ ares_socklen_t ai_addrlen; -+ struct sockaddr *ai_addr; -+ struct ares_addrinfo_node *ai_next; -+}; -+ -+/* -+ * alias - label of the resource record. -+ * name - value (canonical name) of the resource record. -+ * See RFC2181 10.1.1. CNAME terminology. -+ */ -+struct ares_addrinfo_cname { -+ int ttl; -+ char *alias; -+ char *name; -+ struct ares_addrinfo_cname *next; -+}; -+ - struct ares_addrinfo { -- int ai_flags; -- int ai_family; -- int ai_socktype; -- int ai_protocol; -- ares_socklen_t ai_addrlen; -- char *ai_canonname; -- struct sockaddr *ai_addr; -- struct ares_addrinfo *ai_next; -+ struct ares_addrinfo_cname *cnames; -+ struct ares_addrinfo_node *nodes; -+}; -+ -+struct ares_addrinfo_hints { -+ int ai_flags; -+ int ai_family; -+ int ai_socktype; -+ int ai_protocol; - }; - - /* -diff --git a/ares__parse_into_addrinfo.c b/ares__parse_into_addrinfo.c -new file mode 100644 -index 0000000..b080163 ---- /dev/null -+++ b/ares__parse_into_addrinfo.c -@@ -0,0 +1,266 @@ -+/* Copyright (C) 2019 by Andrew Selivanov -+ * -+ * Permission to use, copy, modify, and distribute this -+ * software and its documentation for any purpose and without -+ * fee is hereby granted, provided that the above copyright -+ * notice appear in all copies and that both that copyright -+ * notice and this permission notice appear in supporting -+ * documentation, and that the name of M.I.T. not be used in -+ * advertising or publicity pertaining to distribution of the -+ * software without specific, written prior permission. -+ * M.I.T. makes no representations about the suitability of -+ * this software for any purpose. It is provided "as is" -+ * without express or implied warranty. -+ */ -+ -+#include "ares_setup.h" -+ -+#ifdef HAVE_NETINET_IN_H -+# include -+#endif -+#ifdef HAVE_NETDB_H -+# include -+#endif -+#ifdef HAVE_ARPA_INET_H -+# include -+#endif -+#ifdef HAVE_ARPA_NAMESER_H -+# include -+#else -+# include "nameser.h" -+#endif -+#ifdef HAVE_ARPA_NAMESER_COMPAT_H -+# include -+#endif -+ -+#ifdef HAVE_STRINGS_H -+# include -+#endif -+ -+#ifdef HAVE_LIMITS_H -+# include -+#endif -+ -+#include "ares.h" -+#include "ares_dns.h" -+#include "ares_private.h" -+ -+int ares__parse_into_addrinfo2(const unsigned char *abuf, -+ int alen, -+ char **question_hostname, -+ struct ares_addrinfo *ai) -+{ -+ unsigned int qdcount, ancount; -+ int status, i, rr_type, rr_class, rr_len, rr_ttl; -+ int got_a = 0, got_aaaa = 0, got_cname = 0; -+ long len; -+ const unsigned char *aptr; -+ char *hostname, *rr_name = NULL, *rr_data; -+ struct ares_addrinfo_cname *cname, *cnames = NULL; -+ struct ares_addrinfo_node *node, *nodes = NULL; -+ struct sockaddr_in *sin; -+ struct sockaddr_in6 *sin6; -+ -+ *question_hostname = NULL; -+ -+ /* Give up if abuf doesn't have room for a header. */ -+ if (alen < HFIXEDSZ) -+ return ARES_EBADRESP; -+ -+ /* Fetch the question and answer count from the header. */ -+ qdcount = DNS_HEADER_QDCOUNT(abuf); -+ ancount = DNS_HEADER_ANCOUNT(abuf); -+ if (qdcount != 1) -+ return ARES_EBADRESP; -+ -+ -+ /* Expand the name from the question, and skip past the question. */ -+ aptr = abuf + HFIXEDSZ; -+ status = ares__expand_name_for_response(aptr, abuf, alen, question_hostname, &len); -+ if (status != ARES_SUCCESS) -+ return status; -+ if (aptr + len + QFIXEDSZ > abuf + alen) -+ { -+ return ARES_EBADRESP; -+ } -+ -+ hostname = *question_hostname; -+ -+ aptr += len + QFIXEDSZ; -+ -+ /* Examine each answer resource record (RR) in turn. */ -+ for (i = 0; i < (int)ancount; i++) -+ { -+ /* Decode the RR up to the data field. */ -+ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); -+ if (status != ARES_SUCCESS) -+ { -+ rr_name = NULL; -+ goto failed_stat; -+ } -+ -+ aptr += len; -+ if (aptr + RRFIXEDSZ > abuf + alen) -+ { -+ status = ARES_EBADRESP; -+ goto failed_stat; -+ } -+ rr_type = DNS_RR_TYPE(aptr); -+ rr_class = DNS_RR_CLASS(aptr); -+ rr_len = DNS_RR_LEN(aptr); -+ rr_ttl = DNS_RR_TTL(aptr); -+ aptr += RRFIXEDSZ; -+ if (aptr + rr_len > abuf + alen) -+ { -+ status = ARES_EBADRESP; -+ goto failed_stat; -+ } -+ -+ if (rr_class == C_IN && rr_type == T_A -+ && rr_len == sizeof(struct in_addr) -+ && strcasecmp(rr_name, hostname) == 0) -+ { -+ got_a = 1; -+ if (aptr + sizeof(struct in_addr) > abuf + alen) -+ { /* LCOV_EXCL_START: already checked above */ -+ status = ARES_EBADRESP; -+ goto failed_stat; -+ } /* LCOV_EXCL_STOP */ -+ -+ node = ares__append_addrinfo_node(&nodes); -+ if (!node) -+ { -+ status = ARES_ENOMEM; -+ goto failed_stat; -+ } -+ -+ sin = ares_malloc(sizeof(struct sockaddr_in)); -+ if (!sin) -+ { -+ status = ARES_ENOMEM; -+ goto failed_stat; -+ } -+ memset(sin, 0, sizeof(struct sockaddr_in)); -+ memcpy(&sin->sin_addr.s_addr, aptr, sizeof(struct in_addr)); -+ sin->sin_family = AF_INET; -+ -+ node->ai_addr = (struct sockaddr *)sin; -+ node->ai_family = AF_INET; -+ node->ai_addrlen = sizeof(struct sockaddr_in); -+ -+ node->ai_ttl = rr_ttl; -+ -+ status = ARES_SUCCESS; -+ } -+ else if (rr_class == C_IN && rr_type == T_AAAA -+ && rr_len == sizeof(struct ares_in6_addr) -+ && strcasecmp(rr_name, hostname) == 0) -+ { -+ got_aaaa = 1; -+ if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) -+ { /* LCOV_EXCL_START: already checked above */ -+ status = ARES_EBADRESP; -+ goto failed_stat; -+ } /* LCOV_EXCL_STOP */ -+ -+ node = ares__append_addrinfo_node(&nodes); -+ if (!node) -+ { -+ status = ARES_ENOMEM; -+ goto failed_stat; -+ } -+ -+ sin6 = ares_malloc(sizeof(struct sockaddr_in6)); -+ if (!sin6) -+ { -+ status = ARES_ENOMEM; -+ goto failed_stat; -+ } -+ -+ memset(sin6, 0, sizeof(struct sockaddr_in6)); -+ memcpy(&sin6->sin6_addr.s6_addr, aptr, sizeof(struct ares_in6_addr)); -+ sin6->sin6_family = AF_INET6; -+ -+ node->ai_addr = (struct sockaddr *)sin6; -+ node->ai_family = AF_INET6; -+ node->ai_addrlen = sizeof(struct sockaddr_in6); -+ -+ node->ai_ttl = rr_ttl; -+ -+ status = ARES_SUCCESS; -+ } -+ -+ if (rr_class == C_IN && rr_type == T_CNAME) -+ { -+ got_cname = 1; -+ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, -+ &len); -+ if (status != ARES_SUCCESS) -+ { -+ goto failed_stat; -+ } -+ -+ /* Decode the RR data and replace the hostname with it. */ -+ /* SA: Seems wrong as it introduses order dependency. */ -+ hostname = rr_data; -+ -+ cname = ares__append_addrinfo_cname(&cnames); -+ if (!cname) -+ { -+ status = ARES_ENOMEM; -+ ares_free(rr_data); -+ goto failed_stat; -+ } -+ cname->ttl = rr_ttl; -+ cname->alias = rr_name; -+ cname->name = rr_data; -+ } -+ else -+ { -+ ares_free(rr_name); -+ } -+ -+ -+ aptr += rr_len; -+ if (aptr > abuf + alen) -+ { /* LCOV_EXCL_START: already checked above */ -+ status = ARES_EBADRESP; -+ goto failed_stat; -+ } /* LCOV_EXCL_STOP */ -+ } -+ -+ if (status == ARES_SUCCESS) -+ { -+ ares__addrinfo_cat_nodes(&ai->nodes, nodes); -+ if (got_cname) -+ { -+ ares__addrinfo_cat_cnames(&ai->cnames, cnames); -+ return status; -+ } -+ else if (got_a == 0 && got_aaaa == 0) -+ { -+ /* the check for naliases to be zero is to make sure CNAME responses -+ don't get caught here */ -+ status = ARES_ENODATA; -+ } -+ } -+ -+ return status; -+ -+failed_stat: -+ ares_free(rr_name); -+ ares__freeaddrinfo_cnames(cnames); -+ ares__freeaddrinfo_nodes(nodes); -+ return status; -+} -+ -+int ares__parse_into_addrinfo(const unsigned char *abuf, -+ int alen, -+ struct ares_addrinfo *ai) -+{ -+ int status; -+ char *question_hostname; -+ status = ares__parse_into_addrinfo2(abuf, alen, &question_hostname, ai); -+ ares_free(question_hostname); -+ return status; -+} -diff --git a/ares__readaddrinfo.c b/ares__readaddrinfo.c -new file mode 100644 -index 0000000..407ee86 ---- /dev/null -+++ b/ares__readaddrinfo.c -@@ -0,0 +1,261 @@ -+/* Copyright (C) 2019 by Andrew Selivanov -+ * -+ * Permission to use, copy, modify, and distribute this -+ * software and its documentation for any purpose and without -+ * fee is hereby granted, provided that the above copyright -+ * notice appear in all copies and that both that copyright -+ * notice and this permission notice appear in supporting -+ * documentation, and that the name of M.I.T. not be used in -+ * advertising or publicity pertaining to distribution of the -+ * software without specific, written prior permission. -+ * M.I.T. makes no representations about the suitability of -+ * this software for any purpose. It is provided "as is" -+ * without express or implied warranty. -+ */ -+ -+#include "ares_setup.h" -+ -+#ifdef HAVE_NETINET_IN_H -+# include -+#endif -+#ifdef HAVE_NETDB_H -+# include -+#endif -+#ifdef HAVE_ARPA_INET_H -+# include -+#endif -+ -+#include "ares.h" -+#include "ares_inet_net_pton.h" -+#include "ares_nowarn.h" -+#include "ares_private.h" -+ -+#define MAX_ALIASES 40 -+ -+int ares__readaddrinfo(FILE *fp, -+ const char *name, -+ unsigned short port, -+ const struct ares_addrinfo_hints *hints, -+ struct ares_addrinfo *ai) -+{ -+ char *line = NULL, *p, *q; -+ char *txtaddr, *txthost, *txtalias; -+ char *aliases[MAX_ALIASES]; -+ unsigned int i, alias_count; -+ int status; -+ size_t linesize; -+ ares_sockaddr addr; -+ struct ares_addrinfo_cname *cname = NULL, *cnames = NULL; -+ struct ares_addrinfo_node *node = NULL, *nodes = NULL; -+ int match_with_alias, match_with_canonical; -+ int want_cname = hints->ai_flags & ARES_AI_CANONNAME; -+ -+ /* Validate family */ -+ switch (hints->ai_family) { -+ case AF_INET: -+ case AF_INET6: -+ case AF_UNSPEC: -+ break; -+ default: -+ return ARES_EBADFAMILY; -+ } -+ -+ -+ while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) -+ { -+ match_with_alias = 0; -+ match_with_canonical = 0; -+ alias_count = 0; -+ /* Trim line comment. */ -+ p = line; -+ while (*p && (*p != '#')) -+ p++; -+ *p = '\0'; -+ -+ /* Trim trailing whitespace. */ -+ q = p - 1; -+ while ((q >= line) && ISSPACE(*q)) -+ q--; -+ *++q = '\0'; -+ -+ /* Skip leading whitespace. */ -+ p = line; -+ while (*p && ISSPACE(*p)) -+ p++; -+ if (!*p) -+ /* Ignore line if empty. */ -+ continue; -+ -+ /* Pointer to start of IPv4 or IPv6 address part. */ -+ txtaddr = p; -+ -+ /* Advance past address part. */ -+ while (*p && !ISSPACE(*p)) -+ p++; -+ if (!*p) -+ /* Ignore line if reached end of line. */ -+ continue; -+ -+ /* Null terminate address part. */ -+ *p = '\0'; -+ -+ /* Advance to host name */ -+ p++; -+ while (*p && ISSPACE(*p)) -+ p++; -+ if (!*p) -+ /* Ignore line if reached end of line. */ -+ continue; /* LCOV_EXCL_LINE: trailing whitespace already stripped */ -+ -+ /* Pointer to start of host name. */ -+ txthost = p; -+ -+ /* Advance past host name. */ -+ while (*p && !ISSPACE(*p)) -+ p++; -+ -+ /* Pointer to start of first alias. */ -+ txtalias = NULL; -+ if (*p) -+ { -+ q = p + 1; -+ while (*q && ISSPACE(*q)) -+ q++; -+ if (*q) -+ txtalias = q; -+ } -+ -+ /* Null terminate host name. */ -+ *p = '\0'; -+ -+ /* Find out if host name matches with canonical host name. */ -+ if (strcasecmp(txthost, name) == 0) -+ { -+ match_with_canonical = 1; -+ } -+ -+ /* Find out if host name matches with one of the aliases. */ -+ while (txtalias) -+ { -+ p = txtalias; -+ while (*p && !ISSPACE(*p)) -+ p++; -+ q = p; -+ while (*q && ISSPACE(*q)) -+ q++; -+ *p = '\0'; -+ if (strcasecmp(txtalias, name) == 0) -+ { -+ match_with_alias = 1; -+ if (!want_cname) -+ break; -+ } -+ if (alias_count < MAX_ALIASES) -+ { -+ aliases[alias_count++] = txtalias; -+ } -+ txtalias = *q ? q : NULL; -+ } -+ -+ /* Try next line if host does not match. */ -+ if (!match_with_alias && !match_with_canonical) -+ { -+ continue; -+ } -+ -+ /* -+ * Convert address string to network address for the requested families. -+ * Actual address family possible values are AF_INET and AF_INET6 only. -+ */ -+ if ((hints->ai_family == AF_INET) || (hints->ai_family == AF_UNSPEC)) -+ { -+ addr.sa4.sin_port = htons(port); -+ addr.sa4.sin_addr.s_addr = inet_addr(txtaddr); -+ if (addr.sa4.sin_addr.s_addr != INADDR_NONE) -+ { -+ node = ares__append_addrinfo_node(&nodes); -+ if(!node) -+ { -+ goto enomem; -+ } -+ -+ node->ai_family = addr.sa.sa_family = AF_INET; -+ node->ai_addrlen = sizeof(sizeof(addr.sa4)); -+ node->ai_addr = ares_malloc(sizeof(addr.sa4)); -+ if (!node->ai_addr) -+ { -+ goto enomem; -+ } -+ memcpy(node->ai_addr, &addr.sa4, sizeof(addr.sa4)); -+ } -+ } -+ if ((hints->ai_family == AF_INET6) || (hints->ai_family == AF_UNSPEC)) -+ { -+ addr.sa6.sin6_port = htons(port); -+ if (ares_inet_pton(AF_INET6, txtaddr, &addr.sa6.sin6_addr) > 0) -+ { -+ node = ares__append_addrinfo_node(&nodes); -+ if (!node) -+ { -+ goto enomem; -+ } -+ -+ node->ai_family = addr.sa.sa_family = AF_INET6; -+ node->ai_addrlen = sizeof(sizeof(addr.sa6)); -+ node->ai_addr = ares_malloc(sizeof(addr.sa6)); -+ if (!node->ai_addr) -+ { -+ goto enomem; -+ } -+ memcpy(node->ai_addr, &addr.sa6, sizeof(addr.sa6)); -+ } -+ } -+ if (!node) -+ /* Ignore line if invalid address string for the requested family. */ -+ continue; -+ -+ if (want_cname) -+ { -+ for (i = 0; i < alias_count; ++i) -+ { -+ cname = ares__append_addrinfo_cname(&cnames); -+ if (!cname) -+ { -+ goto enomem; -+ } -+ cname->alias = ares_strdup(aliases[i]); -+ cname->name = ares_strdup(txthost); -+ } -+ /* No aliases, cname only. */ -+ if(!alias_count) -+ { -+ cname = ares__append_addrinfo_cname(&cnames); -+ if (!cname) -+ { -+ goto enomem; -+ } -+ cname->name = ares_strdup(txthost); -+ } -+ } -+ } -+ -+ /* Last read failed. */ -+ if (status == ARES_ENOMEM) -+ { -+ goto enomem; -+ } -+ -+ /* Free line buffer. */ -+ ares_free(line); -+ -+ ares__addrinfo_cat_cnames(&ai->cnames, cnames); -+ ares__addrinfo_cat_nodes(&ai->nodes, nodes); -+ -+ return node ? ARES_SUCCESS : ARES_ENOTFOUND; -+ -+enomem: -+ ares_free(line); -+ ares__freeaddrinfo_cnames(cnames); -+ ares__freeaddrinfo_nodes(nodes); -+ return ARES_ENOMEM; -+} -diff --git a/ares__sortaddrinfo.c b/ares__sortaddrinfo.c -index 4d3c8d6..3787409 100644 ---- a/ares__sortaddrinfo.c -+++ b/ares__sortaddrinfo.c -@@ -54,7 +54,7 @@ - - struct addrinfo_sort_elem - { -- struct ares_addrinfo *ai; -+ struct ares_addrinfo_node *ai; - int has_src_addr; - ares_sockaddr src_addr; - int original_order; -@@ -440,9 +440,9 @@ static int find_src_addr(ares_channel channel, - * 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) -+int ares__sortaddrinfo(ares_channel channel, struct ares_addrinfo_node *list_sentinel) - { -- struct ares_addrinfo *cur; -+ struct ares_addrinfo_node *cur; - int nelem = 0, i; - int has_src_addr; - struct addrinfo_sort_elem *elems; -diff --git a/ares_config.h.cmake b/ares_config.h.cmake -index 49f3b21..b76acc1 100644 ---- a/ares_config.h.cmake -+++ b/ares_config.h.cmake -@@ -45,6 +45,9 @@ - /* Specifies the number of arguments to getservbyport_r */ - #define GETSERVBYPORT_R_ARGS @GETSERVBYPORT_R_ARGS@ - -+/* Specifies the number of arguments to getservbyname_r */ -+#define GETSERVBYNAME_R_ARGS @GETSERVBYNAME_R_ARGS@ -+ - /* Define to 1 if you have AF_INET6. */ - #cmakedefine HAVE_AF_INET6 - -@@ -123,6 +126,9 @@ - /* Define to 1 if you have the getservbyport_r function. */ - #cmakedefine HAVE_GETSERVBYPORT_R - -+/* Define to 1 if you have the getservbyname_r function. */ -+#cmakedefine HAVE_GETSERVBYNAME_R -+ - /* Define to 1 if you have the `gettimeofday' function. */ - #cmakedefine HAVE_GETTIMEOFDAY - -diff --git a/ares_freeaddrinfo.3 b/ares_freeaddrinfo.3 -index 8143299..612cb57 100644 ---- a/ares_freeaddrinfo.3 -+++ b/ares_freeaddrinfo.3 -@@ -20,16 +20,18 @@ ares_freeaddrinfo \- Free addrinfo structure allocated by ares functions - .nf - .B #include - .PP --.B void ares_freeaddrinfo(struct addrinfo *\fIai\fP) -+.B void ares_freeaddrinfo(struct ares_addrinfo *\fIai\fP) - .fi - .SH DESCRIPTION - The - .B ares_freeaddrinfo - function frees a --.B struct addrinfo -+.B struct ares_addrinfo - returned in \fIresult\fP of --.B ares_addr_callback -+.B ares_addrinfo_callback - .SH SEE ALSO - .BR ares_getaddrinfo (3), - .SH AUTHOR - Christian Ammer -+.BR -+Andrew Selivanov -diff --git a/ares_freeaddrinfo.c b/ares_freeaddrinfo.c -new file mode 100644 -index 0000000..128f5da ---- /dev/null -+++ b/ares_freeaddrinfo.c -@@ -0,0 +1,57 @@ -+ -+/* Copyright 1998 by the Massachusetts Institute of Technology. -+ * Copyright (C) 2019 by Andrew Selivanov -+ * -+ * Permission to use, copy, modify, and distribute this -+ * software and its documentation for any purpose and without -+ * fee is hereby granted, provided that the above copyright -+ * notice appear in all copies and that both that copyright -+ * notice and this permission notice appear in supporting -+ * documentation, and that the name of M.I.T. not be used in -+ * advertising or publicity pertaining to distribution of the -+ * software without specific, written prior permission. -+ * M.I.T. makes no representations about the suitability of -+ * this software for any purpose. It is provided "as is" -+ * without express or implied warranty. -+ */ -+ -+#include "ares_setup.h" -+ -+#ifdef HAVE_NETDB_H -+# include -+#endif -+ -+#include "ares.h" -+#include "ares_private.h" -+ -+void ares__freeaddrinfo_cnames(struct ares_addrinfo_cname *head) -+{ -+ struct ares_addrinfo_cname *current; -+ while (head) -+ { -+ current = head; -+ head = head->next; -+ ares_free(current->alias); -+ ares_free(current->name); -+ ares_free(current); -+ } -+} -+ -+void ares__freeaddrinfo_nodes(struct ares_addrinfo_node *head) -+{ -+ struct ares_addrinfo_node *current; -+ while (head) -+ { -+ current = head; -+ head = head->ai_next; -+ ares_free(current->ai_addr); -+ ares_free(current); -+ } -+} -+ -+void ares_freeaddrinfo(struct ares_addrinfo *ai) -+{ -+ ares__freeaddrinfo_cnames(ai->cnames); -+ ares__freeaddrinfo_nodes(ai->nodes); -+ ares_free(ai); -+} -diff --git a/ares_getaddrinfo.3 b/ares_getaddrinfo.3 -index 0089227..d0f30ae 100644 ---- a/ares_getaddrinfo.3 -+++ b/ares_getaddrinfo.3 -@@ -20,12 +20,12 @@ ares_getaddrinfo \- Initiate a host query by name - .nf - .B #include - .PP --.B typedef void (*ares_addr_callback)(void *\fIarg\fP, int \fIstatus\fP, -+.B typedef void (*ares_addrinfo_callback)(void *\fIarg\fP, int \fIstatus\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, --.B ares_addr_callback \fIcallback\fP, void *\fIarg\fP) -+.B ares_addrinfo_callback \fIcallback\fP, void *\fIarg\fP) - .PP - .B struct ares_addrinfo { - .B int \fIai_flags\fP; -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index c9db8a2..86c9e71 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -1,6 +1,7 @@ - - /* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology. - * Copyright (C) 2017 - 2018 by Christian Ammer -+ * Copyright (C) 2019 by Andrew Selivanov - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without -@@ -17,6 +18,13 @@ - - #include "ares_setup.h" - -+#ifdef HAVE_GETSERVBYNAME_R -+# if !defined(GETSERVBYNAME_R_ARGS) || \ -+ (GETSERVBYNAME_R_ARGS < 4) || (GETSERVBYNAME_R_ARGS > 6) -+# error "you MUST specifiy a valid number of arguments for getservbyname_r" -+# endif -+#endif -+ - #ifdef HAVE_NETINET_IN_H - # include - #endif -@@ -51,442 +59,623 @@ - # include "ares_platform.h" - #endif - --struct host_query { -- /* Arguments passed to ares_getaddrinfo */ -+struct host_query -+{ - ares_channel channel; - char *name; -- ares_addr_callback callback; -+ unsigned short port; /* in host order */ -+ ares_addrinfo_callback callback; - void *arg; -- int sent_family; /* this family is what was is being used */ -- int ai_family; /* this family is what is asked for in the API */ -- int timeouts; /* number of timeouts we saw for this request */ -- int next_domain; /* next search domain to try */ -- int single_domain; /* do not check other domains */ -- int status; -- int remaining; -- struct ares_addrinfo* ai; -+ struct ares_addrinfo_hints hints; -+ int sent_family; /* this family is what was is being used */ -+ int timeouts; /* number of timeouts we saw for this request */ -+ const char *remaining_lookups; /* types of lookup we need to perform ("fb" by -+ default, file and dns respectively) */ -+ struct ares_addrinfo *ai; /* store results between lookups */ -+}; -+ -+static const struct ares_addrinfo_hints default_hints = { -+ 0, /* ai_flags */ -+ AF_UNSPEC, /* ai_family */ -+ 0, /* ai_socktype */ -+ 0, /* ai_protocol */ -+}; -+ -+static const struct ares_addrinfo_cname empty_addrinfo_cname = { -+ INT_MAX, /* ttl */ -+ NULL, /* alias */ -+ NULL, /* name */ -+ NULL, /* next */ -+}; -+ -+static const struct ares_addrinfo_node empty_addrinfo_node = { -+ 0, /* ai_ttl */ -+ 0, /* ai_flags */ -+ 0, /* ai_family */ -+ 0, /* ai_socktype */ -+ 0, /* ai_protocol */ -+ 0, /* ai_addrlen */ -+ NULL, /* ai_addr */ -+ NULL /* ai_next */ -+}; -+ -+static const struct ares_addrinfo empty_addrinfo = { -+ NULL, /* cnames */ -+ NULL /* nodes */ - }; - - static void host_callback(void *arg, int status, int timeouts, - unsigned char *abuf, int alen); --static void end_hquery(struct host_query *hquery, int status); --static int file_lookup(const char *name, int family, -- struct ares_addrinfo **ai); --static void sort_addresses(struct hostent *host, -- const struct apattern *sortlist, int nsort); --static void sort6_addresses(struct hostent *host, -- const struct apattern *sortlist, int nsort); --static int get_address_index(const struct in_addr *addr, -- const struct apattern *sortlist, int nsort); --static int get6_address_index(const struct ares_in6_addr *addr, -- const struct apattern *sortlist, int nsort); --static int as_is_first(const struct host_query *hquery); --static int add_to_addrinfo(struct ares_addrinfo** ai, -- const struct hostent* host); --static void next_dns_lookup(struct host_query *hquery); --static int is_implemented(const int family); - -+struct ares_addrinfo_cname *ares__malloc_addrinfo_cname() -+{ -+ struct ares_addrinfo_cname *cname = ares_malloc(sizeof(struct ares_addrinfo_cname)); -+ if (!cname) -+ return NULL; - --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; -+ *cname = empty_addrinfo_cname; -+ return cname; -+} - -- ai_family = hints ? hints->ai_family : AF_UNSPEC; -- if (!is_implemented(ai_family)) { -- callback(arg, ARES_ENOTIMP, 0, NULL); -- return; -- } -+struct ares_addrinfo_cname *ares__append_addrinfo_cname(struct ares_addrinfo_cname **head) -+{ -+ struct ares_addrinfo_cname *tail = ares__malloc_addrinfo_cname(); -+ struct ares_addrinfo_cname *last = *head; -+ if (!last) -+ { -+ *head = tail; -+ return tail; -+ } - -- /* Allocate and fill in the host query structure. */ -- hquery = ares_malloc(sizeof(struct host_query)); -- if (!hquery) { -- callback(arg, ARES_ENOMEM, 0, NULL); -- return; -- } -- hquery->ai = NULL; -- hquery->channel = channel; -- hquery->name = single != NULL ? single : ares_strdup(node); -- hquery->single_domain = single != NULL; -- hquery->ai_family = ai_family; -- hquery->sent_family = -1; /* nothing is sent yet */ -- if (!hquery->name) { -- ares_free(hquery); -- callback(arg, ARES_ENOMEM, 0, NULL); -- return; -- } -- hquery->callback = callback; -- hquery->arg = arg; -- hquery->timeouts = 0; -- hquery->next_domain = -1; /* see next_dns_lookup for more info */ -- hquery->remaining = 0; -- -- /* 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 { -- next_dns_lookup(hquery); -- } -+ while (last->next) -+ { -+ last = last->next; -+ } -+ -+ last->next = tail; -+ return tail; - } - --void ares_freeaddrinfo(struct ares_addrinfo* ai) { -- struct ares_addrinfo* ai_free; -- while (ai) { -- ai_free = ai; -- ai = ai->ai_next; -- ares_free(ai_free->ai_addr); -- ares_free(ai_free); -- } -+void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head, -+ struct ares_addrinfo_cname *tail) -+{ -+ struct ares_addrinfo_cname *last = *head; -+ if (!last) -+ { -+ *head = tail; -+ return; -+ } -+ -+ while (last->next) -+ { -+ last = last->next; -+ } -+ -+ last->next = tail; - } - --static int is_implemented(const int family) { -- return -- family == AF_INET || -- family == AF_INET6 || -- family == AF_UNSPEC; -+struct ares_addrinfo *ares__malloc_addrinfo() -+{ -+ struct ares_addrinfo *ai = ares_malloc(sizeof(struct ares_addrinfo)); -+ if (!ai) -+ return NULL; -+ -+ *ai = empty_addrinfo; -+ return ai; - } - --static int file_lookup(const char *name, int family, struct ares_addrinfo **ai) { -- FILE *fp; -- char **alias; -- int status; -- int error; -- struct hostent *host = NULL; -+struct ares_addrinfo_node *ares__malloc_addrinfo_node() -+{ -+ struct ares_addrinfo_node *node = -+ ares_malloc(sizeof(struct ares_addrinfo_node)); -+ if (!node) -+ return NULL; - --#ifdef WIN32 -- char PATH_HOSTS[MAX_PATH]; -- win_platform platform; -+ *node = empty_addrinfo_node; -+ return node; -+} - -- PATH_HOSTS[0] = '\0'; -+/* Allocate new addrinfo and append to the tail. */ -+struct ares_addrinfo_node *ares__append_addrinfo_node(struct ares_addrinfo_node **head) -+{ -+ struct ares_addrinfo_node *tail = ares__malloc_addrinfo_node(); -+ struct ares_addrinfo_node *last = *head; -+ if (!last) -+ { -+ *head = tail; -+ return tail; -+ } - -- platform = ares__getplatform(); -+ while (last->ai_next) -+ { -+ last = last->ai_next; -+ } - -- if (platform == WIN_NT) { -- char tmp[MAX_PATH]; -- HKEY hkeyHosts; -+ last->ai_next = tail; -+ return tail; -+} - -- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, -- &hkeyHosts) == ERROR_SUCCESS) { -- DWORD dwLength = MAX_PATH; -- RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, -- &dwLength); -- ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); -- RegCloseKey(hkeyHosts); -+void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head, -+ struct ares_addrinfo_node *tail) -+{ -+ struct ares_addrinfo_node *last = *head; -+ if (!last) -+ { -+ *head = tail; -+ return; - } -- } -- else if (platform == WIN_9X) { -- GetWindowsDirectory(PATH_HOSTS, MAX_PATH); -- } -- else { -- return ARES_ENOTFOUND; -- } - -- strcat(PATH_HOSTS, WIN_PATH_HOSTS); -+ while (last->ai_next) -+ { -+ last = last->ai_next; -+ } - --#elif defined(WATT32) -- extern const char *_w32_GetHostsFile (void); -- const char *PATH_HOSTS = _w32_GetHostsFile(); -+ last->ai_next = tail; -+} - -- if (!PATH_HOSTS) { -- return ARES_ENOTFOUND; -- } -+/* Resolve service name into port number given in host byte order. -+ * If not resolved, return 0. -+ */ -+static unsigned short lookup_service(const char *service, int flags) -+{ -+ const char *proto; -+ struct servent *sep; -+#ifdef HAVE_GETSERVBYNAME_R -+ struct servent se; -+ char tmpbuf[4096]; - #endif - -- fp = fopen(PATH_HOSTS, "r"); -- if (!fp) { -- error = ERRNO; -- switch(error) { -- case ENOENT: -- case ESRCH: -- return ARES_ENOTFOUND; -- default: -- DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", -- error, strerror(error))); -- DEBUGF(fprintf(stderr, "Error opening file: %s\n", -- PATH_HOSTS)); -- host = NULL; -- return ARES_EFILE; -- } -- } -- status = ARES_ENOTFOUND; -- while (status != ARES_ENOMEM && -- ares__get_hostent(fp, family, &host) == ARES_SUCCESS) { -- if (strcasecmp(host->h_name, name) == 0) { -- status = add_to_addrinfo(ai, host); -- } -- else { -- for (alias = host->h_aliases; *alias; alias++) { -- if (strcasecmp(*alias, name) == 0) { -- status = add_to_addrinfo(ai, host); -- break; -- } -- } -+ if (service) -+ { -+ if (flags & ARES_NI_UDP) -+ proto = "udp"; -+ else if (flags & ARES_NI_SCTP) -+ proto = "sctp"; -+ else if (flags & ARES_NI_DCCP) -+ proto = "dccp"; -+ else -+ proto = "tcp"; -+#ifdef HAVE_GETSERVBYNAME_R -+ memset(&se, 0, sizeof(se)); -+ sep = &se; -+ memset(tmpbuf, 0, sizeof(tmpbuf)); -+#if GETSERVBYNAME_R_ARGS == 6 -+ if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), -+ &sep) != 0) -+ sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */ -+#elif GETSERVBYNAME_R_ARGS == 5 -+ sep = -+ getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf)); -+#elif GETSERVBYNAME_R_ARGS == 4 -+ if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) -+ sep = NULL; -+#else -+ /* Lets just hope the OS uses TLS! */ -+ sep = getservbyname(service, proto); -+#endif -+#else -+ /* Lets just hope the OS uses TLS! */ -+#if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) -+ sep = getservbyname(service, (char *)proto); -+#else -+ sep = getservbyname(service, proto); -+#endif -+#endif -+ return (sep ? ntohs((unsigned short)sep->s_port) : 0); - } -- ares_free_hostent(host); -- } -- fclose(fp); -- return status; -+ return 0; - } - --static int add_to_addrinfo(struct ares_addrinfo** ai, -- const struct hostent* host) { -- static const struct ares_addrinfo EmptyAddrinfo; -- struct ares_addrinfo* front; -- char** p; -- if (!host || (host->h_addrtype != AF_INET && host->h_addrtype != AF_INET6)) { -- return ARES_SUCCESS; -- } -- for (p = host->h_addr_list; *p; ++p) { -- front = ares_malloc(sizeof(struct ares_addrinfo)); -- if (!front) goto nomem; -- *front = EmptyAddrinfo; -- front->ai_next = *ai; /* insert at front */ -- *ai = front; -- if (host->h_addrtype == AF_INET) { -- front->ai_protocol = IPPROTO_UDP; -- 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); -- } -- else { -- front->ai_protocol = IPPROTO_UDP; -- 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); -+/* If the name looks like an IP address or an error occured, -+ * fake up a host entry, end the query immediately, and return true. -+ * Otherwise return false. -+ */ -+static int fake_addrinfo(const char *name, -+ unsigned short port, -+ const struct ares_addrinfo_hints *hints, -+ struct ares_addrinfo *ai, -+ ares_addrinfo_callback callback, -+ void *arg) -+{ -+ struct ares_addrinfo_cname *cname; -+ struct ares_addrinfo_node *node; -+ ares_sockaddr addr; -+ size_t addrlen; -+ int result = 0; -+ int family = hints->ai_family; -+ if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) -+ { -+ /* It only looks like an IP address if it's all numbers and dots. */ -+ int numdots = 0, valid = 1; -+ const char *p; -+ for (p = name; *p; p++) -+ { -+ if (!ISDIGIT(*p) && *p != '.') -+ { -+ valid = 0; -+ break; -+ } -+ else if (*p == '.') -+ { -+ numdots++; -+ } -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ -+ /* if we don't have 3 dots, it is illegal -+ * (although inet_addr doesn't think so). -+ */ -+ if (numdots != 3 || !valid) -+ result = 0; -+ else -+ result = -+ ((addr.sa4.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE ? 0 -+ : 1); -+ -+ if (result) -+ { -+ family = addr.sa.sa_family = AF_INET; -+ addr.sa4.sin_port = htons(port); -+ addrlen = sizeof(addr.sa4); -+ } - } -- } -- return ARES_SUCCESS; --nomem: -- ares_freeaddrinfo(*ai); -- return ARES_ENOMEM; --} - --static void next_dns_lookup(struct host_query *hquery) { -- char *s = NULL; -- int is_s_allocated = 0; -- int status; -- /* if next_domain == -1 and as_is_first is true, try hquery->name */ -- if(hquery->next_domain == -1) { -- if(as_is_first(hquery)) { -- s = hquery->name; -+ if (family == AF_INET6 || family == AF_UNSPEC) -+ { -+ result = -+ (ares_inet_pton(AF_INET6, name, &addr.sa6.sin6_addr) < 1 ? 0 : 1); -+ addr.sa6.sin6_family = AF_INET6; -+ addr.sa6.sin6_port = htons(port); -+ addrlen = sizeof(addr.sa6); - } -- hquery->next_domain = 0; -- } -- /* if as_is_first is false, try hquery->name at last */ -- if(!s && hquery->next_domain == hquery->channel->ndomains) { -- if(!as_is_first(hquery)) { -- s = hquery->name; -+ -+ if (!result) -+ return 0; -+ -+ node = ares__malloc_addrinfo_node(); -+ if (!node) -+ { -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return 1; - } -- /* avoid infinite loop */ -- hquery->next_domain++; -- } -- -- if (!s && hquery->next_domain < hquery->channel->ndomains) { -- status = ares__cat_domain( -- hquery->name, -- hquery->channel->domains[hquery->next_domain++], -- &s); -- if (status == ARES_SUCCESS) { -- is_s_allocated = 1; -+ -+ ai->nodes = node; -+ -+ node->ai_addr = ares_malloc(addrlen); -+ if (!node->ai_addr) -+ { -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return 1; - } -- } - -- if (s) { -- if (hquery->ai_family == AF_INET || hquery->ai_family == AF_UNSPEC) { -- hquery->remaining++; -- ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); -+ node->ai_addrlen = (unsigned int)addrlen; -+ node->ai_family = addr.sa.sa_family; -+ if (addr.sa.sa_family == AF_INET) -+ memcpy(node->ai_addr, &addr.sa4, sizeof(addr.sa4)); -+ else -+ memcpy(node->ai_addr, &addr.sa6, sizeof(addr.sa6)); -+ -+ if (hints->ai_flags & ARES_AI_CANONNAME) -+ { -+ cname = ares__append_addrinfo_cname(&ai->cnames); -+ if (!cname) -+ { -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return 1; -+ } -+ -+ /* Duplicate the name, to avoid a constness violation. */ -+ cname->name = ares_strdup(name); -+ if (!cname->name) -+ { -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return 1; -+ } - } -- if (hquery->ai_family == AF_INET6 || hquery->ai_family == AF_UNSPEC) { -- hquery->remaining++; -- ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); -+ -+ callback(arg, ARES_SUCCESS, 0, ai); -+ return 1; -+} -+ -+static void end_hquery(struct host_query *hquery, int status) -+{ -+ struct ares_addrinfo_node sentinel; -+ struct ares_addrinfo_node *next; -+ if (status == ARES_SUCCESS) -+ { -+ if (!(hquery->hints.ai_flags & ARES_AI_NOSORT)) -+ { -+ sentinel.ai_next = hquery->ai->nodes; -+ ares__sortaddrinfo(hquery->channel, &sentinel); -+ hquery->ai->nodes = sentinel.ai_next; -+ } -+ next = hquery->ai->nodes; -+ /* Set port into each address (resolved separately). */ -+ while (next) -+ { -+ if (next->ai_family == AF_INET) -+ { -+ ((struct sockaddr_in *)next->ai_addr)->sin_port = htons(hquery->port); -+ } -+ else -+ { -+ ((struct sockaddr_in6 *)next->ai_addr)->sin6_port = htons(hquery->port); -+ } -+ next = next->ai_next; -+ } - } -- if (is_s_allocated) { -- ares_free(s); -+ else -+ { -+ /* Clean up what we have collected by so far. */ -+ ares_freeaddrinfo(hquery->ai); -+ hquery->ai = NULL; - } -- } -- else { -- assert(!hquery->ai); -- end_hquery(hquery, ARES_ENOTFOUND); -- } --} - --static void end_hquery(struct host_query *hquery, int status) { - hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai); - ares_free(hquery->name); - ares_free(hquery); - } - --static void host_callback(void *arg, int status, int timeouts, -- unsigned char *abuf, int alen) { -- struct host_query *hquery = (struct host_query*)arg; -- ares_channel channel = hquery->channel; -- struct hostent *host = NULL; -- int qtype; -- int qtypestatus; -- int addinfostatus = ARES_SUCCESS; -- hquery->timeouts += timeouts; -+static int file_lookup(struct host_query *hquery) -+{ -+ FILE *fp; -+ int error; -+ int status; -+ const char *path_hosts = NULL; - -- hquery->remaining--; -- -- if (status == ARES_SUCCESS) { -- qtypestatus = ares__parse_qtype_reply(abuf, alen, &qtype); -- if (qtypestatus == ARES_SUCCESS && qtype == T_A) { -- /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ -- ares_parse_a_reply(abuf, alen, &host, NULL, NULL); -- if (host && channel->nsort) { -- sort_addresses(host, channel->sortlist, channel->nsort); -- } -- addinfostatus = add_to_addrinfo(&hquery->ai, host); -- ares_free_hostent(host); -- } -- else if (qtypestatus == ARES_SUCCESS && qtype == T_AAAA) { -- /* Can ares_parse_a_reply be unsuccessful (after parse_qtype) */ -- ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); -- if (host && channel->nsort) { -- sort6_addresses(host, channel->sortlist, channel->nsort); -- } -- addinfostatus = add_to_addrinfo(&hquery->ai, host); -- ares_free_hostent(host); -+ if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) -+ { -+ path_hosts = getenv("CARES_HOSTS"); - } -- } - -- if (!hquery->remaining) { -- if (addinfostatus != ARES_SUCCESS) { -- /* no memory */ -- end_hquery(hquery, addinfostatus); -+ if (!path_hosts) -+ { -+#ifdef WIN32 -+ char PATH_HOSTS[MAX_PATH]; -+ win_platform platform; -+ -+ PATH_HOSTS[0] = '\0'; -+ -+ platform = ares__getplatform(); -+ -+ if (platform == WIN_NT) -+ { -+ char tmp[MAX_PATH]; -+ HKEY hkeyHosts; -+ -+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, -+ &hkeyHosts) == ERROR_SUCCESS) -+ { -+ DWORD dwLength = MAX_PATH; -+ RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, -+ &dwLength); -+ ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); -+ RegCloseKey(hkeyHosts); -+ } -+ } -+ else if (platform == WIN_9X) -+ GetWindowsDirectory(PATH_HOSTS, MAX_PATH); -+ else -+ return ARES_ENOTFOUND; -+ -+ strcat(PATH_HOSTS, WIN_PATH_HOSTS); -+ path_hosts = PATH_HOSTS; -+ -+#elif defined(WATT32) -+ const char *PATH_HOSTS = _w32_GetHostsFile(); -+ -+ if (!PATH_HOSTS) -+ return ARES_ENOTFOUND; -+#endif -+ path_hosts = PATH_HOSTS; - } -- else if (hquery->ai) { -- /* at least one query ended with ARES_SUCCESS */ -- end_hquery(hquery, ARES_SUCCESS); -+ -+ fp = fopen(path_hosts, "r"); -+ if (!fp) -+ { -+ error = ERRNO; -+ switch (error) -+ { -+ case ENOENT: -+ case ESRCH: -+ return ARES_ENOTFOUND; -+ default: -+ DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, -+ strerror(error))); -+ DEBUGF(fprintf(stderr, "Error opening file: %s\n", path_hosts)); -+ return ARES_EFILE; -+ } - } -- else if (status == ARES_ENOTFOUND) { -- next_dns_lookup(hquery); -+ status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai); -+ fclose(fp); -+ return status; -+} -+ -+static void next_lookup(struct host_query *hquery, int status_code) -+{ -+ const char *p; -+ int status = status_code; -+ -+ for (p = hquery->remaining_lookups; *p; p++) -+ { -+ switch (*p) -+ { -+ case 'b': -+ /* DNS lookup */ -+ hquery->remaining_lookups = p + 1; -+ if ((hquery->hints.ai_family == AF_INET6) || -+ (hquery->hints.ai_family == AF_UNSPEC)) { -+ /* if inet6 or unspec, start out with AAAA */ -+ hquery->sent_family = AF_INET6; -+ ares_search(hquery->channel, hquery->name, C_IN, T_AAAA, -+ host_callback, hquery); -+ } -+ else { -+ hquery->sent_family = AF_INET; -+ ares_search(hquery->channel, hquery->name, C_IN, T_A, -+ host_callback, hquery); -+ } -+ return; -+ -+ case 'f': -+ /* Host file lookup */ -+ status = file_lookup(hquery); -+ -+ /* this status check below previously checked for !ARES_ENOTFOUND, -+ but we should not assume that this single error code is the one -+ that can occur, as that is in fact no longer the case */ -+ if (status == ARES_SUCCESS) -+ { -+ end_hquery(hquery, status); -+ return; -+ } -+ status = status_code; /* Use original status code */ -+ break; -+ } - } -- else { -+ end_hquery(hquery, status); -+} -+ -+static void host_callback(void *arg, int status, int timeouts, -+ unsigned char *abuf, int alen) -+{ -+ struct host_query *hquery = (struct host_query *) arg; -+ hquery->timeouts += timeouts; -+ if (status == ARES_SUCCESS) -+ { -+ if (hquery->sent_family == AF_INET) -+ { -+ status = ares__parse_into_addrinfo(abuf, alen, hquery->ai); -+ } -+ else if (hquery->sent_family == AF_INET6) -+ { -+ status = ares__parse_into_addrinfo(abuf, alen, hquery->ai); -+ if (hquery->hints.ai_family == AF_UNSPEC) -+ { -+ /* Now look for A records and append them to existing results. */ -+ hquery->sent_family = AF_INET; -+ ares_search(hquery->channel, hquery->name, C_IN, T_A, -+ host_callback, hquery); -+ return; -+ } -+ } - end_hquery(hquery, status); - } -- } -- -- /* at this point we keep on waiting for the next query to finish */ -+ else if ((status == ARES_ENODATA || status == ARES_ESERVFAIL || -+ status == ARES_ECONNREFUSED || status == ARES_EBADRESP || -+ status == ARES_ETIMEOUT) && -+ (hquery->sent_family == AF_INET6 && -+ hquery->hints.ai_family == AF_UNSPEC)) -+ { -+ /* The AAAA query yielded no useful result. Now look up an A instead. */ -+ hquery->sent_family = AF_INET; -+ ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback, -+ hquery); -+ } -+ else if (status == ARES_EDESTRUCTION) -+ end_hquery(hquery, status); -+ else -+ next_lookup(hquery, status); - } - --static void sort_addresses(struct hostent *host, -- const struct apattern *sortlist, int nsort) { -- struct in_addr a1, a2; -- int i1, i2, ind1, ind2; -- -- /* This is a simple insertion sort, not optimized at all. i1 walks -- * through the address list, with the loop invariant that everything -- * to the left of i1 is sorted. In the loop body, the value at i1 is moved -- * back through the list (via i2) until it is in sorted order. -- */ -- for (i1 = 0; host->h_addr_list[i1]; i1++) { -- memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr)); -- ind1 = get_address_index(&a1, sortlist, nsort); -- for (i2 = i1 - 1; i2 >= 0; i2--) { -- memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr)); -- ind2 = get_address_index(&a2, sortlist, nsort); -- if (ind2 <= ind1) { -- break; -- } -- memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr)); -+void ares_getaddrinfo(ares_channel channel, -+ const char* name, const char* service, -+ const struct ares_addrinfo_hints* hints, -+ ares_addrinfo_callback callback, void* arg) -+{ -+ struct host_query *hquery; -+ unsigned short port = 0; -+ int family; -+ struct ares_addrinfo *ai; -+ -+ if (!hints) -+ { -+ hints = &default_hints; - } -- memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr)); -- } --} - --/* Find the first entry in sortlist which matches addr. Return nsort -- * if none of them match. -- */ --static int get_address_index(const struct in_addr *addr, -- const struct apattern *sortlist, -- int nsort) { -- int i; -- -- for (i = 0; i < nsort; i++) { -- if (sortlist[i].family != AF_INET) { -- continue; -+ family = hints->ai_family; -+ -+ /* Right now we only know how to look up Internet addresses -+ and unspec means try both basically. */ -+ if (family != AF_INET && -+ family != AF_INET6 && -+ family != AF_UNSPEC) -+ { -+ callback(arg, ARES_ENOTIMP, 0, NULL); -+ return; - } -- if (sortlist[i].type == PATTERN_MASK) { -- if ((addr->s_addr & sortlist[i].mask.addr4.s_addr) == -- sortlist[i].addrV4.s_addr) { -- break; -- } -+ -+ if (service) -+ { -+ if (hints->ai_flags & ARES_AI_NUMERICSERV) -+ { -+ port = (unsigned short)strtoul(service, NULL, 0); -+ if (!port) -+ { -+ callback(arg, ARES_ESERVICE, 0, NULL); -+ return; -+ } -+ } -+ else -+ { -+ port = lookup_service(service, 0); -+ if (!port) -+ { -+ port = (unsigned short)strtoul(service, NULL, 0); -+ if (!port) -+ { -+ callback(arg, ARES_ESERVICE, 0, NULL); -+ return; -+ } -+ } -+ } - } -- else { -- if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr, -- sortlist[i].mask.bits)) { -- break; -- } -+ -+ ai = ares__malloc_addrinfo(); -+ if (!ai) -+ { -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return; - } -- } -- return i; --} - --static void sort6_addresses(struct hostent *host, -- const struct apattern *sortlist, int nsort) { -- struct ares_in6_addr a1, a2; -- int i1, i2, ind1, ind2; -- -- /* This is a simple insertion sort, not optimized at all. i1 walks -- * through the address list, with the loop invariant that everything -- * to the left of i1 is sorted. In the loop body, the value at i1 is moved -- * back through the list (via i2) until it is in sorted order. -- */ -- for (i1 = 0; host->h_addr_list[i1]; i1++) { -- memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr)); -- ind1 = get6_address_index(&a1, sortlist, nsort); -- for (i2 = i1 - 1; i2 >= 0; i2--) { -- memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr)); -- ind2 = get6_address_index(&a2, sortlist, nsort); -- if (ind2 <= ind1) { -- break; -- } -- memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr)); -+ if (fake_addrinfo(name, port, hints, ai, callback, arg)) -+ { -+ return; - } -- memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr)); -- } --} - --/* Find the first entry in sortlist which matches addr. Return nsort -- * if none of them match. -- */ --static int get6_address_index(const struct ares_in6_addr *addr, -- const struct apattern *sortlist, -- int nsort) { -- int i; -- -- for (i = 0; i < nsort; i++) { -- if (sortlist[i].family != AF_INET6) -- continue; -- if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits)) -- break; -- } -- return i; --} -+ /* Allocate and fill in the host query structure. */ -+ hquery = ares_malloc(sizeof(struct host_query)); -+ if (!hquery) -+ { -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return; -+ } - --static int as_is_first(const struct host_query* hquery) { -- char* p; -- int ndots = 0; -- for (p = hquery->name; *p; p++) { -- if (*p == '.') { -- ndots++; -+ hquery->name = ares_strdup(name); -+ if (!hquery->name) -+ { -+ ares_free(hquery); -+ ares_freeaddrinfo(ai); -+ callback(arg, ARES_ENOMEM, 0, NULL); -+ return; - } -- } -- return ndots >= hquery->channel->ndots; --} - -+ hquery->port = port; -+ hquery->channel = channel; -+ hquery->hints = *hints; -+ hquery->sent_family = -1; /* nothing is sent yet */ -+ hquery->callback = callback; -+ hquery->arg = arg; -+ hquery->remaining_lookups = channel->lookups; -+ hquery->timeouts = 0; -+ hquery->ai = ai; -+ -+ /* Start performing lookups according to channel->lookups. */ -+ next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */); -+} -diff --git a/ares_parse_a_reply.3 b/ares_parse_a_reply.3 -index 8db8ed9..8e4908a 100644 ---- a/ares_parse_a_reply.3 -+++ b/ares_parse_a_reply.3 -@@ -75,4 +75,6 @@ Memory was exhausted. - .SH AUTHOR - Greg Hudson, MIT Information Systems - .br -+Andrew Selivanov -+.br - Copyright 1998 by the Massachusetts Institute of Technology. -diff --git a/ares_parse_a_reply.c b/ares_parse_a_reply.c -index 4fb6d14..8c8da83 100644 ---- a/ares_parse_a_reply.c -+++ b/ares_parse_a_reply.c -@@ -1,5 +1,6 @@ - - /* Copyright 1998 by the Massachusetts Institute of Technology. -+ * Copyright (C) 2019 by Andrew Selivanov - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without -@@ -50,253 +51,145 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, - struct hostent **host, - struct ares_addrttl *addrttls, int *naddrttls) - { -- unsigned int qdcount, ancount; -- int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; -- int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ -- int naliases; -- long len; -- const unsigned char *aptr; -- char *hostname, *rr_name, *rr_data, **aliases; -- struct in_addr *addrs; -- struct hostent *hostent; -- const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; -- -- /* Set *host to NULL for all failure cases. */ -- if (host) -- *host = NULL; -- /* Same with *naddrttls. */ -- if (naddrttls) -- *naddrttls = 0; -+ struct ares_addrinfo ai; -+ struct ares_addrinfo_node *next; -+ struct ares_addrinfo_cname *next_cname; -+ char **aliases = NULL; -+ char *question_hostname = NULL; -+ struct hostent *hostent = NULL; -+ struct in_addr *addrs = NULL; -+ int naliases = 0, naddrs = 0, alias = 0, i; -+ int cname_ttl = INT_MAX; -+ int status; -+ -+ memset(&ai, 0, sizeof(ai)); -+ -+ status = ares__parse_into_addrinfo2(abuf, alen, &question_hostname, &ai); -+ if (status != ARES_SUCCESS) -+ { -+ ares_free(question_hostname); - -- /* Give up if abuf doesn't have room for a header. */ -- if (alen < HFIXEDSZ) -- return ARES_EBADRESP; -+ if (naddrttls) -+ { -+ *naddrttls = naddrs; -+ } - -- /* Fetch the question and answer count from the header. */ -- qdcount = DNS_HEADER_QDCOUNT(abuf); -- ancount = DNS_HEADER_ANCOUNT(abuf); -- if (qdcount != 1) -- return ARES_EBADRESP; -+ return status; -+ } - -- /* Expand the name from the question, and skip past the question. */ -- aptr = abuf + HFIXEDSZ; -- status = ares__expand_name_for_response(aptr, abuf, alen, &hostname, &len); -- if (status != ARES_SUCCESS) -- return status; -- if (aptr + len + QFIXEDSZ > abuf + alen) -+ hostent = ares_malloc(sizeof(struct hostent)); -+ if (!hostent) - { -- ares_free(hostname); -- return ARES_EBADRESP; -+ goto enomem; - } -- aptr += len + QFIXEDSZ; - -- if (host) -+ next = ai.nodes; -+ while (next) - { -- /* Allocate addresses and aliases; ancount gives an upper bound for -- both. */ -- addrs = ares_malloc(ancount * sizeof(struct in_addr)); -- if (!addrs) -- { -- ares_free(hostname); -- return ARES_ENOMEM; -- } -- aliases = ares_malloc((ancount + 1) * sizeof(char *)); -- if (!aliases) -- { -- ares_free(hostname); -- ares_free(addrs); -- return ARES_ENOMEM; -- } -+ ++naddrs; -+ next = next->ai_next; - } -- else -+ -+ next_cname = ai.cnames; -+ while (next_cname) - { -- addrs = NULL; -- aliases = NULL; -+ if(next_cname->alias) -+ ++naliases; -+ next_cname = next_cname->next; - } - -- naddrs = 0; -- naliases = 0; -- -- /* Examine each answer resource record (RR) in turn. */ -- for (i = 0; i < (int)ancount; i++) -+ aliases = ares_malloc((naliases + 1) * sizeof(char *)); -+ if (!aliases) - { -- /* Decode the RR up to the data field. */ -- status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); -- if (status != ARES_SUCCESS) -- break; -- aptr += len; -- if (aptr + RRFIXEDSZ > abuf + alen) -- { -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } -- rr_type = DNS_RR_TYPE(aptr); -- rr_class = DNS_RR_CLASS(aptr); -- rr_len = DNS_RR_LEN(aptr); -- rr_ttl = DNS_RR_TTL(aptr); -- aptr += RRFIXEDSZ; -- if (aptr + rr_len > abuf + alen) -- { -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } -+ goto enomem; -+ } - -- if (rr_class == C_IN && rr_type == T_A -- && rr_len == sizeof(struct in_addr) -- && strcasecmp(rr_name, hostname) == 0) -+ if (naliases) -+ { -+ next_cname = ai.cnames; -+ while (next_cname) - { -- if (addrs) -- { -- if (aptr + sizeof(struct in_addr) > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -- memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr)); -- } -- if (naddrs < max_addr_ttls) -- { -- struct ares_addrttl * const at = &addrttls[naddrs]; -- if (aptr + sizeof(struct in_addr) > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -- memcpy(&at->ipaddr, aptr, sizeof(struct in_addr)); -- at->ttl = rr_ttl; -- } -- naddrs++; -- status = ARES_SUCCESS; -+ if(next_cname->alias) -+ aliases[alias++] = strdup(next_cname->alias); -+ if(next_cname->ttl < cname_ttl) -+ cname_ttl = next_cname->ttl; -+ next_cname = next_cname->next; - } -+ } - -- if (rr_class == C_IN && rr_type == T_CNAME) -- { -- /* Record the RR name as an alias. */ -- if (aliases) -- aliases[naliases] = rr_name; -- else -- ares_free(rr_name); -- naliases++; -- -- /* Decode the RR data and replace the hostname with it. */ -- status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, -- &len); -- if (status != ARES_SUCCESS) -- break; -- ares_free(hostname); -- hostname = rr_data; -+ aliases[alias] = NULL; - -- /* Take the min of the TTLs we see in the CNAME chain. */ -- if (cname_ttl > rr_ttl) -- cname_ttl = rr_ttl; -- } -- else -- ares_free(rr_name); -+ hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); -+ if (!hostent->h_addr_list) -+ { -+ goto enomem; -+ } - -- aptr += rr_len; -- if (aptr > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -+ if (ai.cnames) -+ { -+ hostent->h_name = strdup(ai.cnames->name); -+ ares_free(question_hostname); -+ } -+ else -+ { -+ hostent->h_name = question_hostname; - } - -- if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0) -- /* the check for naliases to be zero is to make sure CNAME responses -- don't get caught here */ -- status = ARES_ENODATA; -- if (status == ARES_SUCCESS) -+ hostent->h_aliases = aliases; -+ hostent->h_addrtype = AF_INET; -+ hostent->h_length = sizeof(struct in_addr); -+ -+ if (naddrs) - { -- /* We got our answer. */ -- if (naddrttls) -+ addrs = ares_malloc(naddrs * sizeof(struct in_addr)); -+ if (!addrs) - { -- const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; -- for (i = 0; i < n; i++) -- { -- /* Ensure that each A TTL is no larger than the CNAME TTL. */ -- if (addrttls[i].ttl > cname_ttl) -- addrttls[i].ttl = cname_ttl; -- } -- *naddrttls = n; -+ goto enomem; - } -- if (aliases) -- aliases[naliases] = NULL; -- if (host) -+ -+ next = ai.nodes; -+ for (i = 0; i < naddrs; i++) - { -- /* Allocate memory to build the host entry. */ -- hostent = ares_malloc(sizeof(struct hostent)); -- if (hostent) -+ hostent->h_addr_list[i] = (char*)&addrs[i]; -+ memcpy(hostent->h_addr_list[i], &(((struct sockaddr_in *)next->ai_addr)->sin_addr), sizeof(struct in_addr)); -+ if (naddrttls) - { -- hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); -- if (hostent->h_addr_list) -- { -- /* Fill in the hostent and return successfully. */ -- hostent->h_name = hostname; -- hostent->h_aliases = aliases; -- hostent->h_addrtype = AF_INET; -- hostent->h_length = sizeof(struct in_addr); -- for (i = 0; i < naddrs; i++) -- hostent->h_addr_list[i] = (char *) &addrs[i]; -- hostent->h_addr_list[naddrs] = NULL; -- if (!naddrs && addrs) -- ares_free(addrs); -- *host = hostent; -- return ARES_SUCCESS; -- } -- ares_free(hostent); -+ if(next->ai_ttl > cname_ttl) -+ addrttls[i].ttl = cname_ttl; -+ else -+ addrttls[i].ttl = next->ai_ttl; -+ -+ memcpy(&addrttls[i].ipaddr, &(((struct sockaddr_in *)next->ai_addr)->sin_addr), sizeof(struct in_addr)); - } -- status = ARES_ENOMEM; -+ next = next->ai_next; - } -- } -- if (aliases) -- { -- for (i = 0; i < naliases; i++) -- ares_free(aliases[i]); -- ares_free(aliases); - } -- ares_free(addrs); -- ares_free(hostname); -- return status; --} -- --/* returned size includes terminating 0. */ --static long encoded_name_size(const unsigned char *begin, const unsigned char *end) --{ -- const unsigned char* it = begin; -- for (; *it && it != end; ++it); -- return it == end ? -1 : (long)((it + 1) - begin); --} - --int ares__parse_qtype_reply(const unsigned char* abuf, int alen, int* qtype) --{ -- unsigned int qdcount, ancount; -- const unsigned char* aptr; -- long len; -+ hostent->h_addr_list[naddrs] = NULL; - -- /* Give up if abuf doesn't have room for a header. */ -- if (alen < HFIXEDSZ) -- return ARES_EBADRESP; -+ if (host) -+ { -+ *host = hostent; -+ } -+ else -+ { -+ ares_free_hostent(hostent); -+ } - -- /* Fetch the question and answer count from the header. */ -- qdcount = DNS_HEADER_QDCOUNT(abuf); -- ancount = DNS_HEADER_ANCOUNT(abuf); -- if (qdcount != 1) -- return ARES_EBADRESP; -+ if (naddrttls) -+ { -+ *naddrttls = naddrs; -+ } - -- /* Expand the name from the question, and skip past the question. */ -- aptr = abuf + HFIXEDSZ; -- len = encoded_name_size(aptr, abuf + alen); -- if (len == -1) -- return ARES_EBADRESP; -- if (aptr + len + QFIXEDSZ > abuf + alen) -- return ARES_EBADRESP; -- aptr += len; -- if (!ancount) -- return ARES_ENODATA; -- *qtype = DNS__16BIT(aptr); -+ ares__freeaddrinfo_cnames(ai.cnames); -+ ares__freeaddrinfo_nodes(ai.nodes); - return ARES_SUCCESS; -+ -+enomem: -+ ares_free(aliases); -+ ares_free(hostent); -+ ares__freeaddrinfo_cnames(ai.cnames); -+ ares__freeaddrinfo_nodes(ai.nodes); -+ ares_free(question_hostname); -+ return ARES_ENOMEM; - } -diff --git a/ares_parse_aaaa_reply.3 b/ares_parse_aaaa_reply.3 -index 476a3f1..674acc5 100644 ---- a/ares_parse_aaaa_reply.3 -+++ b/ares_parse_aaaa_reply.3 -@@ -76,3 +76,5 @@ Memory was exhausted. - Dominick Meglio - .br - Copyright 2005 by Dominick Meglio. -+.BR -+Andrew Selivanov -diff --git a/ares_parse_aaaa_reply.c b/ares_parse_aaaa_reply.c -index 5b38bb5..b296a98 100644 ---- a/ares_parse_aaaa_reply.c -+++ b/ares_parse_aaaa_reply.c -@@ -1,6 +1,7 @@ - - /* Copyright 1998 by the Massachusetts Institute of Technology. - * Copyright 2005 Dominick Meglio -+ * Copyright (C) 2019 by Andrew Selivanov - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without -@@ -52,213 +53,145 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, - struct hostent **host, struct ares_addr6ttl *addrttls, - int *naddrttls) - { -- unsigned int qdcount, ancount; -- int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; -- int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ -- int naliases; -- long len; -- const unsigned char *aptr; -- char *hostname, *rr_name, *rr_data, **aliases; -- struct ares_in6_addr *addrs; -- struct hostent *hostent; -- const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; -- -- /* Set *host to NULL for all failure cases. */ -- if (host) -- *host = NULL; -- /* Same with *naddrttls. */ -- if (naddrttls) -- *naddrttls = 0; -+ struct ares_addrinfo ai; -+ struct ares_addrinfo_node *next; -+ struct ares_addrinfo_cname *next_cname; -+ char **aliases = NULL; -+ char *question_hostname = NULL; -+ struct hostent *hostent = NULL; -+ struct ares_in6_addr *addrs = NULL; -+ int naliases = 0, naddrs = 0, alias = 0, i; -+ int cname_ttl = INT_MAX; -+ int status; -+ -+ memset(&ai, 0, sizeof(ai)); -+ -+ status = ares__parse_into_addrinfo2(abuf, alen, &question_hostname, &ai); -+ if (status != ARES_SUCCESS) -+ { -+ ares_free(question_hostname); - -- /* Give up if abuf doesn't have room for a header. */ -- if (alen < HFIXEDSZ) -- return ARES_EBADRESP; -+ if (naddrttls) -+ { -+ *naddrttls = naddrs; -+ } - -- /* Fetch the question and answer count from the header. */ -- qdcount = DNS_HEADER_QDCOUNT(abuf); -- ancount = DNS_HEADER_ANCOUNT(abuf); -- if (qdcount != 1) -- return ARES_EBADRESP; -+ return status; -+ } - -- /* Expand the name from the question, and skip past the question. */ -- aptr = abuf + HFIXEDSZ; -- status = ares__expand_name_for_response(aptr, abuf, alen, &hostname, &len); -- if (status != ARES_SUCCESS) -- return status; -- if (aptr + len + QFIXEDSZ > abuf + alen) -+ hostent = ares_malloc(sizeof(struct hostent)); -+ if (!hostent) - { -- ares_free(hostname); -- return ARES_EBADRESP; -+ goto enomem; - } -- aptr += len + QFIXEDSZ; - -- /* Allocate addresses and aliases; ancount gives an upper bound for both. */ -- if (host) -+ next = ai.nodes; -+ while (next) - { -- addrs = ares_malloc(ancount * sizeof(struct ares_in6_addr)); -- if (!addrs) -- { -- ares_free(hostname); -- return ARES_ENOMEM; -- } -- aliases = ares_malloc((ancount + 1) * sizeof(char *)); -- if (!aliases) -- { -- ares_free(hostname); -- ares_free(addrs); -- return ARES_ENOMEM; -- } -+ ++naddrs; -+ next = next->ai_next; - } -- else -+ -+ next_cname = ai.cnames; -+ while (next_cname) - { -- addrs = NULL; -- aliases = NULL; -+ if(next_cname->alias) -+ ++naliases; -+ next_cname = next_cname->next; - } -- naddrs = 0; -- naliases = 0; - -- /* Examine each answer resource record (RR) in turn. */ -- for (i = 0; i < (int)ancount; i++) -+ aliases = ares_malloc((naliases + 1) * sizeof(char *)); -+ if (!aliases) - { -- /* Decode the RR up to the data field. */ -- status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); -- if (status != ARES_SUCCESS) -- break; -- aptr += len; -- if (aptr + RRFIXEDSZ > abuf + alen) -- { -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } -- rr_type = DNS_RR_TYPE(aptr); -- rr_class = DNS_RR_CLASS(aptr); -- rr_len = DNS_RR_LEN(aptr); -- rr_ttl = DNS_RR_TTL(aptr); -- aptr += RRFIXEDSZ; -- if (aptr + rr_len > abuf + alen) -- { -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } -+ goto enomem; -+ } - -- if (rr_class == C_IN && rr_type == T_AAAA -- && rr_len == sizeof(struct ares_in6_addr) -- && strcasecmp(rr_name, hostname) == 0) -+ if (naliases) -+ { -+ next_cname = ai.cnames; -+ while (next_cname) - { -- if (addrs) -- { -- if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -- memcpy(&addrs[naddrs], aptr, sizeof(struct ares_in6_addr)); -- } -- if (naddrs < max_addr_ttls) -- { -- struct ares_addr6ttl * const at = &addrttls[naddrs]; -- if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- ares_free(rr_name); -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -- memcpy(&at->ip6addr, aptr, sizeof(struct ares_in6_addr)); -- at->ttl = rr_ttl; -- } -- naddrs++; -- status = ARES_SUCCESS; -+ if(next_cname->alias) -+ aliases[alias++] = strdup(next_cname->alias); -+ if(next_cname->ttl < cname_ttl) -+ cname_ttl = next_cname->ttl; -+ next_cname = next_cname->next; - } -+ } - -- if (rr_class == C_IN && rr_type == T_CNAME) -- { -- /* Record the RR name as an alias. */ -- if (aliases) -- aliases[naliases] = rr_name; -- else -- ares_free(rr_name); -- naliases++; -- -- /* Decode the RR data and replace the hostname with it. */ -- status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, -- &len); -- if (status != ARES_SUCCESS) -- break; -- ares_free(hostname); -- hostname = rr_data; -+ aliases[alias] = NULL; - -- /* Take the min of the TTLs we see in the CNAME chain. */ -- if (cname_ttl > rr_ttl) -- cname_ttl = rr_ttl; -- } -- else -- ares_free(rr_name); -+ hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); -+ if (!hostent->h_addr_list) -+ { -+ goto enomem; -+ } - -- aptr += rr_len; -- if (aptr > abuf + alen) -- { /* LCOV_EXCL_START: already checked above */ -- status = ARES_EBADRESP; -- break; -- } /* LCOV_EXCL_STOP */ -+ if (ai.cnames) -+ { -+ hostent->h_name = strdup(ai.cnames->name); -+ ares_free(question_hostname); - } -+ else -+ { -+ hostent->h_name = question_hostname; -+ } -+ -+ hostent->h_aliases = aliases; -+ hostent->h_addrtype = AF_INET6; -+ hostent->h_length = sizeof(struct ares_in6_addr); - -- /* the check for naliases to be zero is to make sure CNAME responses -- don't get caught here */ -- if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0) -- status = ARES_ENODATA; -- if (status == ARES_SUCCESS) -+ if (naddrs) - { -- /* We got our answer. */ -- if (naddrttls) -+ addrs = ares_malloc(naddrs * sizeof(struct ares_in6_addr)); -+ if (!addrs) - { -- const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; -- for (i = 0; i < n; i++) -- { -- /* Ensure that each A TTL is no larger than the CNAME TTL. */ -- if (addrttls[i].ttl > cname_ttl) -- addrttls[i].ttl = cname_ttl; -- } -- *naddrttls = n; -+ goto enomem; - } -- if (aliases) -- aliases[naliases] = NULL; -- if (host) -+ -+ next = ai.nodes; -+ for (i = 0; i < naddrs; i++) - { -- /* Allocate memory to build the host entry. */ -- hostent = ares_malloc(sizeof(struct hostent)); -- if (hostent) -+ hostent->h_addr_list[i] = (char*)&addrs[i]; -+ memcpy(hostent->h_addr_list[i], &(((struct sockaddr_in6 *)next->ai_addr)->sin6_addr), sizeof(struct ares_in6_addr)); -+ if (naddrttls) - { -- hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); -- if (hostent->h_addr_list) -- { -- /* Fill in the hostent and return successfully. */ -- hostent->h_name = hostname; -- hostent->h_aliases = aliases; -- hostent->h_addrtype = AF_INET6; -- hostent->h_length = sizeof(struct ares_in6_addr); -- for (i = 0; i < naddrs; i++) -- hostent->h_addr_list[i] = (char *) &addrs[i]; -- hostent->h_addr_list[naddrs] = NULL; -- if (!naddrs && addrs) -- ares_free(addrs); -- *host = hostent; -- return ARES_SUCCESS; -- } -- ares_free(hostent); -+ if(next->ai_ttl > cname_ttl) -+ addrttls[i].ttl = cname_ttl; -+ else -+ addrttls[i].ttl = next->ai_ttl; -+ -+ memcpy(&addrttls[i].ip6addr, &(((struct sockaddr_in6 *)next->ai_addr)->sin6_addr), sizeof(struct ares_in6_addr)); - } -- status = ARES_ENOMEM; -+ next = next->ai_next; - } - } -- if (aliases) -+ -+ hostent->h_addr_list[naddrs] = NULL; -+ -+ if (host) -+ { -+ *host = hostent; -+ } -+ else - { -- for (i = 0; i < naliases; i++) -- ares_free(aliases[i]); -- ares_free(aliases); -+ ares_free_hostent(hostent); - } -- ares_free(addrs); -- ares_free(hostname); -- return status; -+ -+ if (naddrttls) -+ { -+ *naddrttls = naddrs; -+ } -+ -+ ares__freeaddrinfo_cnames(ai.cnames); -+ ares__freeaddrinfo_nodes(ai.nodes); -+ return ARES_SUCCESS; -+ -+enomem: -+ ares_free(aliases); -+ ares_free(hostent); -+ ares__freeaddrinfo_cnames(ai.cnames); -+ ares__freeaddrinfo_nodes(ai.nodes); -+ ares_free(question_hostname); -+ return ARES_ENOMEM; - } -diff --git a/ares_private.h b/ares_private.h -index dcbdf0e..82280a2 100644 ---- a/ares_private.h -+++ b/ares_private.h -@@ -358,7 +358,36 @@ 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); -+int ares__sortaddrinfo(ares_channel channel, struct ares_addrinfo_node *ai_node); -+int ares__readaddrinfo(FILE *fp, const char *name, unsigned short port, -+ const struct ares_addrinfo_hints *hints, -+ struct ares_addrinfo *ai); -+ -+struct ares_addrinfo *ares__malloc_addrinfo(void); -+ -+struct ares_addrinfo_node *ares__malloc_addrinfo_node(void); -+void ares__freeaddrinfo_nodes(struct ares_addrinfo_node *ai_node); -+ -+struct ares_addrinfo_node *ares__append_addrinfo_node(struct ares_addrinfo_node **ai_node); -+void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head, -+ struct ares_addrinfo_node *tail); -+ -+struct ares_addrinfo_cname *ares__malloc_addrinfo_cname(void); -+void ares__freeaddrinfo_cnames(struct ares_addrinfo_cname *ai_cname); -+ -+struct ares_addrinfo_cname *ares__append_addrinfo_cname(struct ares_addrinfo_cname **ai_cname); -+ -+void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head, -+ struct ares_addrinfo_cname *tail); -+ -+int ares__parse_into_addrinfo(const unsigned char *abuf, -+ int alen, -+ struct ares_addrinfo *ai); -+ -+int ares__parse_into_addrinfo2(const unsigned char *abuf, -+ int alen, -+ char **question_hostname, -+ struct ares_addrinfo *ai); - - #if 0 /* Not used */ - long ares__tvdiff(struct timeval t1, struct timeval t2); -diff --git a/m4/cares-functions.m4 b/m4/cares-functions.m4 -index 7c7c92b..0f3992c 100644 ---- a/m4/cares-functions.m4 -+++ b/m4/cares-functions.m4 -@@ -1661,6 +1661,146 @@ AC_DEFUN([CARES_CHECK_FUNC_GETSERVBYPORT_R], [ - ]) - - -+dnl CARES_CHECK_FUNC_GETSERVBYNAME_R -+dnl ------------------------------------------------- -+dnl Verify if getservbyname_r is available, prototyped, -+dnl and can be compiled. If all of these are true, and -+dnl usage has not been previously disallowed with -+dnl shell variable cares_disallow_getservbyname_r, then -+dnl HAVE_GETSERVBYNAME_R will be defined. -+ -+AC_DEFUN([CARES_CHECK_FUNC_GETSERVBYNAME_R], [ -+ AC_REQUIRE([CARES_INCLUDES_NETDB])dnl -+ # -+ tst_links_getservbyname_r="unknown" -+ tst_proto_getservbyname_r="unknown" -+ tst_compi_getservbyname_r="unknown" -+ tst_allow_getservbyname_r="unknown" -+ tst_nargs_getservbyname_r="unknown" -+ # -+ AC_MSG_CHECKING([if getservbyname_r can be linked]) -+ AC_LINK_IFELSE([ -+ AC_LANG_FUNC_LINK_TRY([getservbyname_r]) -+ ],[ -+ AC_MSG_RESULT([yes]) -+ tst_links_getservbyname_r="yes" -+ ],[ -+ AC_MSG_RESULT([no]) -+ tst_links_getservbyname_r="no" -+ ]) -+ # -+ if test "$tst_links_getservbyname_r" = "yes"; then -+ AC_MSG_CHECKING([if getservbyname_r is prototyped]) -+ AC_EGREP_CPP([getservbyname_r],[ -+ $cares_includes_netdb -+ ],[ -+ AC_MSG_RESULT([yes]) -+ tst_proto_getservbyname_r="yes" -+ ],[ -+ AC_MSG_RESULT([no]) -+ tst_proto_getservbyname_r="no" -+ ]) -+ fi -+ # -+ if test "$tst_proto_getservbyname_r" = "yes"; then -+ if test "$tst_nargs_getservbyname_r" = "unknown"; then -+ AC_MSG_CHECKING([if getservbyname_r takes 4 args.]) -+ AC_COMPILE_IFELSE([ -+ AC_LANG_PROGRAM([[ -+ $cares_includes_netdb -+ ]],[[ -+ if(0 != getservbyname_r(0, 0, 0, 0)) -+ return 1; -+ ]]) -+ ],[ -+ AC_MSG_RESULT([yes]) -+ tst_compi_getservbyname_r="yes" -+ tst_nargs_getservbyname_r="4" -+ ],[ -+ AC_MSG_RESULT([no]) -+ tst_compi_getservbyname_r="no" -+ ]) -+ fi -+ if test "$tst_nargs_getservbyname_r" = "unknown"; then -+ AC_MSG_CHECKING([if getservbyname_r takes 5 args.]) -+ AC_COMPILE_IFELSE([ -+ AC_LANG_PROGRAM([[ -+ $cares_includes_netdb -+ ]],[[ -+ if(0 != getservbyname_r(0, 0, 0, 0, 0)) -+ return 1; -+ ]]) -+ ],[ -+ AC_MSG_RESULT([yes]) -+ tst_compi_getservbyname_r="yes" -+ tst_nargs_getservbyname_r="5" -+ ],[ -+ AC_MSG_RESULT([no]) -+ tst_compi_getservbyname_r="no" -+ ]) -+ fi -+ if test "$tst_nargs_getservbyname_r" = "unknown"; then -+ AC_MSG_CHECKING([if getservbyname_r takes 6 args.]) -+ AC_COMPILE_IFELSE([ -+ AC_LANG_PROGRAM([[ -+ $cares_includes_netdb -+ ]],[[ -+ if(0 != getservbyname_r(0, 0, 0, 0, 0, 0)) -+ return 1; -+ ]]) -+ ],[ -+ AC_MSG_RESULT([yes]) -+ tst_compi_getservbyname_r="yes" -+ tst_nargs_getservbyname_r="6" -+ ],[ -+ AC_MSG_RESULT([no]) -+ tst_compi_getservbyname_r="no" -+ ]) -+ fi -+ AC_MSG_CHECKING([if getservbyname_r is compilable]) -+ if test "$tst_compi_getservbyname_r" = "yes"; then -+ AC_MSG_RESULT([yes]) -+ else -+ AC_MSG_RESULT([no]) -+ fi -+ fi -+ # -+ if test "$tst_compi_getservbyname_r" = "yes"; then -+ AC_MSG_CHECKING([if getservbyname_r usage allowed]) -+ if test "x$cares_disallow_getservbyname_r" != "xyes"; then -+ AC_MSG_RESULT([yes]) -+ tst_allow_getservbyname_r="yes" -+ else -+ AC_MSG_RESULT([no]) -+ tst_allow_getservbyname_r="no" -+ fi -+ fi -+ # -+ AC_MSG_CHECKING([if getservbyname_r might be used]) -+ if test "$tst_links_getservbyname_r" = "yes" && -+ test "$tst_proto_getservbyname_r" = "yes" && -+ test "$tst_compi_getservbyname_r" = "yes" && -+ test "$tst_allow_getservbyname_r" = "yes"; then -+ AC_MSG_RESULT([yes]) -+ AC_DEFINE_UNQUOTED(HAVE_GETSERVBYNAME_R, 1, -+ [Define to 1 if you have the getservbyname_r function.]) -+ AC_DEFINE_UNQUOTED(GETSERVBYNAME_R_ARGS, $tst_nargs_getservbyname_r, -+ [Specifies the number of arguments to getservbyname_r]) -+ if test "$tst_nargs_getservbyname_r" -eq "4"; then -+ AC_DEFINE(GETSERVBYNAME_R_BUFSIZE, sizeof(struct servent_data), -+ [Specifies the size of the buffer to pass to getservbyname_r]) -+ else -+ AC_DEFINE(GETSERVBYNAME_R_BUFSIZE, 4096, -+ [Specifies the size of the buffer to pass to getservbyname_r]) -+ fi -+ ac_cv_func_getservbyname_r="yes" -+ else -+ AC_MSG_RESULT([no]) -+ ac_cv_func_getservbyname_r="no" -+ fi -+]) -+ -+ - dnl CARES_CHECK_FUNC_INET_NET_PTON - dnl ------------------------------------------------- - dnl Verify if inet_net_pton is available, prototyped, can -diff --git a/test/ares-test-internal.cc b/test/ares-test-internal.cc -index a2d31c5..e9a0244 100644 ---- a/test/ares-test-internal.cc -+++ b/test/ares-test-internal.cc -@@ -356,6 +356,107 @@ TEST_F(LibraryTest, GetHostentAllocFail) { - fclose(fp); - } - -+TEST_F(DefaultChannelTest, GetAddrInfoHostsPositive) { -+ TempFile hostsfile("1.2.3.4 example.com \n" -+ " 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n" -+ "#comment\n" -+ "4.5.6.7\n" -+ "1.3.5.7 \n" -+ "::1 ipv6.com"); -+ EnvValue with_env("CARES_HOSTS", hostsfile.filename()); -+ struct ares_addrinfo_hints hints = {}; -+ AddrInfoResult result = {}; -+ hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "example.com", NULL, &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{example.com addr=[1.2.3.4]}", ss.str()); -+} -+ -+TEST_F(DefaultChannelTest, GetAddrInfoHostsSpaces) { -+ TempFile hostsfile("1.2.3.4 example.com \n" -+ " 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n" -+ "#comment\n" -+ "4.5.6.7\n" -+ "1.3.5.7 \n" -+ "::1 ipv6.com"); -+ EnvValue with_env("CARES_HOSTS", hostsfile.filename()); -+ struct ares_addrinfo_hints hints = {}; -+ AddrInfoResult result = {}; -+ hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "google.com", NULL, &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{www.google.com->google.com, www2.google.com->google.com addr=[2.3.4.5]}", ss.str()); -+} -+ -+TEST_F(DefaultChannelTest, GetAddrInfoHostsByALias) { -+ TempFile hostsfile("1.2.3.4 example.com \n" -+ " 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n" -+ "#comment\n" -+ "4.5.6.7\n" -+ "1.3.5.7 \n" -+ "::1 ipv6.com"); -+ EnvValue with_env("CARES_HOSTS", hostsfile.filename()); -+ struct ares_addrinfo_hints hints = {}; -+ AddrInfoResult result = {}; -+ hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "www2.google.com", NULL, &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{www.google.com->google.com, www2.google.com->google.com addr=[2.3.4.5]}", ss.str()); -+} -+ -+TEST_F(DefaultChannelTest, GetAddrInfoHostsIPV6) { -+ TempFile hostsfile("1.2.3.4 example.com \n" -+ " 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n" -+ "#comment\n" -+ "4.5.6.7\n" -+ "1.3.5.7 \n" -+ "::1 ipv6.com"); -+ EnvValue with_env("CARES_HOSTS", hostsfile.filename()); -+ struct ares_addrinfo_hints hints = {}; -+ AddrInfoResult result = {}; -+ hints.ai_family = AF_INET6; -+ hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "ipv6.com", NULL, &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{ipv6.com addr=[[0000:0000:0000:0000:0000:0000:0000:0001]]}", ss.str()); -+} -+ -+TEST_F(LibraryTest, GetAddrInfoAllocFail) { -+ TempFile hostsfile("1.2.3.4 example.com alias1 alias2\n"); -+ struct ares_addrinfo_hints hints; -+ unsigned short port = 80; -+ -+ memset(&hints, 0, sizeof(hints)); -+ hints.ai_family = AF_INET; -+ -+ FILE *fp = fopen(hostsfile.filename(), "r"); -+ ASSERT_NE(nullptr, fp); -+ -+ for (int ii = 1; ii <= 3; ii++) { -+ rewind(fp); -+ ClearFails(); -+ SetAllocFail(ii); -+ struct ares_addrinfo ai; -+ EXPECT_EQ(ARES_ENOMEM, ares__readaddrinfo(fp, "example.com", port, &hints, &ai)) << ii; -+ } -+ fclose(fp); -+} -+ - TEST(Misc, OnionDomain) { - EXPECT_EQ(0, ares__is_onion_domain("onion.no")); - EXPECT_EQ(0, ares__is_onion_domain(".onion.no")); -diff --git a/test/ares-test-live-ai.cc b/test/ares-test-live-ai.cc -deleted file mode 100644 -index ab93587..0000000 ---- a/test/ares-test-live-ai.cc -+++ /dev/null -@@ -1,82 +0,0 @@ --// This file includes tests that attempt to do real lookups --// of DNS names using the local machine's live infrastructure. --// As a result, we don't check the results very closely, to allow --// for varying local configurations. -- --#include "ares-test.h" --#include "ares-test-ai.h" -- --#ifdef HAVE_NETDB_H --#include --#endif -- --namespace ares { --namespace test { -- --MATCHER_P(IncludesAtLeastNumAddresses, n, "") { -- int cnt = 0; -- 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.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.get(); ai != NULL; ai = ai->ai_next) -- if (ai->ai_family == addrtype) -- return true; -- return false; --} -- --void DefaultChannelTestAI::Process() { -- ProcessWork(channel_, NoExtraFDs, nullptr); --} -- --// Use the address of Google's public DNS servers as example addresses that are --// likely to be accessible everywhere/everywhen. -- --VIRT_NONVIRT_TEST_F(DefaultChannelTestAI, LiveGetHostByNameV4) { -- struct ares_addrinfo hints = {}; -- hints.ai_family = AF_INET; -- 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.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; -- 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.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; -- 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.ai_, IncludesAtLeastNumAddresses(2)); -- EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET6)); -- EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET)); --} -- --} // namespace test --} // namespace ares -diff --git a/test/ares-test-live.cc b/test/ares-test-live.cc -index 3508705..07616e0 100644 ---- a/test/ares-test-live.cc -+++ b/test/ares-test-live.cc -@@ -18,6 +18,70 @@ unsigned char gdns_addr4[4] = {0x08, 0x08, 0x08, 0x08}; - unsigned char gdns_addr6[16] = {0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}; - -+MATCHER_P(IncludesAtLeastNumAddresses, n, "") { -+ if(!arg) -+ return false; -+ int cnt = 0; -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) -+ cnt++; -+ return cnt >= n; -+} -+ -+MATCHER_P(OnlyIncludesAddrType, addrtype, "") { -+ if(!arg) -+ return false; -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) -+ if (ai->ai_family != addrtype) -+ return false; -+ return true; -+} -+ -+MATCHER_P(IncludesAddrType, addrtype, "") { -+ if(!arg) -+ return false; -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) -+ if (ai->ai_family == addrtype) -+ return true; -+ return false; -+} -+ -+//VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetAddrInfoV4) { -+ //struct ares_addrinfo_hints hints = {}; -+ //hints.ai_family = AF_INET; -+ //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.ai_, IncludesAtLeastNumAddresses(1)); -+ //EXPECT_THAT(result.ai_, OnlyIncludesAddrType(AF_INET)); -+//} -+ -+//VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetAddrInfoV6) { -+ //struct ares_addrinfo_hints hints = {}; -+ //hints.ai_family = AF_INET6; -+ //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.ai_, IncludesAtLeastNumAddresses(1)); -+ //EXPECT_THAT(result.ai_, OnlyIncludesAddrType(AF_INET6)); -+//} -+ -+//VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetAddrInfoUnspec) { -+ //struct ares_addrinfo_hints hints = {}; -+ //hints.ai_family = AF_UNSPEC; -+ //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.ai_, IncludesAtLeastNumAddresses(2)); -+ //EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET6)); -+ //EXPECT_THAT(result.ai_, IncludesAddrType(AF_INET)); -+//} -+ - VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByNameV4) { - HostResult result; - ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -index c293102..d22b9a3 100644 ---- a/test/ares-test-mock-ai.cc -+++ b/test/ares-test-mock-ai.cc -@@ -19,7 +19,7 @@ MATCHER_P(IncludesNumAddresses, n, "") { - if(!arg) - return false; - int cnt = 0; -- for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next) -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) - cnt++; - return n == cnt; - } -@@ -30,7 +30,7 @@ MATCHER_P(IncludesV4Address, address, "") { - in_addr addressnum = {}; - if (!inet_pton(AF_INET, address, &addressnum)) - return false; // wrong number format? -- for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next) { -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != AF_INET) - continue; - if (reinterpret_cast(ai->ai_addr)->sin_addr.s_addr == -@@ -47,7 +47,7 @@ MATCHER_P(IncludesV6Address, address, "") { - if (!inet_pton(AF_INET6, address, &addressnum)) { - return false; // wrong number format? - } -- for (const ares_addrinfo* ai = arg.get(); ai != NULL; ai = ai->ai_next) { -+ for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != AF_INET6) - continue; - if (!memcmp( -@@ -59,7 +59,7 @@ MATCHER_P(IncludesV6Address, address, "") { - } - - // UDP only so mock server doesn't get confused by concatenated requests --TEST_P(MockUDPChannelTestAI, ParallelLookups) { -+TEST_P(MockUDPChannelTestAI, GetAddrInfoParallelLookups) { - DNSPacket rsp1; - rsp1.set_response().set_aa() - .add_question(new DNSQuestion("www.google.com", ns_t_a)) -@@ -73,8 +73,9 @@ TEST_P(MockUDPChannelTestAI, ParallelLookups) { - ON_CALL(server_, OnRequest("www.example.com", ns_t_a)) - .WillByDefault(SetReply(&server_, &rsp2)); - -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - AddrInfoResult result1; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result1); - AddrInfoResult result2; -@@ -113,8 +114,9 @@ TEST_P(MockUDPChannelTestAI, TruncationRetry) { - .WillOnce(SetReply(&server_, &rspok)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -130,8 +132,9 @@ TEST_P(MockTCPChannelTestAI, MalformedResponse) { - .WillOnce(SetReplyData(&server_, one)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -147,8 +150,9 @@ TEST_P(MockTCPChannelTestAI, FormErrResponse) { - .WillOnce(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -164,8 +168,9 @@ TEST_P(MockTCPChannelTestAI, ServFailResponse) { - .WillOnce(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -182,8 +187,9 @@ TEST_P(MockTCPChannelTestAI, NotImplResponse) { - .WillOnce(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -200,8 +206,9 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) { - .WillOnce(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -209,23 +216,23 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) { - EXPECT_EQ(ARES_ECONNREFUSED, result.status_); - } - --// TODO: make it work --//TEST_P(MockTCPChannelTestAI, YXDomainResponse) { --// DNSPacket rsp; --// rsp.set_response().set_aa() --// .add_question(new DNSQuestion("www.google.com", ns_t_a)); --// rsp.set_rcode(ns_r_yxdomain); --// EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) --// .WillOnce(SetReply(&server_, &rsp)); --// --// AddrInfoResult result; --// struct ares_addrinfo hints = {}; --// hints.ai_family = AF_INET; --// ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); --// Process(); --// EXPECT_TRUE(result.done_); --// EXPECT_EQ(ARES_ENODATA, result.status_); --//} -+TEST_P(MockTCPChannelTestAI, YXDomainResponse) { -+ DNSPacket rsp; -+ rsp.set_response().set_aa() -+ .add_question(new DNSQuestion("www.google.com", ns_t_a)); -+ rsp.set_rcode(ns_r_yxdomain); -+ EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) -+ .WillOnce(SetReply(&server_, &rsp)); -+ -+ AddrInfoResult result; -+ struct ares_addrinfo_hints hints = {}; -+ hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ EXPECT_EQ(ARES_ENODATA, result.status_); -+} - - class MockExtraOptsTestAI - : public MockChannelOptsTest, -@@ -261,8 +268,9 @@ TEST_P(MockExtraOptsTestAI, SimpleQuery) { - .WillByDefault(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -301,8 +309,9 @@ TEST_P(MockNoCheckRespChannelTestAI, ServFailResponse) { - .WillByDefault(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -318,8 +327,9 @@ TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) { - .WillByDefault(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -335,8 +345,9 @@ TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) { - .WillByDefault(SetReply(&server_, &rsp)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -353,8 +364,9 @@ TEST_P(MockChannelTestAI, FamilyV6) { - ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) - .WillByDefault(SetReply(&server_, &rsp6)); - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET6; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "example.com.", NULL, &hints, - AddrInfoCallback, &result); - Process(); -@@ -371,8 +383,9 @@ TEST_P(MockChannelTestAI, FamilyV4) { - ON_CALL(server_, OnRequest("example.com", ns_t_a)) - .WillByDefault(SetReply(&server_, &rsp4)); - AddrInfoResult result = {}; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "example.com.", NULL, &hints, - AddrInfoCallback, &result); - Process(); -@@ -390,15 +403,16 @@ TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) { - ON_CALL(server_, OnRequest("example.com", ns_t_a)) - .WillByDefault(SetReply(&server_, &rsp4)); - AddrInfoResult result = {}; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "example.com.", NULL, &hints, - AddrInfoCallback, &result); - Process(); - 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")); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{addr=[2.3.4.5], addr=[7.8.9.0]}", ss.str()); - } - - TEST_P(MockChannelTestAI, FamilyUnspecified) { -@@ -417,8 +431,9 @@ TEST_P(MockChannelTestAI, FamilyUnspecified) { - ON_CALL(server_, OnRequest("example.com", ns_t_a)) - .WillByDefault(SetReply(&server_, &rsp4)); - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_UNSPEC; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "example.com.", NULL, &hints, - AddrInfoCallback, &result); - Process(); -@@ -446,8 +461,9 @@ TEST_P(MockEDNSChannelTestAI, RetryWithoutEDNS) { - .WillOnce(SetReply(&server_, &rspok)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -474,8 +490,9 @@ TEST_P(MockChannelTestAI, SearchDomains) { - .WillByDefault(SetReply(&server_, &yesthird)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -519,8 +536,9 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) { - .WillByDefault(SetReply(&server_, &failthird4)); - - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_UNSPEC; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -536,8 +554,9 @@ class MockMultiServerChannelTestAI - : MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {} - void CheckExample() { - AddrInfoResult result; -- struct ares_addrinfo hints = {}; -+ struct ares_addrinfo_hints hints = {}; - hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; - ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result); - Process(); - EXPECT_TRUE(result.done_); -@@ -648,6 +667,26 @@ TEST_P(NoRotateMultiMockTestAI, ThirdServer) { - CheckExample(); - } - -+TEST_P(MockChannelTestAI, FamilyV4ServiceName) { -+ DNSPacket rsp4; -+ rsp4.set_response().set_aa() -+ .add_question(new DNSQuestion("example.com", ns_t_a)) -+ .add_answer(new DNSARR("example.com", 100, {1, 1, 1, 1})) -+ .add_answer(new DNSARR("example.com", 100, {2, 2, 2, 2})); -+ ON_CALL(server_, OnRequest("example.com", ns_t_a)) -+ .WillByDefault(SetReply(&server_, &rsp4)); -+ AddrInfoResult result = {}; -+ struct ares_addrinfo_hints hints = {}; -+ hints.ai_family = AF_INET; -+ hints.ai_flags = ARES_AI_NOSORT; -+ ares_getaddrinfo(channel_, "example.com", "http", &hints, AddrInfoCallback, &result); -+ Process(); -+ EXPECT_TRUE(result.done_); -+ std::stringstream ss; -+ ss << result.ai_; -+ EXPECT_EQ("{addr=[1.1.1.1:80], addr=[2.2.2.2:80]}", ss.str()); -+} -+ - // force-tcp does currently not work, possibly test DNS server swallows - // bytes from second query - //INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI, -diff --git a/test/ares-test-mock.cc b/test/ares-test-mock.cc -index 7deecb8..fdd7118 100644 ---- a/test/ares-test-mock.cc -+++ b/test/ares-test-mock.cc -@@ -51,7 +51,7 @@ TEST_P(MockChannelTest, Basic) { - } - - // UDP only so mock server doesn't get confused by concatenated requests --TEST_P(MockUDPChannelTest, ParallelLookups) { -+TEST_P(MockUDPChannelTest, GetHostByNameParallelLookups) { - DNSPacket rsp1; - rsp1.set_response().set_aa() - .add_question(new DNSQuestion("www.google.com", ns_t_a)) -diff --git a/test/ares-test.cc b/test/ares-test.cc -index 1128e99..2df72c9 100644 ---- a/test/ares-test.cc -+++ b/test/ares-test.cc -@@ -567,7 +567,7 @@ void HostCallback(void *data, int status, int timeouts, - - std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) { - os << '{'; -- if (result.done_) { -+ if (result.done_ && result.ai_) { - os << StatusToString(result.status_) << " " << result.ai_; - } else { - os << "(incomplete)"; -@@ -578,11 +578,25 @@ std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) { - - 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 << "' "; -+ struct ares_addrinfo_cname *next_cname = ai->cnames; -+ while(next_cname) { -+ if(next_cname->alias) { -+ os << next_cname->alias << "->"; - } -+ if(next_cname->name) { -+ os << next_cname->name; -+ } -+ if((next_cname = next_cname->next)) -+ os << ", "; -+ else -+ os << " "; -+ } -+ -+ struct ares_addrinfo_node *next = ai->nodes; -+ while(next) { -+ //if(next->ai_canonname) { -+ //os << "'" << next->ai_canonname << "' "; -+ //} - unsigned short port = 0; - os << "addr=["; - if(next->ai_family == AF_INET) { -diff --git a/test/ares-test.h b/test/ares-test.h -index ae675aa..3a9986e 100644 ---- a/test/ares-test.h -+++ b/test/ares-test.h -@@ -454,7 +454,6 @@ private: - } \ - void VCLASS_NAME(casename, testname)::InnerTestBody() - -- - } // namespace test - } // namespace ares - --- -2.16.4 - diff --git a/0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch b/0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch deleted file mode 100644 index eb8d384..0000000 --- a/0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch +++ /dev/null @@ -1,35 +0,0 @@ -From e65da6067e5f3524c940671dcffc966cc69f451f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20No=C3=A9?= -Date: Wed, 17 Jul 2019 09:29:11 -0400 -Subject: [PATCH 08/10] Add missing limits.h include from ares_getaddrinfo.c - (#267) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This files references INT_MAX, but does not include limits.h. This can -cause a build failure on some platforms. Include limits.h if we have it. - -Fix-by: Dan Noé ---- - ares_getaddrinfo.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/ares_getaddrinfo.c b/ares_getaddrinfo.c -index 86c9e71..dba5a00 100644 ---- a/ares_getaddrinfo.c -+++ b/ares_getaddrinfo.c -@@ -48,6 +48,10 @@ - #endif - #include - -+#ifdef HAVE_LIMITS_H -+#include -+#endif -+ - #include "ares.h" - #include "bitncmp.h" - #include "ares_private.h" --- -2.16.4 - diff --git a/0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch b/0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch deleted file mode 100644 index 803cf80..0000000 --- a/0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch +++ /dev/null @@ -1,70 +0,0 @@ -From d50b452fcbf34bddac3e59dfd53ff7d93fad7794 Mon Sep 17 00:00:00 2001 -From: Christian Ammer -Date: Mon, 3 Dec 2018 01:08:49 +0100 -Subject: [PATCH 09/10] Increase portability of `ares-test-mock-ai.cc` (#235) - -* using portable ares_inet_pton and updated includes in ares-test-mock-ai -* forgot to remove deleted ares-test-ai.cc in Makefile.inc - -Fix By: Christian Ammer (@ChristianAmmer) ---- - test/Makefile.inc | 1 - - test/ares-test-ai.cc | 0 - test/ares-test-mock-ai.cc | 9 ++++----- - 3 files changed, 4 insertions(+), 6 deletions(-) - delete mode 100644 test/ares-test-ai.cc - -diff --git a/test/Makefile.inc b/test/Makefile.inc -index 7952b4c..3c68d7c 100644 ---- a/test/Makefile.inc -+++ b/test/Makefile.inc -@@ -1,7 +1,6 @@ - TESTSOURCES = ares-test-main.cc \ - ares-test-init.cc \ - ares-test.cc \ -- ares-test-ai.cc \ - ares-test-ns.cc \ - ares-test-parse.cc \ - ares-test-parse-a.cc \ -diff --git a/test/ares-test-ai.cc b/test/ares-test-ai.cc -deleted file mode 100644 -index e69de29..0000000 -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -index d22b9a3..d0df867 100644 ---- a/test/ares-test-mock-ai.cc -+++ b/test/ares-test-mock-ai.cc -@@ -1,11 +1,10 @@ - #include "ares-test-ai.h" - #include "dns-proto.h" - --#ifdef HAVE_NETDB_H --#include -+#ifdef HAVE_NETINET_IN_H -+#include - #endif - --#include - #include - #include - -@@ -28,7 +27,7 @@ MATCHER_P(IncludesV4Address, address, "") { - if(!arg) - return false; - in_addr addressnum = {}; -- if (!inet_pton(AF_INET, address, &addressnum)) -+ if (!ares_inet_pton(AF_INET, address, &addressnum)) - return false; // wrong number format? - for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != AF_INET) -@@ -44,7 +43,7 @@ MATCHER_P(IncludesV6Address, address, "") { - if(!arg) - return false; - in6_addr addressnum = {}; -- if (!inet_pton(AF_INET6, address, &addressnum)) { -+ if (!ares_inet_pton(AF_INET6, address, &addressnum)) { - return false; // wrong number format? - } - for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) { --- -2.16.4 - diff --git a/0010-Disable-failing-test.patch b/0010-Disable-failing-test.patch deleted file mode 100644 index 9824f76..0000000 --- a/0010-Disable-failing-test.patch +++ /dev/null @@ -1,36 +0,0 @@ -From affbda0162129a44fb0a7547d981f874bc1ce280 Mon Sep 17 00:00:00 2001 -From: Michal Rostecki -Date: Wed, 23 Oct 2019 16:59:03 +0200 -Subject: [PATCH 10/10] Disable failing test - -FamilyV4ServiceName passes when you run it locally, but it cannot run on -OBS. ---- - test/ares-test-mock-ai.cc | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc -index d0df867..d7cc87e 100644 ---- a/test/ares-test-mock-ai.cc -+++ b/test/ares-test-mock-ai.cc -@@ -666,7 +666,7 @@ TEST_P(NoRotateMultiMockTestAI, ThirdServer) { - CheckExample(); - } - --TEST_P(MockChannelTestAI, FamilyV4ServiceName) { -+/* TEST_P(MockChannelTestAI, FamilyV4ServiceName) { - DNSPacket rsp4; - rsp4.set_response().set_aa() - .add_question(new DNSQuestion("example.com", ns_t_a)) -@@ -684,7 +684,7 @@ TEST_P(MockChannelTestAI, FamilyV4ServiceName) { - std::stringstream ss; - ss << result.ai_; - EXPECT_EQ("{addr=[1.1.1.1:80], addr=[2.2.2.2:80]}", ss.str()); --} -+} */ - - // force-tcp does currently not work, possibly test DNS server swallows - // bytes from second query --- -2.16.4 - diff --git a/c-ares-1.15.0-20191108.tar.gz b/c-ares-1.15.0-20191108.tar.gz new file mode 100644 index 0000000..4ac84db --- /dev/null +++ b/c-ares-1.15.0-20191108.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:327ca0ac334f8bcdc8d9da0cbc5e0ff2e304c248aab6cac8835936b1e3548a86 +size 1362964 diff --git a/c-ares-1.15.0.tar.gz b/c-ares-1.15.0.tar.gz deleted file mode 100644 index 6686f25..0000000 --- a/c-ares-1.15.0.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6cdb97871f2930530c97deb7cf5c8fa4be5a0b02c7cea6e7c7667672a39d6852 -size 1347687 diff --git a/c-ares-1.15.0.tar.gz.asc b/c-ares-1.15.0.tar.gz.asc deleted file mode 100644 index b2b59fd..0000000 --- a/c-ares-1.15.0.tar.gz.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQEzBAABCgAdFiEEJ+3q8i86vOtQ25oSXMkI/bceEsIFAlvPiSoACgkQXMkI/bce -EsL1Twf/fAocxkO1QNQ0OQcX1Ga/LD37Z0S9gMNoOcW9ptB0G2A1T4IW4aN2gnko -tofSzeJ5kZZQ3Dk11MiWWxkowZn2+hE+/H13JRUgkJ9W0HS9wqxiTZksS2xORjvx -2KMeBLTcd2ordcnTP1Ufb8rzH9++3G+rgeAP7tqVnYx9y55u5onoAgwLwuw+ypOJ -ua0gjxysxCvg9li+c2ZvuOrFIv3sS7VBSsRJhoJwSVslCGGiDOQpH/AMq4Er7jBL -cTqVYUy3d7leaeRuGJaQb3q/CG8o+iE9CQBmituTYauP7fXQrjPdVKq7IQyqTboi -1M24ymm+IBgICBxVzXfx+PuotbwXIg== -=XhwX ------END PGP SIGNATURE----- diff --git a/c-ares.changes b/c-ares.changes index 2f58e8f..9f53472 100644 --- a/c-ares.changes +++ b/c-ares.changes @@ -1,3 +1,25 @@ +------------------------------------------------------------------- +Fri Nov 8 11:16:29 UTC 2019 - Adam Majer + +- Update to upstream snapshot 20191108 + * getaddrinfo - avoid infinite loop in case of NXDOMAIN + * ares_getenv - return NULL in all cases + * implement ares_getaddrinfo + +- onion-crash.patch: removed, upstreamed. +- removed upstream patches that are part of the snapshot: + 0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch + 0002-Remaining-queries-counter-fix-additional-unit-tests-.patch + 0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch + 0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch + 0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch + 0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch + 0007-getaddrinfo-enhancements-257.patch + 0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch + 0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch + 0010-Disable-failing-test.patch +- disable-live-tests.patch - updated + ------------------------------------------------------------------- Wed Oct 23 15:11:27 UTC 2019 - Michał Rostecki diff --git a/c-ares.spec b/c-ares.spec index 2188484..dcd28c7 100644 --- a/c-ares.spec +++ b/c-ares.spec @@ -18,39 +18,22 @@ %define libname libcares2 Name: c-ares -Version: 1.15.0 +Version: 1.15.0~20191108 Release: 0 Summary: Library for asynchronous name resolves License: MIT Group: Development/Libraries/C and C++ URL: http://c-ares.haxx.se/ -Source0: http://c-ares.haxx.se/download/%{name}-%{version}.tar.gz -Source1: http://c-ares.haxx.se/download/%{name}-%{version}.tar.gz.asc +#Source0: https://c-ares.haxx.se/daily-snapshot/c-ares-1.15.0-20191108.tar.gz +Source0: c-ares-1.15.0-20191108.tar.gz +#Source0: http://c-ares.haxx.se/download/%{name}-%{version}.tar.gz +#Source1: http://c-ares.haxx.se/download/%{name}-%{version}.tar.gz.asc Source3: %{name}.keyring Source4: baselibs.conf Patch0: 0001-Use-RPM-compiler-options.patch Patch1: disable-live-tests.patch -Patch2: onion-crash.patch -# PATCH-FEATURE-UPSTREAM 0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch -Patch3: 0001-Add-initial-implementation-for-ares_getaddrinfo-112.patch -# PATCH-FEATURE-UPSTREAM 0002-Remaining-queries-counter-fix-additional-unit-tests-.patch -Patch4: 0002-Remaining-queries-counter-fix-additional-unit-tests-.patch -# PATCH-FEATURE-UPSTREAM 0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch -Patch5: 0003-Bugfix-for-ares_getaddrinfo-and-additional-unit-test.patch -# PATCH-FEATURE-UPSTREAM 0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch -Patch6: 0004-Add-ares__sortaddrinfo-to-support-getaddrinfo-sorted.patch -# PATCH-FEATURE-UPSTREAM 0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch -Patch7: 0005-getaddrinfo-avoid-infinite-loop-in-case-of-NXDOMAIN-.patch -# PATCH-FEATURE-UPSTREAM 0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch -Patch8: 0006-getaddrinfo-callback-must-be-called-on-bad-domain-24.patch -# PATCH-FEATURE-UPSTREAM 0007-getaddrinfo-enhancements-257.patch -Patch9: 0007-getaddrinfo-enhancements-257.patch -# PATCH-FEATURE-UPSTREAM 0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch -Patch10: 0008-Add-missing-limits.h-include-from-ares_getaddrinfo.c.patch -# PATCH-FEATURE-UPSTREAM 0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch -Patch11: 0009-Increase-portability-of-ares-test-mock-ai.cc-235.patch # PATCH-FIX-OPENSUSE 0010-Disable-failing-test.patch -Patch12: 0010-Disable-failing-test.patch +#Patch12: 0010-Disable-failing-test.patch BuildRequires: autoconf BuildRequires: automake BuildRequires: gcc-c++ @@ -85,7 +68,7 @@ asynchronously. c-ares is a fork of the library named 'ares', written by Greg Hudson at MIT. %prep -%autosetup -p1 +%autosetup -p1 -n c-ares-1.15.0-20191108 # Remove bogus cflags checking sed -i -e '/XC_CHECK_BUILD_FLAGS/d' configure.ac diff --git a/disable-live-tests.patch b/disable-live-tests.patch index 65857c3..1796a62 100644 --- a/disable-live-tests.patch +++ b/disable-live-tests.patch @@ -1,19 +1,20 @@ -Index: c-ares-1.15.0/test/Makefile.inc +Index: c-ares-1.15.0-20191108/test/Makefile.inc =================================================================== ---- c-ares-1.15.0.orig/test/Makefile.inc -+++ c-ares-1.15.0/test/Makefile.inc -@@ -13,7 +13,6 @@ TESTSOURCES = ares-test-main.cc \ +--- c-ares-1.15.0-20191108.orig/test/Makefile.inc ++++ c-ares-1.15.0-20191108/test/Makefile.inc +@@ -13,8 +13,6 @@ TESTSOURCES = ares-test-main.cc \ ares-test-parse-srv.cc \ ares-test-parse-txt.cc \ ares-test-misc.cc \ - ares-test-live.cc \ +- ares-test-live-ai.cc \ ares-test-mock.cc \ + ares-test-mock-ai.cc \ ares-test-internal.cc \ - dns-proto.cc \ -Index: c-ares-1.15.0/test/ares-test-misc.cc +Index: c-ares-1.15.0-20191108/test/ares-test-misc.cc =================================================================== ---- c-ares-1.15.0.orig/test/ares-test-misc.cc -+++ c-ares-1.15.0/test/ares-test-misc.cc +--- c-ares-1.15.0-20191108.orig/test/ares-test-misc.cc ++++ c-ares-1.15.0-20191108/test/ares-test-misc.cc @@ -47,10 +47,12 @@ TEST_F(DefaultChannelTest, SetServers) { EXPECT_EQ(expected, GetNameServers(channel_)); diff --git a/onion-crash.patch b/onion-crash.patch deleted file mode 100644 index 86dd983..0000000 --- a/onion-crash.patch +++ /dev/null @@ -1,35 +0,0 @@ -https://github.com/c-ares/c-ares/pull/241 - -From 98297b969880855c4ed514935bf5bb4cf2ae1ec0 Mon Sep 17 00:00:00 2001 -From: XadillaX -Date: Thu, 31 Jan 2019 13:14:06 +0800 -Subject: [PATCH] fix: init bufp before reject .onion to make it can be free - correctly - ---- - ares_create_query.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/ares_create_query.c b/ares_create_query.c -index 1606b1a1..9efce17c 100644 ---- a/ares_create_query.c -+++ b/ares_create_query.c -@@ -94,14 +94,14 @@ int ares_create_query(const char *name, int dnsclass, int type, - size_t buflen; - unsigned char *buf; - -- /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ -- if (ares__is_onion_domain(name)) -- return ARES_ENOTFOUND; -- - /* Set our results early, in case we bail out early with an error. */ - *buflenp = 0; - *bufp = NULL; - -+ /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ -+ if (ares__is_onion_domain(name)) -+ return ARES_ENOTFOUND; -+ - /* Allocate a memory area for the maximum size this packet might need. +2 - * is for the length byte and zero termination if no dots or ecscaping is - * used.