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 @ki11roypull/262/head
parent
ba1aa0635a
commit
7d3591ee8a
25 changed files with 1899 additions and 929 deletions
@ -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; |
||||
} |
@ -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); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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
|
Loading…
Reference in new issue