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.

767 lines
28 KiB

/*
* Copyright (C) The c-ares project
*
* 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.
*
* 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};
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
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_ETIMEOUT, 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);
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
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);
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
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);
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
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 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(bool rotate)
: MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {}
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(false) {}
};
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}));
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
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();
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
// 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))
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
.WillOnce(SetReply(servers_[0].get(), &notimplrsp));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
.WillOnce(SetReply(servers_[1].get(), &okrsp));
CheckExample();
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
// 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))
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
.WillOnce(SetReply(servers_[1].get(), &servfailrsp))
.WillOnce(SetReply(servers_[1].get(), &notimplrsp));
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
.WillOnce(SetReply(servers_[2].get(), &notimplrsp));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].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,
Reimplement ares_gethostbyname() by wrapping ares_getaddrinfo() (#428) ares_gethostbyname() and ares_getaddrinfo() do a lot of similar things, however ares_getaddrinfo() has some desirable behaviors that should be imported into ares_gethostbyname(). For one, it sorts the address lists for the most likely to succeed based on the current system routes. Next, when AF_UNSPEC is specified, it properly handles search lists instead of first searching all of AF_INET6 then AF_INET, since ares_gethostbyname() searches in parallel. Therefore, this PR should also resolve the issues attempted in #94. A few things this PR does: 1. ares_parse_a_reply() and ares_parse_aaaa_reply() had very similar code to translate struct ares_addrinfo into a struct hostent as well as into struct ares_addrttl/ares_addr6ttl this has been split out into helper functions of ares__addrinfo2hostent() and ares__addrinfo2addrttl() to prevent this duplicative code. 2. ares_getaddrinfo() was apparently never honoring HOSTALIASES, and this was discovered once ares_gethostbyname() was turned into a wrapper, the affected test cases started failing. 3. A slight API modification to save the query hostname into struct ares_addrinfo as the last element of name. Since this is the last element, and all user-level instances of struct ares_addrinfo are allocated internally by c-ares, this is not an ABI-breaking change nor would it impact any API compatibility. This was needed since struct hostent has an h_name element. 4. Test Framework: MockServer tests via TCP would fail if more than 1 request was received at a time which is common when ares_getaddrinfo() queries for both A and AAAA records simultaneously. Infact, this was a long standing issue in which the ares_getaddrinfo() test were bypassing TCP alltogether. This has been corrected, the message is now processed in a loop. 5. Some tests had to be updated for overall correctness as they were invalid but somehow passing prior to this change. Change By: Brad House (@bradh352)
3 years ago
::testing::ValuesIn(ares::test::families_modes));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockUDPChannelTestAI,
::testing::ValuesIn(ares::test::families));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockTCPChannelTestAI,
::testing::ValuesIn(ares::test::families));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockExtraOptsTestAI,
::testing::ValuesIn(ares::test::families_modes));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockExtraOptsNDots5TestAI,
::testing::ValuesIn(ares::test::families_modes));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockNoCheckRespChannelTestAI,
::testing::ValuesIn(ares::test::families_modes));
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockEDNSChannelTestAI,
::testing::ValuesIn(ares::test::families_modes));
INSTANTIATE_TEST_SUITE_P(TransportModesAI, NoRotateMultiMockTestAI,
::testing::ValuesIn(ares::test::families_modes));
} // namespace test
} // namespace ares