SHA256
1
0
forked from pool/c-ares
c-ares/0007-getaddrinfo-enhancements-257.patch
Tomáš Chvátal 4c1bcc5dd1 Accepting request 742197 from home:mrostecki:branches:devel:kubic
Needed to fix envoy-proxy

- Add upstream patches with the ares_getaddrinfo function:
  * 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
- Add a patch which disables test failing on OBS (but passing in
  local environment):
  * 0010-Disable-failing-test.patch

OBS-URL: https://build.opensuse.org/request/show/742197
OBS-URL: https://build.opensuse.org/package/show/devel:libraries:c_c++/c-ares?expand=0&rev=9
2019-10-23 15:55:33 +00:00

3592 lines
112 KiB
Diff

From 7658a2d81cd69c207c411319101ce8a9b245f088 Mon Sep 17 00:00:00 2001
From: Andrew Selivanov <andrew.selivanov@gmail.com>
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 <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#else
+# include "nameser.h"
+#endif
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+# include <arpa/nameser_compat.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#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 <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#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 <ares.h>
.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 <andrew.selivanov@gmail.com>
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 <netdb.h>
+#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 <ares.h>
.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 <netinet/in.h>
#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 <andrew.selivanov@gmail.com>
+.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 <andrew.selivanov@gmail.com>
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 <netdb.h>
-#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<sockaddr_in*>(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