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. 50
      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. 851
      ares_getaddrinfo.c
  12. 2
      ares_parse_a_reply.3
  13. 305
      ares_parse_a_reply.c
  14. 2
      ares_parse_aaaa_reply.3
  15. 269
      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 (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)

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

@ -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;
};
struct ares_addrinfo {
/*
* 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;
char *ai_canonname;
struct sockaddr *ai_addr;
struct ares_addrinfo *ai_next;
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_cname *cnames;
struct ares_addrinfo_node *nodes;
};
struct ares_addrinfo_hints {
int ai_flags;
int ai_family;
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 ares_addrinfo *ai;
struct ares_addrinfo_node *ai;
int has_src_addr;
ares_sockaddr src_addr;
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.
* 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;

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

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

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

@ -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,116 +59,382 @@
# 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;
struct ares_addrinfo_hints hints;
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;
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);
while (last->next)
{
last = last->next;
}
last->next = tail;
return tail;
}
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;
}
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);
while (last->next)
{
last = last->next;
}
last->next = tail;
}
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;
}
struct ares_addrinfo_node *ares__malloc_addrinfo_node()
{
struct ares_addrinfo_node *node =
ares_malloc(sizeof(struct ares_addrinfo_node));
if (!node)
return NULL;
*node = empty_addrinfo_node;
return node;
}
/* 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;
}
while (last->ai_next)
{
last = last->ai_next;
}
last->ai_next = tail;
return tail;
}
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;
}
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);
while (last->ai_next)
{
last = last->ai_next;
}
else {
next_dns_lookup(hquery);
last->ai_next = tail;
}
/* 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
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);
}
return 0;
}
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);
/* 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);
}
}
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);
}
if (!result)
return 0;
node = ares__malloc_addrinfo_node();
if (!node)
{
ares_freeaddrinfo(ai);
callback(arg, ARES_ENOMEM, 0, NULL);
return 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;
}
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;
}
}
callback(arg, ARES_SUCCESS, 0, ai);
return 1;
}
static int is_implemented(const int family) {
return
family == AF_INET ||
family == AF_INET6 ||
family == AF_UNSPEC;
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;
}
}
else
{
/* Clean up what we have collected by so far. */
ares_freeaddrinfo(hquery->ai);
hquery->ai = NULL;
}
hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai);
ares_free(hquery->name);
ares_free(hquery);
}
static int file_lookup(const char *name, int family, struct ares_addrinfo **ai) {
static int file_lookup(struct host_query *hquery)
{
FILE *fp;
char **alias;
int status;
int error;
struct hostent *host = NULL;
int status;
const char *path_hosts = NULL;
if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS)
{
path_hosts = getenv("CARES_HOSTS");
}
if (!path_hosts)
{
#ifdef WIN32
char PATH_HOSTS[MAX_PATH];
win_platform platform;
@ -169,12 +443,14 @@ static int file_lookup(const char *name, int family, struct ares_addrinfo **ai)
platform = ares__getplatform();
if (platform == WIN_NT) {
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) {
&hkeyHosts) == ERROR_SUCCESS)
{
DWORD dwLength = MAX_PATH;
RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
&dwLength);
@ -182,311 +458,224 @@ static int file_lookup(const char *name, int family, struct ares_addrinfo **ai)
RegCloseKey(hkeyHosts);
}
}
else if (platform == WIN_9X) {
else if (platform == WIN_9X)
GetWindowsDirectory(PATH_HOSTS, MAX_PATH);
}
else {
else
return ARES_ENOTFOUND;
}
strcat(PATH_HOSTS, WIN_PATH_HOSTS);
path_hosts = PATH_HOSTS;
#elif defined(WATT32)
extern const char *_w32_GetHostsFile (void);
const char *PATH_HOSTS = _w32_GetHostsFile();
if (!PATH_HOSTS) {
if (!PATH_HOSTS)
return ARES_ENOTFOUND;
}
#endif
path_hosts = PATH_HOSTS;
}
fp = fopen(PATH_HOSTS, "r");
if (!fp) {
fp = fopen(path_hosts, "r");
if (!fp)
{
error = ERRNO;
switch(error) {
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;
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;
}
}
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;
}
}
}
ares_free_hostent(host);
}
status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai);
fclose(fp);
return status;
}
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);
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 {
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);
}
}
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;
}
hquery->next_domain = 0;
}
/* if as_is_first is false, try hquery->name at last */
if(!s && hquery->next_domain == hquery->channel->ndomains) {
if(!as_is_first(hquery)) {
s = hquery->name;
}
/* avoid infinite loop */
hquery->next_domain++;
hquery->sent_family = AF_INET;
ares_search(hquery->channel, hquery->name, C_IN, T_A,
host_callback, hquery);
}
return;
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;
}
}
case 'f':
/* Host file lookup */
status = file_lookup(hquery);
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);
}
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);
}
if (is_s_allocated) {
ares_free(s);
/* 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 {
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);
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;
ares_channel channel = hquery->channel;
struct hostent *host = NULL;
int qtype;
int qtypestatus;
int addinfostatus = ARES_SUCCESS;
unsigned char *abuf, int alen)
{
struct host_query *hquery = (struct host_query *) arg;
hquery->timeouts += timeouts;
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->remaining) {
if (addinfostatus != ARES_SUCCESS) {
/* no memory */
end_hquery(hquery, addinfostatus);
}
else if (hquery->ai) {
/* at least one query ended with ARES_SUCCESS */
end_hquery(hquery, ARES_SUCCESS);
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;
}
else if (status == ARES_ENOTFOUND) {
next_dns_lookup(hquery);
}
else {
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;
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;
/* 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;
if (!hints)
{
hints = &default_hints;
}
memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
}
memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
}
}
/* Find the first entry in sortlist which matches addr. Return nsort
* if none of them match.
*/
static int get_address_index(const struct in_addr *addr,
const struct apattern *sortlist,
int nsort) {
int i;
for (i = 0; i < nsort; i++) {
if (sortlist[i].family != AF_INET) {
continue;
}
if (sortlist[i].type == PATTERN_MASK) {
if ((addr->s_addr & sortlist[i].mask.addr4.s_addr) ==
sortlist[i].addrV4.s_addr) {
break;
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 (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 {
if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr,
sortlist[i].mask.bits)) {
break;
}
else
{
port = lookup_service(service, 0);
if (!port)
{
port = (unsigned short)strtoul(service, NULL, 0);
if (!port)
{
callback(arg, ARES_ESERVICE, 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));
}
memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
ai = ares__malloc_addrinfo();
if (!ai)
{
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
}
/* 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;
if (fake_addrinfo(name, port, hints, ai, callback, arg))
{
return;
}
return i;
}
static int as_is_first(const struct host_query* hquery) {
char* p;
int ndots = 0;
for (p = hquery->name; *p; p++) {
if (*p == '.') {
ndots++;
/* 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;
}
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 */);
}

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

@ -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;
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);
/* Set *host to NULL for all failure cases. */
if (host)
*host = NULL;
/* Same with *naddrttls. */
if (naddrttls)
*naddrttls = 0;
/* 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, &hostname, &len);
if (status != ARES_SUCCESS)
return status;
if (aptr + len + QFIXEDSZ > abuf + alen)
{
ares_free(hostname);
return ARES_EBADRESP;
*naddrttls = naddrs;
}
aptr += len + QFIXEDSZ;
if (host)
{
/* 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;
return status;
}
aliases = ares_malloc((ancount + 1) * sizeof(char *));
if (!aliases)
hostent = ares_malloc(sizeof(struct hostent));
if (!hostent)
{
ares_free(hostname);
ares_free(addrs);
return ARES_ENOMEM;
}
goto enomem;
}
else
next = ai.nodes;
while (next)
{
addrs = NULL;
aliases = NULL;
++naddrs;
next = next->ai_next;
}
naddrs = 0;
naliases = 0;
/* 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)
break;
aptr += len;
if (aptr + RRFIXEDSZ > abuf + alen)
next_cname = ai.cnames;
while (next_cname)
{
ares_free(rr_name);
status = ARES_EBADRESP;
break;
if(next_cname->alias)
++naliases;
next_cname = next_cname->next;
}
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)
aliases = ares_malloc((naliases + 1) * sizeof(char *));
if (!aliases)
{
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)
{
if (addrs)
next_cname = ai.cnames;
while (next_cname)
{
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(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 (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)
{
/* Record the RR name as an alias. */
if (aliases)
aliases[naliases] = rr_name;
else
ares_free(rr_name);
naliases++;
aliases[alias] = NULL;
/* 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;
hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (!hostent->h_addr_list)
{
goto enomem;
}
/* Take the min of the TTLs we see in the CNAME chain. */
if (cname_ttl > rr_ttl)
cname_ttl = rr_ttl;
if (ai.cnames)
{
hostent->h_name = strdup(ai.cnames->name);
ares_free(question_hostname);
}
else
ares_free(rr_name);
{
hostent->h_name = question_hostname;
}
aptr += rr_len;
if (aptr > abuf + alen)
{ /* LCOV_EXCL_START: already checked above */
status = ARES_EBADRESP;
break;
} /* LCOV_EXCL_STOP */
hostent->h_aliases = aliases;
hostent->h_addrtype = AF_INET;
hostent->h_length = sizeof(struct in_addr);
if (naddrs)
{
addrs = ares_malloc(naddrs * sizeof(struct in_addr));
if (!addrs)
{
goto enomem;
}
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)
next = ai.nodes;
for (i = 0; i < naddrs; i++)
{
/* We got our answer. */
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)
{
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)
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));
}
*naddrttls = n;
next = next->ai_next;
}
if (aliases)
aliases[naliases] = NULL;
}
hostent->h_addr_list[naddrs] = NULL;
if (host)
{
/* Allocate memory to build the host entry. */
hostent = ares_malloc(sizeof(struct hostent));
if (hostent)
{
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);
}
status = ARES_ENOMEM;
}
}
if (aliases)
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;
}
/* returned size includes terminating 0. */
static long encoded_name_size(const unsigned char *begin, const unsigned char *end)
{
const unsigned char* it = begin;
for (; *it && it != end; ++it);
return it == end ? -1 : (long)((it + 1) - begin);
}
int ares__parse_qtype_reply(const unsigned char* abuf, int alen, int* qtype)
{
unsigned int qdcount, ancount;
const unsigned char* aptr;
long len;
/* Give up if abuf doesn't have room for a header. */
if (alen < HFIXEDSZ)
return ARES_EBADRESP;
/* Fetch the question and answer count from the header. */
qdcount = DNS_HEADER_QDCOUNT(abuf);
ancount = DNS_HEADER_ANCOUNT(abuf);
if (qdcount != 1)
return ARES_EBADRESP;
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;
}

