A C library for asynchronous DNS requests (grpc依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

968 lines
38 KiB

/* MIT License
*
* Copyright (c) The c-ares project and its contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include "ares-test-ai.h"
#include "dns-proto.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <sstream>
#include <vector>
using testing::InvokeWithoutArgs;
using testing::DoAll;
namespace ares {
namespace test {
MATCHER_P(IncludesNumAddresses, n, "") {
if(!arg)
return false;
int cnt = 0;
for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next)
cnt++;
return n == cnt;
}
MATCHER_P(IncludesV4Address, address, "") {
if(!arg)
return false;
in_addr addressnum = {};
if (!ares_inet_pton(AF_INET, address, &addressnum))
return false; // wrong number format?
for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) {
if (ai->ai_family != AF_INET)
continue;
if (ai->ai_addrlen != sizeof(struct sockaddr_in))
continue;
if (reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr.s_addr ==
addressnum.s_addr)
return true; // found
}
return false;
}
MATCHER_P(IncludesV6Address, address, "") {
if(!arg)
return false;
in6_addr addressnum = {};
if (!ares_inet_pton(AF_INET6, address, &addressnum)) {
return false; // wrong number format?
}
for (const ares_addrinfo_node* ai = arg->nodes; ai != NULL; ai = ai->ai_next) {
if (ai->ai_family != AF_INET6)
continue;
if (ai->ai_addrlen != sizeof(struct sockaddr_in6))
continue;
if (!memcmp(
reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_addr.s6_addr,
addressnum.s6_addr, sizeof(addressnum.s6_addr)))
return true; // found
}
return false;
}
// UDP only so mock server doesn't get confused by concatenated requests
TEST_P(MockUDPChannelTestAI, GetAddrInfoParallelLookups) {
DNSPacket rsp1;
rsp1.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A))
.add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(SetReply(&server_, &rsp1));
DNSPacket rsp2;
rsp2.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_A))
.add_answer(new DNSARR("www.example.com", 100, {1, 2, 3, 4}));
ON_CALL(server_, OnRequest("www.example.com", T_A))
.WillByDefault(SetReply(&server_, &rsp2));
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;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result2);
AddrInfoResult result3;
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result3);
Process();
EXPECT_TRUE(result1.done_);
EXPECT_EQ(result1.status_, ARES_SUCCESS);
EXPECT_THAT(result1.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result1.ai_, IncludesV4Address("2.3.4.5"));
EXPECT_TRUE(result2.done_);
EXPECT_EQ(result2.status_, ARES_SUCCESS);
EXPECT_THAT(result2.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result2.ai_, IncludesV4Address("1.2.3.4"));
EXPECT_TRUE(result3.done_);
EXPECT_EQ(result3.status_, ARES_SUCCESS);
EXPECT_THAT(result3.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result3.ai_, IncludesV4Address("2.3.4.5"));
}
// UDP to TCP specific test
TEST_P(MockUDPChannelTestAI, TruncationRetry) {
DNSPacket rsptruncated;
rsptruncated.set_response().set_aa().set_tc()
.add_question(new DNSQuestion("www.google.com", T_A));
DNSPacket rspok;
rspok.set_response()
.add_question(new DNSQuestion("www.google.com", T_A))
.add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4}));
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
.WillOnce(SetReply(&server_, &rsptruncated))
.WillOnce(SetReply(&server_, &rspok));
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(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
}
TEST_P(MockTCPChannelTestAI, MalformedResponse) {
std::vector<byte> one = {0x01};
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(SetReplyData(&server_, one));
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_EBADRESP, result.status_);
}
TEST_P(MockTCPChannelTestAI, FormErrResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(FORMERR);
EXPECT_CALL(server_, OnRequest("www.google.com", 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_EFORMERR, result.status_);
}
TEST_P(MockTCPChannelTestAI, ServFailResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(SERVFAIL);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_ESERVFAIL, result.status_);
}
TEST_P(MockTCPChannelTestAI, NotImplResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(NOTIMP);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_ENOTIMP, result.status_);
}
TEST_P(MockTCPChannelTestAI, RefusedResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(REFUSED);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_EREFUSED, result.status_);
}
TEST_P(MockTCPChannelTestAI, YXDomainResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(YXDOMAIN);
EXPECT_CALL(server_, OnRequest("www.google.com", 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,
public ::testing::WithParamInterface< std::pair<int, bool> > {
public:
MockExtraOptsTestAI()
: MockChannelOptsTest(1, GetParam().first, GetParam().second,
FillOptions(&opts_),
ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {}
static struct ares_options* FillOptions(struct ares_options * opts) {
memset(opts, 0, sizeof(struct ares_options));
// Set a few options that affect socket communications
opts->socket_send_buffer_size = 514;
opts->socket_receive_buffer_size = 514;
return opts;
}
private:
struct ares_options opts_;
};
TEST_P(MockExtraOptsTestAI, SimpleQuery) {
ares_set_local_ip4(channel_, 0x7F000001);
byte addr6[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
ares_set_local_ip6(channel_, addr6);
ares_set_local_dev(channel_, "dummy");
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A))
.add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
class MockExtraOptsNDotsTestAI
: public MockChannelOptsTest,
public ::testing::WithParamInterface< std::pair<int, bool> > {
public:
MockExtraOptsNDotsTestAI(int ndots)
: MockChannelOptsTest(1, GetParam().first, GetParam().second,
FillOptions(&opts_, ndots),
ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF|ARES_OPT_NDOTS) {}
static struct ares_options* FillOptions(struct ares_options * opts, int ndots) {
memset(opts, 0, sizeof(struct ares_options));
// Set a few options that affect socket communications
opts->socket_send_buffer_size = 514;
opts->socket_receive_buffer_size = 514;
opts->ndots = ndots;
return opts;
}
private:
struct ares_options opts_;
};
class MockExtraOptsNDots5TestAI : public MockExtraOptsNDotsTestAI {
public:
MockExtraOptsNDots5TestAI() : MockExtraOptsNDotsTestAI(5) {}
};
TEST_P(MockExtraOptsNDots5TestAI, SimpleQuery) {
ares_set_local_ip4(channel_, 0x7F000001);
byte addr6[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
ares_set_local_ip6(channel_, addr6);
ares_set_local_dev(channel_, "dummy");
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("dynamodb.us-east-1.amazonaws.com", T_A))
.add_answer(new DNSARR("dynamodb.us-east-1.amazonaws.com", 100, {123, 45, 67, 8}));
ON_CALL(server_, OnRequest("dynamodb.us-east-1.amazonaws.com", T_A))
.WillByDefault(SetReply(&server_, &rsp));
AddrInfoResult result;
struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "dynamodb.us-east-1.amazonaws.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("123.45.67.8"));
}
class MockExtraOptsNDots0TestAI : public MockExtraOptsNDotsTestAI {
public:
MockExtraOptsNDots0TestAI() : MockExtraOptsNDotsTestAI(0) {}
};
TEST_P(MockExtraOptsNDots0TestAI, SimpleQuery) {
DNSPacket rsp_ndots0;
rsp_ndots0.set_response().set_aa()
.add_question(new DNSQuestion("ndots0", T_A))
.add_answer(new DNSARR("ndots0", 100, {1, 2, 3, 4}));
ON_CALL(server_, OnRequest("ndots0", T_A))
.WillByDefault(SetReply(&server_, &rsp_ndots0));
DNSPacket rsp_ndots0_first;
rsp_ndots0_first.set_response().set_aa()
.add_question(new DNSQuestion("ndots0.first.com", T_A))
.add_answer(new DNSARR("ndots0.first.com", 100, {99, 99, 99, 99}));
ON_CALL(server_, OnRequest("ndots0.first.com", T_A))
.WillByDefault(SetReply(&server_, &rsp_ndots0_first));
DNSPacket rsp_ndots0_second;
rsp_ndots0_second.set_response().set_aa()
.add_question(new DNSQuestion("ndots0.second.org", T_A))
.add_answer(new DNSARR("ndots0.second.org", 100, {98, 98, 98, 98}));
ON_CALL(server_, OnRequest("ndots0.second.org", T_A))
.WillByDefault(SetReply(&server_, &rsp_ndots0_second));
DNSPacket rsp_ndots0_third;
rsp_ndots0_third.set_response().set_aa()
.add_question(new DNSQuestion("ndots0.third.gov", T_A))
.add_answer(new DNSARR("ndots0.third.gov", 100, {97, 97, 97, 97}));
ON_CALL(server_, OnRequest("ndots0.third.gov", T_A))
.WillByDefault(SetReply(&server_, &rsp_ndots0_third));
AddrInfoResult result;
struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "ndots0", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_SUCCESS, result.status_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{addr=[1.2.3.4]}", ss.str());
}
class MockFlagsChannelOptsTestAI
: public MockChannelOptsTest,
public ::testing::WithParamInterface< std::pair<int, bool> > {
public:
MockFlagsChannelOptsTestAI(int flags)
: MockChannelOptsTest(1, GetParam().first, GetParam().second,
FillOptions(&opts_, flags), ARES_OPT_FLAGS) {}
static struct ares_options* FillOptions(struct ares_options * opts, int flags) {
memset(opts, 0, sizeof(struct ares_options));
opts->flags = flags;
return opts;
}
private:
struct ares_options opts_;
};
class MockNoCheckRespChannelTestAI : public MockFlagsChannelOptsTestAI {
public:
MockNoCheckRespChannelTestAI() : MockFlagsChannelOptsTestAI(ARES_FLAG_NOCHECKRESP) {}
};
TEST_P(MockNoCheckRespChannelTestAI, ServFailResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(SERVFAIL);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_ESERVFAIL, result.status_);
}
TEST_P(MockNoCheckRespChannelTestAI, NotImplResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(NOTIMP);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_ENOTIMP, result.status_);
}
TEST_P(MockNoCheckRespChannelTestAI, RefusedResponse) {
DNSPacket rsp;
rsp.set_response().set_aa()
.add_question(new DNSQuestion("www.google.com", T_A));
rsp.set_rcode(REFUSED);
ON_CALL(server_, OnRequest("www.google.com", T_A))
.WillByDefault(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_EREFUSED, result.status_);
}
TEST_P(MockChannelTestAI, FamilyV6) {
DNSPacket rsp6;
rsp6.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSAaaaRR("example.com", 100,
{0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03}));
ON_CALL(server_, OnRequest("example.com", T_AAAA))
.WillByDefault(SetReply(&server_, &rsp6));
AddrInfoResult result;
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();
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
}
// Test case for Issue #662
TEST_P(MockChannelTestAI, PartialQueryCancel) {
std::vector<byte> nothing;
DNSPacket reply;
reply.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_A))
.add_answer(new DNSARR("example.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
ON_CALL(server_, OnRequest("example.com", T_A))
.WillByDefault(SetReply(&server_, &reply));
ON_CALL(server_, OnRequest("example.com", T_AAAA))
.WillByDefault(SetReplyData(&server_, nothing));
AddrInfoResult result;
struct ares_addrinfo_hints hints = {};
hints.ai_family = AF_UNSPEC;
ares_getaddrinfo(channel_, "example.com.", NULL, &hints,
AddrInfoCallback, &result);
// After 100ms, issues ares_cancel(), this should be enough time for the A
// record reply, but before the timeout on the AAAA record.
Process(100);
EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ECANCELLED, result.status_);
}
TEST_P(MockChannelTestAI, FamilyV4) {
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_A))
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("example.com", 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.", NULL, &hints,
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
TEST_P(MockChannelTestAI, FamilyV4_MultipleAddresses) {
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_A))
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}))
.add_answer(new DNSARR("example.com", 100, {7, 8, 9, 0}));
ON_CALL(server_, OnRequest("example.com", 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.", NULL, &hints,
AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{addr=[2.3.4.5], addr=[7.8.9.0]}", ss.str());
}
TEST_P(MockChannelTestAI, FamilyUnspecified) {
DNSPacket rsp6;
rsp6.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSAaaaRR("example.com", 100,
{0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03}));
ON_CALL(server_, OnRequest("example.com", T_AAAA))
.WillByDefault(SetReply(&server_, &rsp6));
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_A))
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("example.com", T_A))
.WillByDefault(SetReply(&server_, &rsp4));
AddrInfoResult result;
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();
EXPECT_TRUE(result.done_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(2));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
EXPECT_THAT(result.ai_, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
}
class MockEDNSChannelTestAI : public MockFlagsChannelOptsTestAI {
public:
MockEDNSChannelTestAI() : MockFlagsChannelOptsTestAI(ARES_FLAG_EDNS) {}
};
TEST_P(MockEDNSChannelTestAI, RetryWithoutEDNS) {
DNSPacket rspfail;
rspfail.set_response().set_aa().set_rcode(FORMERR)
.add_question(new DNSQuestion("www.google.com", T_A));
DNSPacket rspok;
rspok.set_response()
.add_question(new DNSQuestion("www.google.com", T_A))
.add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4}));
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
.WillOnce(SetReply(&server_, &rspfail))
.WillOnce(SetReply(&server_, &rspok));
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_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
}
TEST_P(MockChannelTestAI, SearchDomains) {
DNSPacket nofirst;
nofirst.set_response().set_aa().set_rcode(NXDOMAIN)
.add_question(new DNSQuestion("www.first.com", T_A));
ON_CALL(server_, OnRequest("www.first.com", T_A))
.WillByDefault(SetReply(&server_, &nofirst));
DNSPacket nosecond;
nosecond.set_response().set_aa().set_rcode(NXDOMAIN)
.add_question(new DNSQuestion("www.second.org", T_A));
ON_CALL(server_, OnRequest("www.second.org", T_A))
.WillByDefault(SetReply(&server_, &nosecond));
DNSPacket yesthird;
yesthird.set_response().set_aa()
.add_question(new DNSQuestion("www.third.gov", T_A))
.add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.third.gov", T_A))
.WillByDefault(SetReply(&server_, &yesthird));
AddrInfoResult result;
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_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
TEST_P(MockChannelTestAI, SearchDomainsServFailOnAAAA) {
DNSPacket nofirst;
nofirst.set_response().set_aa().set_rcode(NXDOMAIN)
.add_question(new DNSQuestion("www.first.com", T_AAAA));
ON_CALL(server_, OnRequest("www.first.com", T_AAAA))
.WillByDefault(SetReply(&server_, &nofirst));
DNSPacket nofirst4;
nofirst4.set_response().set_aa().set_rcode(NXDOMAIN)
.add_question(new DNSQuestion("www.first.com", T_A));
ON_CALL(server_, OnRequest("www.first.com", T_A))
.WillByDefault(SetReply(&server_, &nofirst4));
DNSPacket nosecond;
nosecond.set_response().set_aa().set_rcode(NXDOMAIN)
.add_question(new DNSQuestion("www.second.org", T_AAAA));
ON_CALL(server_, OnRequest("www.second.org", T_AAAA))
.WillByDefault(SetReply(&server_, &nosecond));
DNSPacket yessecond4;
yessecond4.set_response().set_aa()
.add_question(new DNSQuestion("www.second.org", T_A))
.add_answer(new DNSARR("www.second.org", 0x0200, {2, 3, 4, 5}));
ON_CALL(server_, OnRequest("www.second.org", T_A))
.WillByDefault(SetReply(&server_, &yessecond4));
DNSPacket failthird;
failthird.set_response().set_aa().set_rcode(SERVFAIL)
.add_question(new DNSQuestion("www.third.gov", T_AAAA));
ON_CALL(server_, OnRequest("www.third.gov", T_AAAA))
.WillByDefault(SetReply(&server_, &failthird));
DNSPacket failthird4;
failthird4.set_response().set_aa().set_rcode(SERVFAIL)
.add_question(new DNSQuestion("www.third.gov", T_A));
ON_CALL(server_, OnRequest("www.third.gov", T_A))
.WillByDefault(SetReply(&server_, &failthird4));
AddrInfoResult result;
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_);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
class MockMultiServerChannelTestAI
: public MockChannelOptsTest,
public ::testing::WithParamInterface< std::pair<int, bool> > {
public:
MockMultiServerChannelTestAI(ares_options *opts, int optmask)
: MockChannelOptsTest(3, GetParam().first, GetParam().second, opts, optmask) {}
void CheckExample() {
AddrInfoResult result;
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_);
EXPECT_EQ(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("2.3.4.5"));
}
};
class NoRotateMultiMockTestAI : public MockMultiServerChannelTestAI {
public:
NoRotateMultiMockTestAI() : MockMultiServerChannelTestAI(nullptr, ARES_OPT_NOROTATE) {}
};
TEST_P(NoRotateMultiMockTestAI, ThirdServer) {
struct ares_options opts;
int optmask = 0;
memset(&opts, 0, sizeof(opts));
EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel_, &opts, &optmask));
EXPECT_EQ(ARES_OPT_NOROTATE, (optmask & ARES_OPT_NOROTATE));
ares_destroy_options(&opts);
DNSPacket servfailrsp;
servfailrsp.set_response().set_aa().set_rcode(SERVFAIL)
.add_question(new DNSQuestion("www.example.com", T_A));
DNSPacket notimplrsp;
notimplrsp.set_response().set_aa().set_rcode(NOTIMP)
.add_question(new DNSQuestion("www.example.com", T_A));
DNSPacket okrsp;
okrsp.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_A))
.add_answer(new DNSARR("www.example.com", 100, {2,3,4,5}));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &servfailrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &notimplrsp));
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &okrsp));
CheckExample();
// Second time around, still starts from server [2], as [0] and [1] both
// recorded failures
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &servfailrsp));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &notimplrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &okrsp));
CheckExample();
// Third time around, server order is [1] (f0), [2] (f1), [0] (f2), which
// means [1] will get called twice in a row as after the first call
// order will be [1] (f1), [2] (f1), [0] (f2) since sort order is
// (failure count, index)
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &servfailrsp))
.WillOnce(SetReply(servers_[1].get(), &notimplrsp));
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &notimplrsp));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &okrsp));
CheckExample();
}
#define SERVER_FAILOVER_RETRY_DELAY 750
class ServerFailoverOptsMockTestAI : public MockMultiServerChannelTestAI {
public:
ServerFailoverOptsMockTestAI()
: MockMultiServerChannelTestAI(FillOptions(&opts_),
ARES_OPT_SERVER_FAILOVER | ARES_OPT_NOROTATE) {}
static struct ares_options* FillOptions(struct ares_options *opts) {
memset(opts, 0, sizeof(struct ares_options));
opts->server_failover_opts.retry_chance = 1;
opts->server_failover_opts.retry_delay = SERVER_FAILOVER_RETRY_DELAY;
return opts;
}
private:
struct ares_options opts_;
};
// Test case to trigger server failover behavior. We use a retry chance of
// 100% and a retry delay so that we can test behavior reliably.
TEST_P(ServerFailoverOptsMockTestAI, ServerFailoverOpts) {
DNSPacket servfailrsp;
servfailrsp.set_response().set_aa().set_rcode(SERVFAIL)
.add_question(new DNSQuestion("www.example.com", T_A));
DNSPacket okrsp;
okrsp.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_A))
.add_answer(new DNSARR("www.example.com", 100, {2,3,4,5}));
auto tv_begin = std::chrono::high_resolution_clock::now();
auto tv_now = std::chrono::high_resolution_clock::now();
unsigned int delay_ms;
// 1. If all servers are healthy, then the first server should be selected.
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: First server should be selected" << std::endl;
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &okrsp));
CheckExample();
// 2. Failed servers should be retried after the retry delay.
//
// Fail server #0 but leave server #1 as healthy.
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: Server0 will fail but leave Server1 as healthy" << std::endl;
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &servfailrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &okrsp));
CheckExample();
// Sleep for the retry delay (actually a little more than the retry delay to account
// for unreliable timing, e.g. NTP slew) and send in another query. Server #0
// should be retried.
tv_now = std::chrono::high_resolution_clock::now();
delay_ms = SERVER_FAILOVER_RETRY_DELAY + (SERVER_FAILOVER_RETRY_DELAY / 10);
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: sleep " << delay_ms << "ms" << std::endl;
ares_sleep_time(delay_ms);
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: Server0 should be past retry delay and should be tried again successfully" << std::endl;
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &okrsp));
CheckExample();
// 3. If there are multiple failed servers, then the servers should be
// retried in sorted order.
//
// Fail all servers for the first round of tries. On the second round server
// #1 responds successfully.
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: All 3 servers will fail on the first attempt. On second attempt, Server0 will fail, but Server1 will answer correctly." << std::endl;
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &servfailrsp))
.WillOnce(SetReply(servers_[0].get(), &servfailrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &servfailrsp))
.WillOnce(SetReply(servers_[1].get(), &okrsp));
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &servfailrsp));
CheckExample();
// At this point the sorted servers look like [1] (f0) [2] (f1) [0] (f2).
// Sleep for the retry delay and send in another query. Server #2 should be
// retried first, and then server #0.
tv_now = std::chrono::high_resolution_clock::now();
delay_ms = SERVER_FAILOVER_RETRY_DELAY + (SERVER_FAILOVER_RETRY_DELAY / 10);
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: sleep " << delay_ms << "ms" << std::endl;
ares_sleep_time(delay_ms);
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: Past retry delay, so will choose Server2 and Server0 that are down. Server2 will fail but Server0 will succeed." << std::endl;
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &servfailrsp));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &okrsp));
CheckExample();
// Test might take a while to run and the sleep may not be accurate, so we
// want to track this interval otherwise we may not pass the last test case
// on slow systems.
auto elapse_start = tv_now;
// 4. If there are multiple failed servers, then servers which have not yet
// met the retry delay should be skipped.
//
// The sorted servers currently look like [0] (f0) [1] (f0) [2] (f2) and
// server #2 has just been retried.
// Sleep for 1/2 the retry delay and trigger a failure on server #0.
tv_now = std::chrono::high_resolution_clock::now();
delay_ms = (SERVER_FAILOVER_RETRY_DELAY/2);
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: sleep " << delay_ms << "ms" << std::endl;
ares_sleep_time(delay_ms);
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: Retry delay has not been hit yet. Server0 was last successful, so should be tried first (and will fail), Server1 is also healthy so will respond." << std::endl;
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &servfailrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &okrsp));
CheckExample();
// The sorted servers now look like [1] (f0) [0] (f1) [2] (f2). Server #0
// has just failed whilst server #2 is somewhere in its retry delay.
// Sleep until we know server #2s retry delay has elapsed but Server #0 has
// not.
tv_now = std::chrono::high_resolution_clock::now();
unsigned int elapsed_time = (unsigned int)std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - elapse_start).count();
delay_ms = (SERVER_FAILOVER_RETRY_DELAY) + (SERVER_FAILOVER_RETRY_DELAY / 10);
if (elapsed_time > delay_ms) {
if (verbose) std::cerr << "elapsed duration " << elapsed_time << "ms greater than desired delay of " << delay_ms << "ms, not sleeping" << std::endl;
} else {
delay_ms -= elapsed_time; // subtract already elapsed time
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: sleep " << delay_ms << "ms" << std::endl;
ares_sleep_time(delay_ms);
}
tv_now = std::chrono::high_resolution_clock::now();
if (verbose) std::cerr << std::chrono::duration_cast<std::chrono::milliseconds>(tv_now - tv_begin).count() << "ms: Retry delay has expired on Server2 but not Server0, will try on Server2 and fail, then Server1 will answer" << std::endl;
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[2].get(), &servfailrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &okrsp));
CheckExample();
}
TEST_P(MockChannelTestAI, FamilyV4ServiceName) {
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("example.com", 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", 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());
}
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockChannelTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockUDPChannelTestAI,
::testing::ValuesIn(ares::test::families), PrintFamily);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockTCPChannelTestAI,
::testing::ValuesIn(ares::test::families), PrintFamily);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockExtraOptsTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockExtraOptsNDots5TestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockExtraOptsNDots0TestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockNoCheckRespChannelTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockEDNSChannelTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(TransportModesAI, NoRotateMultiMockTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
INSTANTIATE_TEST_SUITE_P(TransportModesAI, ServerFailoverOptsMockTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
} // namespace test
} // namespace ares