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
pull/262/head
Andrew Selivanov 6 years ago committed by Brad House
parent ba1aa0635a
commit 7d3591ee8a
  1. 14
      CMakeLists.txt
  2. 22
      Makefile.inc
  3. 60
      ares.h
  4. 266
      ares__parse_into_addrinfo.c
  5. 261
      ares__readaddrinfo.c
  6. 6
      ares__sortaddrinfo.c
  7. 6
      ares_config.h.cmake
  8. 8
      ares_freeaddrinfo.3
  9. 57
      ares_freeaddrinfo.c
  10. 4
      ares_getaddrinfo.3
  11. 933
      ares_getaddrinfo.c
  12. 2
      ares_parse_a_reply.3
  13. 325
      ares_parse_a_reply.c
  14. 2
      ares_parse_aaaa_reply.3
  15. 291
      ares_parse_aaaa_reply.c
  16. 31
      ares_private.h
  17. 140
      m4/cares-functions.m4
  18. 1
      test/Makefile.inc
  19. 101
      test/ares-test-internal.cc
  20. 82
      test/ares-test-live-ai.cc
  21. 64
      test/ares-test-live.cc
  22. 125
      test/ares-test-mock-ai.cc
  23. 2
      test/ares-test-mock.cc
  24. 24
      test/ares-test.cc
  25. 1
      test/ares-test.h