@ -76,3 +76,5 @@ Memory was exhausted.
Dominick Meglio
.br
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 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;
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);
/* Set *host to NULL for all failure cases. */
if (host)
*host = NULL;
/* Same with *naddrttls. */
if (naddrttls)
*naddrttls = 0;
/* 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, &hostname, &len);
if (status != ARES_SUCCESS)
return status;
if (aptr + len + QFIXEDSZ > abuf + alen)
{
ares_free(hostname);
return ARES_EBADRESP;
*naddrttls = naddrs;
}
aptr += len + QFIXEDSZ;
/* Allocate addresses and aliases; ancount gives an upper bound for both. */
if (host)
{
addrs = ares_malloc(ancount * sizeof(struct ares_in6_addr));
if (!addrs)
{
ares_free(hostname);
return ARES_ENOMEM;
return status;
}
aliases = ares_malloc((ancount + 1) * sizeof(char *));
if (!aliases)
hostent = ares_malloc(sizeof(struct hostent));
if (!hostent)
{
ares_free(hostname);
ares_free(addrs);
return ARES_ENOMEM;
}
goto enomem;
}
else
next = ai.nodes;
while (next)
{
addrs = NULL;
aliases = NULL;
++naddrs;
next = next->ai_next;
}
naddrs = 0;
naliases = 0;
/* Examine each answer resource record (RR) in turn. */
for (i = 0; i < (int)ancount; i++)
next_cname = ai.cnames;
while (next_cname)
{
/* 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;
if(next_cname->alias)
++naliases;
next_cname = next_cname->next;
}
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)
aliases = ares_malloc((naliases + 1) * sizeof(char *));
if (!aliases)
{
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 (addrs)
if (naliases)
{
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)
next_cname = ai.cnames;
while (next_cname)
{
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;
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;
}
naddrs++;
status = ARES_SUCCESS;
}
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++;
aliases[alias] = NULL;
/* 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;
hostent->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (!hostent->h_addr_list)
{
goto enomem;
}
/* Take the min of the TTLs we see in the CNAME chain. */
if (cname_ttl > rr_ttl)
cname_ttl = rr_ttl;
if (ai.cnames)
{
hostent->h_name = strdup(ai.cnames->name);
ares_free(question_hostname);
}
else
ares_free(rr_name);
{
hostent->h_name = question_hostname;
}
hostent->h_aliases = aliases;
hostent->h_addrtype = AF_INET6;
hostent->h_length = sizeof(struct ares_in6_addr);
aptr += rr_len;
if (aptr > abuf + alen)
{ /* LCOV_EXCL_START: already checked above */
status = ARES_EBADRESP;
break;
} /* LCOV_EXCL_STOP */
if (naddrs)
{
addrs = ares_malloc(naddrs * sizeof(struct ares_in6_addr));
if (!addrs)
{
goto enomem;
}
/* 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)
next = ai.nodes;
for (i = 0; i < naddrs; i++)
{
/* We got our answer. */
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)
{
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)
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));
}
next = next->ai_next;
}
*naddrttls = n;
}
if (aliases)
aliases[naliases] = NULL;
hostent->h_addr_list[naddrs] = NULL;
if (host)
{
/* Allocate memory to build the host entry. */
hostent = ares_malloc(sizeof(struct hostent));
if (hostent)
{
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);
}
status = ARES_ENOMEM;
}
else
{
ares_free_hostent(hostent);
}
if (aliases)
if (naddrttls)
{
for (i = 0; i < naliases; i++)
ares_free(aliases[i]);
ares_free(aliases);
*naddrttls = naddrs;
}
ares_free(addrs);
ares_free(hostname);
return status;
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__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);

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

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

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

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

@ -18,7 +18,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;
}
@ -29,7 +29,7 @@ MATCHER_P(IncludesV4Address, address, "") {
in_addr addressnum = {};
if (!ares_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 ==
@ -46,7 +46,7 @@ MATCHER_P(IncludesV6Address, address, "") {
if (!ares_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(
@ -58,7 +58,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))
@ -72,8 +72,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;
@ -112,8 +113,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_);
@ -129,8 +131,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_);
@ -146,8 +149,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_);
@ -163,8 +167,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_);
@ -181,8 +186,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_);
@ -199,8 +205,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_);
@ -208,23 +215,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,
@ -260,8 +267,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_);
@ -300,8 +308,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_);
@ -317,8 +326,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_);
@ -334,8 +344,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_);
@ -352,8 +363,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();
@ -370,8 +382,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();
@ -389,15 +402,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) {
@ -416,8 +430,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();
@ -445,8 +460,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_);
@ -473,8 +489,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_);
@ -518,8 +535,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_);
@ -535,8 +553,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_);
@ -647,6 +666,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,

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

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

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

Loading…
Cancel
Save