@ -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 (gethostname "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOSTNAME)
CHECK_SYMBOL_EXISTS (getnameinfo "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETNAMEINFO) 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 (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 (gettimeofday "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETTIMEOFDAY)
CHECK_SYMBOL_EXISTS (if_indextoname "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_IF_INDEXTONAME) 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) CHECK_SYMBOL_EXISTS (inet_net_pton "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_INET_NET_PTON)
@ -492,6 +493,19 @@ IF (HAVE_GETSERVBYPORT_R)
ENDIF () ENDIF ()
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 # Set some aliases used for ares_build.h
IF (HAVE_SYS_TYPES_H) IF (HAVE_SYS_TYPES_H)
SET (CARES_HAVE_SYS_TYPES_H 1) SET (CARES_HAVE_SYS_TYPES_H 1)

@ -1,6 +1,8 @@
CSOURCES = ares__close_sockets.c \ CSOURCES = ares__close_sockets.c \
ares__get_hostent.c \ ares__get_hostent.c \
ares__parse_into_addrinfo.c \
ares__readaddrinfo.c \
ares__sortaddrinfo.c \ ares__sortaddrinfo.c \
ares__read_line.c \ ares__read_line.c \
ares__timeval.c \ ares__timeval.c \
@ -13,6 +15,8 @@ CSOURCES = ares__close_sockets.c \
ares_fds.c \ ares_fds.c \
ares_free_hostent.c \ ares_free_hostent.c \
ares_free_string.c \ ares_free_string.c \
ares_freeaddrinfo.c \
ares_getaddrinfo.c \
ares_getenv.c \ ares_getenv.c \
ares_gethostbyaddr.c \ ares_gethostbyaddr.c \
ares_gethostbyname.c \ ares_gethostbyname.c \
@ -49,8 +53,7 @@ CSOURCES = ares__close_sockets.c \
bitncmp.c \ bitncmp.c \
inet_net_pton.c \ inet_net_pton.c \
inet_ntop.c \ inet_ntop.c \
windows_port.c \ windows_port.c
ares_getaddrinfo.c
HHEADERS = ares.h \ HHEADERS = ares.h \
ares_android.h \ ares_android.h \
@ -88,8 +91,10 @@ MANPAGES = ares_cancel.3 \
ares_free_data.3 \ ares_free_data.3 \
ares_free_hostent.3 \ ares_free_hostent.3 \
ares_free_string.3 \ ares_free_string.3 \
ares_freeaddrinfo.3 \
ares_get_servers.3 \ ares_get_servers.3 \
ares_get_servers_ports.3 \ ares_get_servers_ports.3 \
ares_getaddrinfo.3 \
ares_gethostbyaddr.3 \ ares_gethostbyaddr.3 \
ares_gethostbyname.3 \ ares_gethostbyname.3 \
ares_gethostbyname_file.3 \ ares_gethostbyname_file.3 \
@ -131,8 +136,7 @@ MANPAGES = ares_cancel.3 \
ares_set_sortlist.3 \ ares_set_sortlist.3 \
ares_strerror.3 \ ares_strerror.3 \
ares_timeout.3 \ ares_timeout.3 \
ares_version.3 \ ares_version.3
ares_getaddrinfo.3
HTMLPAGES = ares_cancel.html \ HTMLPAGES = ares_cancel.html \
ares_create_query.html \ ares_create_query.html \
@ -145,8 +149,10 @@ HTMLPAGES = ares_cancel.html \
ares_free_data.html \ ares_free_data.html \
ares_free_hostent.html \ ares_free_hostent.html \
ares_free_string.html \ ares_free_string.html \
ares_freeaddrinfo.html \
ares_get_servers.html \ ares_get_servers.html \
ares_get_servers_ports.html \ ares_get_servers_ports.html \
ares_getaddrinfo.html \
ares_gethostbyaddr.html \ ares_gethostbyaddr.html \
ares_gethostbyname.html \ ares_gethostbyname.html \
ares_gethostbyname_file.html \ ares_gethostbyname_file.html \
@ -187,8 +193,7 @@ HTMLPAGES = ares_cancel.html \
ares_set_sortlist.html \ ares_set_sortlist.html \
ares_strerror.html \ ares_strerror.html \
ares_timeout.html \ ares_timeout.html \
ares_version.html \ ares_version.html
ares_getaddrinfo.html
PDFPAGES = ares_cancel.pdf \ PDFPAGES = ares_cancel.pdf \
ares_create_query.pdf \ ares_create_query.pdf \
@ -201,8 +206,10 @@ PDFPAGES = ares_cancel.pdf \
ares_free_data.pdf \ ares_free_data.pdf \
ares_free_hostent.pdf \ ares_free_hostent.pdf \
ares_free_string.pdf \ ares_free_string.pdf \
ares_freeaddrinfo.pdf \
ares_get_servers.pdf \ ares_get_servers.pdf \
ares_get_servers_ports.pdf \ ares_get_servers_ports.pdf \
ares_getaddrinfo.pdf \
ares_gethostbyaddr.pdf \ ares_gethostbyaddr.pdf \
ares_gethostbyname.pdf \ ares_gethostbyname.pdf \
ares_gethostbyname_file.pdf \ ares_gethostbyname_file.pdf \
@ -243,8 +250,7 @@ PDFPAGES = ares_cancel.pdf \
ares_set_sortlist.pdf \ ares_set_sortlist.pdf \
ares_strerror.pdf \ ares_strerror.pdf \
ares_timeout.pdf \ ares_timeout.pdf \
ares_version.pdf \ ares_version.pdf
ares_getaddrinfo.pdf
SAMPLESOURCES = ares_getopt.c \ SAMPLESOURCES = ares_getopt.c \
ares_nowarn.c \ ares_nowarn.c \

@ -135,6 +135,9 @@ extern "C" {
/* More error codes */ /* More error codes */
#define ARES_ECANCELLED 24 /* introduced in 1.7.0 */ #define ARES_ECANCELLED 24 /* introduced in 1.7.0 */
/* More ares_getaddrinfo error codes */
#define ARES_ESERVICE 25 /* introduced in 1.?.0 */
/* Flag values */ /* Flag values */
#define ARES_FLAG_USEVC (1 << 0) #define ARES_FLAG_USEVC (1 << 0)
#define ARES_FLAG_PRIMARY (1 << 1) #define ARES_FLAG_PRIMARY (1 << 1)
@ -192,6 +195,8 @@ extern "C" {
#define ARES_AI_V4MAPPED (1 << 4) #define ARES_AI_V4MAPPED (1 << 4)
#define ARES_AI_ALL (1 << 5) #define ARES_AI_ALL (1 << 5)
#define ARES_AI_ADDRCONFIG (1 << 6) #define ARES_AI_ADDRCONFIG (1 << 6)
#define ARES_AI_NOSORT (1 << 7)
#define ARES_AI_ENVHOSTS (1 << 8)
/* Reserved for future use */ /* Reserved for future use */
#define ARES_AI_IDN (1 << 10) #define ARES_AI_IDN (1 << 10)
#define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11) #define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11)
@ -279,6 +284,7 @@ struct timeval;
struct sockaddr; struct sockaddr;
struct ares_channeldata; struct ares_channeldata;
struct ares_addrinfo; struct ares_addrinfo;
struct ares_addrinfo_hints;
typedef struct ares_channeldata *ares_channel; typedef struct ares_channeldata *ares_channel;
@ -307,7 +313,7 @@ typedef int (*ares_sock_config_callback)(ares_socket_t socket_fd,
int type, int type,
void *data); void *data);
typedef void (*ares_addr_callback)(void *arg, typedef void (*ares_addrinfo_callback)(void *arg,
int status, int status,
int timeouts, int timeouts,
struct ares_addrinfo *res); struct ares_addrinfo *res);
@ -376,9 +382,12 @@ CARES_EXTERN int ares_set_sortlist(ares_channel channel,
const char *sortstr); const char *sortstr);
CARES_EXTERN void ares_getaddrinfo(ares_channel channel, CARES_EXTERN void ares_getaddrinfo(ares_channel channel,
const char* node, const char* service, const char* node,
const struct ares_addrinfo* hints, const char* service,
ares_addr_callback callback, void* arg); const struct ares_addrinfo_hints* hints,
ares_addrinfo_callback callback,
void* arg);
CARES_EXTERN void ares_freeaddrinfo(struct ares_addrinfo* ai); CARES_EXTERN void ares_freeaddrinfo(struct ares_addrinfo* ai);
/* /*
@ -570,15 +579,42 @@ struct ares_soa_reply {
unsigned int minttl; 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 { struct ares_addrinfo {
int ai_flags; struct ares_addrinfo_cname *cnames;
int ai_family; struct ares_addrinfo_node *nodes;
int ai_socktype; };
int ai_protocol;
ares_socklen_t ai_addrlen; struct ares_addrinfo_hints {
char *ai_canonname; int ai_flags;
struct sockaddr *ai_addr; int ai_family;
struct ares_addrinfo *ai_next; int ai_socktype;
int ai_protocol;
}; };
/* /*

@ -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;
}

@ -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;
}

@ -54,7 +54,7 @@
struct addrinfo_sort_elem struct addrinfo_sort_elem
{ {
struct ares_addrinfo *ai; struct ares_addrinfo_node *ai;
int has_src_addr; int has_src_addr;
ares_sockaddr src_addr; ares_sockaddr src_addr;
int original_order; int original_order;
@ -439,9 +439,9 @@ static int find_src_addr(ares_channel channel,
* Sort the linked list starting at sentinel->ai_next in RFC6724 order. * Sort the linked list starting at sentinel->ai_next in RFC6724 order.
* Will leave the list unchanged if an error occurs. * 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 nelem = 0, i;
int has_src_addr; int has_src_addr;
struct addrinfo_sort_elem *elems; struct addrinfo_sort_elem *elems;

@ -45,6 +45,9 @@
/* Specifies the number of arguments to getservbyport_r */ /* Specifies the number of arguments to getservbyport_r */
#define GETSERVBYPORT_R_ARGS @GETSERVBYPORT_R_ARGS@ #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. */ /* Define to 1 if you have AF_INET6. */
#cmakedefine HAVE_AF_INET6 #cmakedefine HAVE_AF_INET6
@ -123,6 +126,9 @@
/* Define to 1 if you have the getservbyport_r function. */ /* Define to 1 if you have the getservbyport_r function. */
#cmakedefine HAVE_GETSERVBYPORT_R #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. */ /* Define to 1 if you have the `gettimeofday' function. */
#cmakedefine HAVE_GETTIMEOFDAY #cmakedefine HAVE_GETTIMEOFDAY

@ -20,16 +20,18 @@ ares_freeaddrinfo \- Free addrinfo structure allocated by ares functions
.nf .nf
.B #include <ares.h> .B #include <ares.h>
.PP .PP
.B void ares_freeaddrinfo(struct addrinfo *\fIai\fP) .B void ares_freeaddrinfo(struct ares_addrinfo *\fIai\fP)
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
The The
.B ares_freeaddrinfo .B ares_freeaddrinfo
function frees a function frees a
.B struct addrinfo .B struct ares_addrinfo
returned in \fIresult\fP of returned in \fIresult\fP of
.B ares_addr_callback .B ares_addrinfo_callback
.SH SEE ALSO .SH SEE ALSO
.BR ares_getaddrinfo (3), .BR ares_getaddrinfo (3),
.SH AUTHOR .SH AUTHOR
Christian Ammer Christian Ammer
.BR
Andrew Selivanov <andrew.selivanov@gmail.com>

@ -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);
}

@ -20,12 +20,12 @@ ares_getaddrinfo \- Initiate a host query by name
.nf .nf
.B #include <ares.h> .B #include <ares.h>
.PP .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) .B int \fItimeouts\fP, struct ares_addrinfo *\fIresult\fP)
.PP .PP
.B void ares_getaddrinfo(ares_channel \fIchannel\fP, const char *\fIname\fP, .B void ares_getaddrinfo(ares_channel \fIchannel\fP, const char *\fIname\fP,
.B const char* \fIservice\fP, const struct ares_addrinfo *\fIhints\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 .PP
.B struct ares_addrinfo { .B struct ares_addrinfo {
.B int \fIai_flags\fP; .B int \fIai_flags\fP;

File diff suppressed because it is too large Load Diff

@ -75,4 +75,6 @@ Memory was exhausted.
.SH AUTHOR .SH AUTHOR
Greg Hudson, MIT Information Systems Greg Hudson, MIT Information Systems
.br .br
Andrew Selivanov <andrew.selivanov@gmail.com>
.br
Copyright 1998 by the Massachusetts Institute of Technology. Copyright 1998 by the Massachusetts Institute of Technology.

@ -1,5 +1,6 @@
/* Copyright 1998 by the Massachusetts Institute of Technology. /* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2019 by Andrew Selivanov
* *
* Permission to use, copy, modify, and distribute this * Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without * 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 hostent **host,
struct ares_addrttl *addrttls, int *naddrttls) struct ares_addrttl *addrttls, int *naddrttls)
{ {
unsigned int qdcount, ancount; struct ares_addrinfo ai;
int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; struct ares_addrinfo_node *next;
int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ struct ares_addrinfo_cname *next_cname;
int naliases; char **aliases = NULL;
long len; char *question_hostname = NULL;
const unsigned char *aptr; struct hostent *hostent = NULL;
char *hostname, *rr_name, *rr_data, **aliases; struct in_addr *addrs = NULL;
struct in_addr *addrs; int naliases = 0, naddrs = 0, alias = 0, i;
struct hostent *hostent; int cname_ttl = INT_MAX;
const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; int status;
/* Set *host to NULL for all failure cases. */ memset(&ai, 0, sizeof(ai));
if (host)
*host = NULL; status = ares__parse_into_addrinfo2(abuf, alen, &question_hostname, &ai);
/* Same with *naddrttls. */ if (status != ARES_SUCCESS)
if (naddrttls) {
*naddrttls = 0; ares_free(question_hostname);
/* Give up if abuf doesn't have room for a header. */ if (naddrttls)
if (alen < HFIXEDSZ) {
return ARES_EBADRESP; *naddrttls = naddrs;
}
/* Fetch the question and answer count from the header. */ return status;
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. */ hostent = ares_malloc(sizeof(struct hostent));
aptr = abuf + HFIXEDSZ; if (!hostent)
status = ares__expand_name_for_response(aptr, abuf, alen, &hostname, &len);
if (status != ARES_SUCCESS)
return status;
if (aptr + len + QFIXEDSZ > abuf + alen)
{ {
ares_free(hostname); goto enomem;
return ARES_EBADRESP;
} }
aptr += len + QFIXEDSZ;
if (host) next = ai.nodes;
while (next)
{ {
/* Allocate addresses and aliases; ancount gives an upper bound for ++naddrs;
both. */ next = next->ai_next;
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;
}
} }
else
next_cname = ai.cnames;
while (next_cname)
{ {
addrs = NULL; if(next_cname->alias)
aliases = NULL; ++naliases;
next_cname = next_cname->next;
} }
naddrs = 0; aliases = ares_malloc((naliases + 1) * sizeof(char *));
naliases = 0; if (!aliases)
/* Examine each answer resource record (RR) in turn. */
for (i = 0; i < (int)ancount; i++)
{ {
/* Decode the RR up to the data field. */ goto enomem;
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;
}
if (rr_class == C_IN && rr_type == T_A if (naliases)
&& rr_len == sizeof(struct in_addr) {
&& strcasecmp(rr_name, hostname) == 0) next_cname = ai.cnames;
while (next_cname)
{ {
if (addrs) if(next_cname->alias)
{ aliases[alias++] = strdup(next_cname->alias);
if (aptr + sizeof(struct in_addr) > abuf + alen) if(next_cname->ttl < cname_ttl)
{ /* LCOV_EXCL_START: already checked above */ cname_ttl = next_cname->ttl;
ares_free(rr_name); next_cname = next_cname->next;
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 (rr_class == C_IN && rr_type == T_CNAME) aliases[alias] = NULL;
{
/* 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;
/* Take the min of the TTLs we see in the CNAME chain. */ hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (cname_ttl > rr_ttl) if (!hostent->h_addr_list)
cname_ttl = rr_ttl; {
} goto enomem;
else }
ares_free(rr_name);
aptr += rr_len; if (ai.cnames)
if (aptr > abuf + alen) {
{ /* LCOV_EXCL_START: already checked above */ hostent->h_name = strdup(ai.cnames->name);
status = ARES_EBADRESP; ares_free(question_hostname);
break; }
} /* LCOV_EXCL_STOP */ else
{
hostent->h_name = question_hostname;
} }
if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0) hostent->h_aliases = aliases;
/* the check for naliases to be zero is to make sure CNAME responses hostent->h_addrtype = AF_INET;
don't get caught here */ hostent->h_length = sizeof(struct in_addr);
status = ARES_ENODATA;
if (status == ARES_SUCCESS) if (naddrs)
{ {
/* We got our answer. */ addrs = ares_malloc(naddrs * sizeof(struct in_addr));
if (naddrttls) if (!addrs)
{ {
const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; goto enomem;
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;
} }
if (aliases)
aliases[naliases] = NULL; next = ai.nodes;
if (host) for (i = 0; i < naddrs; i++)
{ {
/* Allocate memory to build the host entry. */ hostent->h_addr_list[i] = (char*)&addrs[i];
hostent = ares_malloc(sizeof(struct hostent)); memcpy(hostent->h_addr_list[i], &(((struct sockaddr_in *)next->ai_addr)->sin_addr), sizeof(struct in_addr));
if (hostent) if (naddrttls)
{ {
hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); if(next->ai_ttl > cname_ttl)
if (hostent->h_addr_list) addrttls[i].ttl = cname_ttl;
{ else
/* Fill in the hostent and return successfully. */ addrttls[i].ttl = next->ai_ttl;
hostent->h_name = hostname;
hostent->h_aliases = aliases; memcpy(&addrttls[i].ipaddr, &(((struct sockaddr_in *)next->ai_addr)->sin_addr), sizeof(struct in_addr));
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);
} }
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) hostent->h_addr_list[naddrs] = NULL;
{
unsigned int qdcount, ancount;
const unsigned char* aptr;
long len;
/* Give up if abuf doesn't have room for a header. */ if (host)
if (alen < HFIXEDSZ) {
return ARES_EBADRESP; *host = hostent;
}
else
{
ares_free_hostent(hostent);
}
/* Fetch the question and answer count from the header. */ if (naddrttls)
qdcount = DNS_HEADER_QDCOUNT(abuf); {
ancount = DNS_HEADER_ANCOUNT(abuf); *naddrttls = naddrs;
if (qdcount != 1) }
return ARES_EBADRESP;
/* Expand the name from the question, and skip past the question. */ ares__freeaddrinfo_cnames(ai.cnames);
aptr = abuf + HFIXEDSZ; ares__freeaddrinfo_nodes(ai.nodes);
len = encoded_name_size(aptr, abuf + alen);
if (len == -1)
return ARES_EBADRESP;
if (aptr + len + QFIXEDSZ > abuf + alen)
return ARES_EBADRESP;
aptr += len;
if (!ancount)
return ARES_ENODATA;
*qtype = DNS__16BIT(aptr);
return ARES_SUCCESS; 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;
} }

@ -76,3 +76,5 @@ Memory was exhausted.
Dominick Meglio Dominick Meglio
.br .br
Copyright 2005 by Dominick Meglio. Copyright 2005 by Dominick Meglio.
.BR
Andrew Selivanov <andrew.selivanov@gmail.com>

@ -1,6 +1,7 @@
/* Copyright 1998 by the Massachusetts Institute of Technology. /* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright 2005 Dominick Meglio * Copyright 2005 Dominick Meglio
* Copyright (C) 2019 by Andrew Selivanov
* *
* Permission to use, copy, modify, and distribute this * Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without * 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, struct hostent **host, struct ares_addr6ttl *addrttls,
int *naddrttls) int *naddrttls)
{ {
unsigned int qdcount, ancount; struct ares_addrinfo ai;
int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; struct ares_addrinfo_node *next;
int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ struct ares_addrinfo_cname *next_cname;
int naliases; char **aliases = NULL;
long len; char *question_hostname = NULL;
const unsigned char *aptr; struct hostent *hostent = NULL;
char *hostname, *rr_name, *rr_data, **aliases; struct ares_in6_addr *addrs = NULL;
struct ares_in6_addr *addrs; int naliases = 0, naddrs = 0, alias = 0, i;
struct hostent *hostent; int cname_ttl = INT_MAX;
const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; int status;
/* Set *host to NULL for all failure cases. */ memset(&ai, 0, sizeof(ai));
if (host)
*host = NULL; status = ares__parse_into_addrinfo2(abuf, alen, &question_hostname, &ai);
/* Same with *naddrttls. */ if (status != ARES_SUCCESS)
if (naddrttls) {
*naddrttls = 0; ares_free(question_hostname);
/* Give up if abuf doesn't have room for a header. */ if (naddrttls)
if (alen < HFIXEDSZ) {
return ARES_EBADRESP; *naddrttls = naddrs;
}
/* Fetch the question and answer count from the header. */ return status;
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. */ hostent = ares_malloc(sizeof(struct hostent));
aptr = abuf + HFIXEDSZ; if (!hostent)
status = ares__expand_name_for_response(aptr, abuf, alen, &hostname, &len);
if (status != ARES_SUCCESS)
return status;
if (aptr + len + QFIXEDSZ > abuf + alen)
{ {
ares_free(hostname); goto enomem;
return ARES_EBADRESP;
} }
aptr += len + QFIXEDSZ;
/* Allocate addresses and aliases; ancount gives an upper bound for both. */ next = ai.nodes;
if (host) while (next)
{ {
addrs = ares_malloc(ancount * sizeof(struct ares_in6_addr)); ++naddrs;
if (!addrs) next = next->ai_next;
{
ares_free(hostname);
return ARES_ENOMEM;
}
aliases = ares_malloc((ancount + 1) * sizeof(char *));
if (!aliases)
{
ares_free(hostname);
ares_free(addrs);
return ARES_ENOMEM;
}
} }
else
next_cname = ai.cnames;
while (next_cname)
{ {
addrs = NULL; if(next_cname->alias)
aliases = NULL; ++naliases;
next_cname = next_cname->next;
} }
naddrs = 0;
naliases = 0;
/* Examine each answer resource record (RR) in turn. */ aliases = ares_malloc((naliases + 1) * sizeof(char *));
for (i = 0; i < (int)ancount; i++) if (!aliases)
{ {
/* Decode the RR up to the data field. */ goto enomem;
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;
}
if (rr_class == C_IN && rr_type == T_AAAA if (naliases)
&& rr_len == sizeof(struct ares_in6_addr) {
&& strcasecmp(rr_name, hostname) == 0) next_cname = ai.cnames;
while (next_cname)
{ {
if (addrs) if(next_cname->alias)
{ aliases[alias++] = strdup(next_cname->alias);
if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) if(next_cname->ttl < cname_ttl)
{ /* LCOV_EXCL_START: already checked above */ cname_ttl = next_cname->ttl;
ares_free(rr_name); next_cname = next_cname->next;
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 (rr_class == C_IN && rr_type == T_CNAME) aliases[alias] = NULL;
{
/* 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;
/* Take the min of the TTLs we see in the CNAME chain. */ hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (cname_ttl > rr_ttl) if (!hostent->h_addr_list)
cname_ttl = rr_ttl; {
} goto enomem;
else }
ares_free(rr_name);
aptr += rr_len; if (ai.cnames)
if (aptr > abuf + alen) {
{ /* LCOV_EXCL_START: already checked above */ hostent->h_name = strdup(ai.cnames->name);
status = ARES_EBADRESP; ares_free(question_hostname);
break;
} /* LCOV_EXCL_STOP */
} }
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 if (naddrs)
don't get caught here */
if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0)
status = ARES_ENODATA;
if (status == ARES_SUCCESS)
{ {
/* We got our answer. */ addrs = ares_malloc(naddrs * sizeof(struct ares_in6_addr));
if (naddrttls) if (!addrs)
{ {
const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; goto enomem;
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;
} }
if (aliases)
aliases[naliases] = NULL; next = ai.nodes;
if (host) for (i = 0; i < naddrs; i++)
{ {
/* Allocate memory to build the host entry. */ hostent->h_addr_list[i] = (char*)&addrs[i];
hostent = ares_malloc(sizeof(struct hostent)); memcpy(hostent->h_addr_list[i], &(((struct sockaddr_in6 *)next->ai_addr)->sin6_addr), sizeof(struct ares_in6_addr));
if (hostent) if (naddrttls)
{ {
hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *)); if(next->ai_ttl > cname_ttl)
if (hostent->h_addr_list) addrttls[i].ttl = cname_ttl;
{ else
/* Fill in the hostent and return successfully. */ addrttls[i].ttl = next->ai_ttl;
hostent->h_name = hostname;
hostent->h_aliases = aliases; memcpy(&addrttls[i].ip6addr, &(((struct sockaddr_in6 *)next->ai_addr)->sin6_addr), sizeof(struct ares_in6_addr));
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);
} }
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_hostent(hostent);
ares_free(aliases[i]);
ares_free(aliases);
} }
ares_free(addrs);
ares_free(hostname); if (naddrttls)
return status; {
*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;
} }

@ -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__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__single_domain(ares_channel channel, const char *name, char **s);
int ares__cat_domain(const char *name, const char *domain, 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 */ #if 0 /* Not used */
long ares__tvdiff(struct timeval t1, struct timeval t2); long ares__tvdiff(struct timeval t1, struct timeval t2);

@ -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 CARES_CHECK_FUNC_INET_NET_PTON
dnl ------------------------------------------------- dnl -------------------------------------------------
dnl Verify if inet_net_pton is available, prototyped, can dnl Verify if inet_net_pton is available, prototyped, can

@ -14,7 +14,6 @@ TESTSOURCES = ares-test-main.cc \
ares-test-parse-txt.cc \ ares-test-parse-txt.cc \
ares-test-misc.cc \ ares-test-misc.cc \
ares-test-live.cc \ ares-test-live.cc \
ares-test-live-ai.cc \
ares-test-mock.cc \ ares-test-mock.cc \
ares-test-mock-ai.cc \ ares-test-mock-ai.cc \
ares-test-internal.cc \ ares-test-internal.cc \

@ -356,6 +356,107 @@ TEST_F(LibraryTest, GetHostentAllocFail) {
fclose(fp); 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) { TEST(Misc, OnionDomain) {
EXPECT_EQ(0, ares__is_onion_domain("onion.no")); EXPECT_EQ(0, ares__is_onion_domain("onion.no"));
EXPECT_EQ(0, ares__is_onion_domain(".onion.no")); EXPECT_EQ(0, ares__is_onion_domain(".onion.no"));

@ -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

@ -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, unsigned char gdns_addr6[16] = {0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}; 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) { VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByNameV4) {
HostResult result; HostResult result;
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);

@ -18,7 +18,7 @@ MATCHER_P(IncludesNumAddresses, n, "") {
if(!arg) if(!arg)
return false; return false;
int cnt = 0; 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++; cnt++;
return n == cnt; return n == cnt;
} }
@ -29,7 +29,7 @@ MATCHER_P(IncludesV4Address, address, "") {
in_addr addressnum = {}; in_addr addressnum = {};
if (!ares_inet_pton(AF_INET, address, &addressnum)) if (!ares_inet_pton(AF_INET, address, &addressnum))
return false; // wrong number format? 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) if (ai->ai_family != AF_INET)
continue; continue;
if (reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr.s_addr == if (reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr.s_addr ==
@ -46,7 +46,7 @@ MATCHER_P(IncludesV6Address, address, "") {
if (!ares_inet_pton(AF_INET6, address, &addressnum)) { if (!ares_inet_pton(AF_INET6, address, &addressnum)) {
return false; // wrong number format? 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) if (ai->ai_family != AF_INET6)
continue; continue;
if (!memcmp( if (!memcmp(
@ -58,7 +58,7 @@ MATCHER_P(IncludesV6Address, address, "") {
} }
// UDP only so mock server doesn't get confused by concatenated requests // UDP only so mock server doesn't get confused by concatenated requests
TEST_P(MockUDPChannelTestAI, ParallelLookups) { TEST_P(MockUDPChannelTestAI, GetAddrInfoParallelLookups) {
DNSPacket rsp1; DNSPacket rsp1;
rsp1.set_response().set_aa() rsp1.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", ns_t_a)) .add_question(new DNSQuestion("www.google.com", ns_t_a))
@ -72,8 +72,9 @@ TEST_P(MockUDPChannelTestAI, ParallelLookups) {
ON_CALL(server_, OnRequest("www.example.com", ns_t_a)) ON_CALL(server_, OnRequest("www.example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp2)); .WillByDefault(SetReply(&server_, &rsp2));
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
AddrInfoResult result1; AddrInfoResult result1;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result1); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result1);
AddrInfoResult result2; AddrInfoResult result2;
@ -112,8 +113,9 @@ TEST_P(MockUDPChannelTestAI, TruncationRetry) {
.WillOnce(SetReply(&server_, &rspok)); .WillOnce(SetReply(&server_, &rspok));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -129,8 +131,9 @@ TEST_P(MockTCPChannelTestAI, MalformedResponse) {
.WillOnce(SetReplyData(&server_, one)); .WillOnce(SetReplyData(&server_, one));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -146,8 +149,9 @@ TEST_P(MockTCPChannelTestAI, FormErrResponse) {
.WillOnce(SetReply(&server_, &rsp)); .WillOnce(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -163,8 +167,9 @@ TEST_P(MockTCPChannelTestAI, ServFailResponse) {
.WillOnce(SetReply(&server_, &rsp)); .WillOnce(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -181,8 +186,9 @@ TEST_P(MockTCPChannelTestAI, NotImplResponse) {
.WillOnce(SetReply(&server_, &rsp)); .WillOnce(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -199,8 +205,9 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) {
.WillOnce(SetReply(&server_, &rsp)); .WillOnce(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -208,23 +215,23 @@ TEST_P(MockTCPChannelTestAI, RefusedResponse) {
EXPECT_EQ(ARES_ECONNREFUSED, result.status_); EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
} }
// TODO: make it work TEST_P(MockTCPChannelTestAI, YXDomainResponse) {
//TEST_P(MockTCPChannelTestAI, YXDomainResponse) { DNSPacket rsp;
// DNSPacket rsp; rsp.set_response().set_aa()
// rsp.set_response().set_aa() .add_question(new DNSQuestion("www.google.com", ns_t_a));
// .add_question(new DNSQuestion("www.google.com", ns_t_a)); rsp.set_rcode(ns_r_yxdomain);
// rsp.set_rcode(ns_r_yxdomain); EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a))
// EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) .WillOnce(SetReply(&server_, &rsp));
// .WillOnce(SetReply(&server_, &rsp));
// AddrInfoResult result;
// AddrInfoResult result; struct ares_addrinfo_hints hints = {};
// struct ares_addrinfo hints = {}; hints.ai_family = AF_INET;
// hints.ai_family = AF_INET; hints.ai_flags = ARES_AI_NOSORT;
// ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
// Process(); Process();
// EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
// EXPECT_EQ(ARES_ENODATA, result.status_); EXPECT_EQ(ARES_ENODATA, result.status_);
//} }
class MockExtraOptsTestAI class MockExtraOptsTestAI
: public MockChannelOptsTest, : public MockChannelOptsTest,
@ -260,8 +267,9 @@ TEST_P(MockExtraOptsTestAI, SimpleQuery) {
.WillByDefault(SetReply(&server_, &rsp)); .WillByDefault(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -300,8 +308,9 @@ TEST_P(MockNoCheckRespChannelTestAI, ServFailResponse) {
.WillByDefault(SetReply(&server_, &rsp)); .WillByDefault(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -317,8 +326,9 @@ TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) {
.WillByDefault(SetReply(&server_, &rsp)); .WillByDefault(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -334,8 +344,9 @@ TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) {
.WillByDefault(SetReply(&server_, &rsp)); .WillByDefault(SetReply(&server_, &rsp));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -352,8 +363,9 @@ TEST_P(MockChannelTestAI, FamilyV6) {
ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) ON_CALL(server_, OnRequest("example.com", ns_t_aaaa))
.WillByDefault(SetReply(&server_, &rsp6)); .WillByDefault(SetReply(&server_, &rsp6));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET6; hints.ai_family = AF_INET6;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints, ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AddrInfoCallback, &result); AddrInfoCallback, &result);
Process(); Process();
@ -370,8 +382,9 @@ TEST_P(MockChannelTestAI, FamilyV4) {
ON_CALL(server_, OnRequest("example.com", ns_t_a)) ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4)); .WillByDefault(SetReply(&server_, &rsp4));
AddrInfoResult result = {}; AddrInfoResult result = {};
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints, ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AddrInfoCallback, &result); AddrInfoCallback, &result);
Process(); Process();
@ -389,15 +402,16 @@ TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) {
ON_CALL(server_, OnRequest("example.com", ns_t_a)) ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4)); .WillByDefault(SetReply(&server_, &rsp4));
AddrInfoResult result = {}; AddrInfoResult result = {};
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints, ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AddrInfoCallback, &result); AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(2)); std::stringstream ss;
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5")); ss << result.ai_;
EXPECT_THAT(result.ai_, IncludesV4Address("7.8.9.0")); EXPECT_EQ("{addr=[2.3.4.5], addr=[7.8.9.0]}", ss.str());
} }
TEST_P(MockChannelTestAI, FamilyUnspecified) { TEST_P(MockChannelTestAI, FamilyUnspecified) {
@ -416,8 +430,9 @@ TEST_P(MockChannelTestAI, FamilyUnspecified) {
ON_CALL(server_, OnRequest("example.com", ns_t_a)) ON_CALL(server_, OnRequest("example.com", ns_t_a))
.WillByDefault(SetReply(&server_, &rsp4)); .WillByDefault(SetReply(&server_, &rsp4));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints, ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AddrInfoCallback, &result); AddrInfoCallback, &result);
Process(); Process();
@ -445,8 +460,9 @@ TEST_P(MockEDNSChannelTestAI, RetryWithoutEDNS) {
.WillOnce(SetReply(&server_, &rspok)); .WillOnce(SetReply(&server_, &rspok));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -473,8 +489,9 @@ TEST_P(MockChannelTestAI, SearchDomains) {
.WillByDefault(SetReply(&server_, &yesthird)); .WillByDefault(SetReply(&server_, &yesthird));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -518,8 +535,9 @@ TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
.WillByDefault(SetReply(&server_, &failthird4)); .WillByDefault(SetReply(&server_, &failthird4));
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -535,8 +553,9 @@ class MockMultiServerChannelTestAI
: MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {} : MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {}
void CheckExample() { void CheckExample() {
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo hints = {}; struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
@ -647,6 +666,26 @@ TEST_P(NoRotateMultiMockTestAI, ThirdServer) {
CheckExample(); 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 // force-tcp does currently not work, possibly test DNS server swallows
// bytes from second query // bytes from second query
//INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI, //INSTANTIATE_TEST_CASE_P(AddressFamiliesAI, MockChannelTestAI,

@ -51,7 +51,7 @@ TEST_P(MockChannelTest, Basic) {
} }
// UDP only so mock server doesn't get confused by concatenated requests // UDP only so mock server doesn't get confused by concatenated requests
TEST_P(MockUDPChannelTest, ParallelLookups) { TEST_P(MockUDPChannelTest, GetHostByNameParallelLookups) {
DNSPacket rsp1; DNSPacket rsp1;
rsp1.set_response().set_aa() rsp1.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", ns_t_a)) .add_question(new DNSQuestion("www.google.com", ns_t_a))

@ -567,7 +567,7 @@ void HostCallback(void *data, int status, int timeouts,
std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) { std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) {
os << '{'; os << '{';
if (result.done_) { if (result.done_ && result.ai_) {
os << StatusToString(result.status_) << " " << result.ai_; os << StatusToString(result.status_) << " " << result.ai_;
} else { } else {
os << "(incomplete)"; os << "(incomplete)";
@ -578,11 +578,25 @@ std::ostream& operator<<(std::ostream& os, const AddrInfoResult& result) {
std::ostream& operator<<(std::ostream& os, const AddrInfo& ai) { std::ostream& operator<<(std::ostream& os, const AddrInfo& ai) {
os << '{'; os << '{';
struct ares_addrinfo *next = ai.get(); struct ares_addrinfo_cname *next_cname = ai->cnames;
while(next) { while(next_cname) {
if(next->ai_canonname) { if(next_cname->alias) {
os << "'" << next->ai_canonname << "' "; 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; unsigned short port = 0;
os << "addr=["; os << "addr=[";
if(next->ai_family == AF_INET) { if(next->ai_family == AF_INET) {

@ -454,7 +454,6 @@ private:
} \ } \
void VCLASS_NAME(casename, testname)::InnerTestBody() void VCLASS_NAME(casename, testname)::InnerTestBody()
} // namespace test } // namespace test
} // namespace ares } // namespace ares

Loading…
Cancel
Save