|
|
|
/* 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.h"
|
|
|
|
#include "dns-proto.h"
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
using testing::InvokeWithoutArgs;
|
|
|
|
using testing::DoAll;
|
|
|
|
|
|
|
|
namespace ares {
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, Basic) {
|
|
|
|
std::vector<byte> reply = {
|
|
|
|
0x00, 0x00, // qid
|
|
|
|
0x84, // response + query + AA + not-TC + not-RD
|
|
|
|
0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError
|
|
|
|
0x00, 0x01, // 1 question
|
|
|
|
0x00, 0x01, // 1 answer RRs
|
|
|
|
0x00, 0x00, // 0 authority RRs
|
|
|
|
0x00, 0x00, // 0 additional RRs
|
|
|
|
// Question
|
|
|
|
0x03, 'w', 'w', 'w',
|
|
|
|
0x06, 'g', 'o', 'o', 'g', 'l', 'e',
|
|
|
|
0x03, 'c', 'o', 'm',
|
|
|
|
0x00,
|
|
|
|
0x00, 0x01, // type A
|
|
|
|
0x00, 0x01, // class IN
|
|
|
|
// Answer
|
|
|
|
0x03, 'w', 'w', 'w',
|
|
|
|
0x06, 'g', 'o', 'o', 'g', 'l', 'e',
|
|
|
|
0x03, 'c', 'o', 'm',
|
|
|
|
0x00,
|
|
|
|
0x00, 0x01, // type A
|
|
|
|
0x00, 0x01, // class IN
|
|
|
|
0x00, 0x00, 0x01, 0x00, // TTL
|
|
|
|
0x00, 0x04, // rdata length
|
|
|
|
0x01, 0x02, 0x03, 0x04
|
|
|
|
};
|
|
|
|
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillByDefault(SetReplyData(&server_, reply));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// UDP only so mock server doesn't get confused by concatenated requests
|
|
|
|
TEST_P(MockUDPChannelTest, GetHostByNameParallelLookups) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
HostResult result1;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result1);
|
|
|
|
HostResult result2;
|
|
|
|
ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result2);
|
|
|
|
HostResult result3;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result3);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result1.done_);
|
|
|
|
EXPECT_TRUE(result2.done_);
|
|
|
|
EXPECT_TRUE(result3.done_);
|
|
|
|
std::stringstream ss1;
|
|
|
|
ss1 << result1.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss1.str());
|
|
|
|
std::stringstream ss2;
|
|
|
|
ss2 << result2.host_;
|
|
|
|
EXPECT_EQ("{'www.example.com' aliases=[] addrs=[1.2.3.4]}", ss2.str());
|
|
|
|
std::stringstream ss3;
|
|
|
|
ss3 << result3.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss3.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// UDP to TCP specific test
|
|
|
|
TEST_P(MockUDPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sock_cb_count = 0;
|
|
|
|
static int SocketConnectCallback(ares_socket_t fd, int type, void *data) {
|
|
|
|
int rc = *(int*)data;
|
|
|
|
if (verbose) std::cerr << "SocketConnectCallback(" << fd << ") invoked" << std::endl;
|
|
|
|
sock_cb_count++;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SockCallback) {
|
|
|
|
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}));
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(SetReply(&server_, &rsp));
|
|
|
|
|
|
|
|
// Get notified of new sockets
|
|
|
|
int rc = ARES_SUCCESS;
|
|
|
|
ares_set_socket_callback(channel_, SocketConnectCallback, &rc);
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
sock_cb_count = 0;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_EQ(1, sock_cb_count);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SockFailCallback) {
|
|
|
|
// Notification of new sockets gives an error.
|
|
|
|
int rc = -1;
|
|
|
|
ares_set_socket_callback(channel_, SocketConnectCallback, &rc);
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
sock_cb_count = 0;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_LT(1, sock_cb_count);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sock_config_cb_count = 0;
|
|
|
|
static int SocketConfigureCallback(ares_socket_t fd, int type, void *data) {
|
|
|
|
int rc = *(int*)data;
|
|
|
|
if (verbose) std::cerr << "SocketConfigureCallback(" << fd << ") invoked" << std::endl;
|
|
|
|
sock_config_cb_count++;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SockConfigureCallback) {
|
|
|
|
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}));
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(SetReply(&server_, &rsp));
|
|
|
|
|
|
|
|
// Get notified of new sockets
|
|
|
|
int rc = ARES_SUCCESS;
|
|
|
|
ares_set_socket_configure_callback(channel_, SocketConfigureCallback, &rc);
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
sock_config_cb_count = 0;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_EQ(1, sock_config_cb_count);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SockConfigureFailCallback) {
|
|
|
|
// Notification of new sockets gives an error.
|
|
|
|
int rc = -1;
|
|
|
|
ares_set_socket_configure_callback(channel_, SocketConfigureCallback, &rc);
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
sock_config_cb_count = 0;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_LT(1, sock_config_cb_count);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ECONNREFUSED, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, ReInit) {
|
|
|
|
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}));
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(SetReply(&server_, &rsp));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, ares_reinit(channel_));
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAXUDPQUERIES_TOTAL 32
|
|
|
|
#define MAXUDPQUERIES_LIMIT 8
|
|
|
|
|
|
|
|
class MockUDPMaxQueriesTest
|
|
|
|
: public MockChannelOptsTest,
|
|
|
|
public ::testing::WithParamInterface<int> {
|
|
|
|
public:
|
|
|
|
MockUDPMaxQueriesTest()
|
|
|
|
: MockChannelOptsTest(1, GetParam(), false,
|
|
|
|
FillOptions(&opts_),
|
|
|
|
ARES_OPT_UDP_MAX_QUERIES) {}
|
|
|
|
static struct ares_options* FillOptions(struct ares_options * opts) {
|
|
|
|
memset(opts, 0, sizeof(struct ares_options));
|
|
|
|
opts->udp_max_queries = MAXUDPQUERIES_LIMIT;
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
struct ares_options opts_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(MockUDPMaxQueriesTest, GetHostByNameParallelLookups) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
// Get notified of new sockets so we can validate how many are created
|
|
|
|
int rc = ARES_SUCCESS;
|
|
|
|
ares_set_socket_callback(channel_, SocketConnectCallback, &rc);
|
|
|
|
sock_cb_count = 0;
|
|
|
|
|
|
|
|
HostResult result[MAXUDPQUERIES_TOTAL];
|
|
|
|
for (size_t i=0; i<MAXUDPQUERIES_TOTAL; i++) {
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Process();
|
|
|
|
|
|
|
|
EXPECT_EQ(MAXUDPQUERIES_TOTAL / MAXUDPQUERIES_LIMIT, sock_cb_count);
|
|
|
|
|
|
|
|
for (size_t i=0; i<MAXUDPQUERIES_TOTAL; i++) {
|
|
|
|
std::stringstream ss;
|
|
|
|
EXPECT_TRUE(result[i].done_);
|
|
|
|
ss << result[i].host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Query Cache support (#625)
This PR implements a query cache at the lowest possible level, the actual dns request and response messages. Only successful and `NXDOMAIN` responses are cached. The lowest TTL in the response message determines the cache validity period for the response, and is capped at the configuration value for `qcache_max_ttl`. For `NXDOMAIN` responses, the SOA record is evaluated.
For a query to match the cache, the opcode, flags, and each question's class, type, and name are all evaluated. This is to prevent matching a cached entry for a subtly different query (such as if the RD flag is set on one request and not another).
For things like ares_getaddrinfo() or ares_search() that may spawn multiple queries, each individual message received is cached rather than the overarching response. This makes it possible for one query in the sequence to be purged from the cache while others still return cached results which means there is no chance of ever returning stale data.
We have had a lot of user requests to return TTLs on all the various parsers like `ares_parse_caa_reply()`, and likely this is because they want to implement caching mechanisms of their own, thus this PR should solve those issues as well.
Due to the internal data structures we have these days, this PR is less than 500 lines of new code.
Fixes #608
Fix By: Brad House (@bradh352)
1 year ago
|
|
|
class CacheQueriesTest
|
|
|
|
: public MockChannelOptsTest,
|
|
|
|
public ::testing::WithParamInterface<int> {
|
|
|
|
public:
|
|
|
|
CacheQueriesTest()
|
|
|
|
: MockChannelOptsTest(1, GetParam(), false,
|
|
|
|
FillOptions(&opts_),
|
|
|
|
ARES_OPT_QUERY_CACHE) {}
|
|
|
|
static struct ares_options* FillOptions(struct ares_options * opts) {
|
|
|
|
memset(opts, 0, sizeof(struct ares_options));
|
|
|
|
opts->qcache_max_ttl = 3600;
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
struct ares_options opts_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(CacheQueriesTest, GetHostByNameCache) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
// Get notified of new sockets so we can validate how many are created
|
|
|
|
int rc = ARES_SUCCESS;
|
|
|
|
ares_set_socket_callback(channel_, SocketConnectCallback, &rc);
|
|
|
|
sock_cb_count = 0;
|
|
|
|
|
|
|
|
HostResult result1;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result1);
|
|
|
|
Process();
|
|
|
|
|
|
|
|
std::stringstream ss1;
|
|
|
|
EXPECT_TRUE(result1.done_);
|
|
|
|
ss1 << result1.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss1.str());
|
|
|
|
|
|
|
|
/* Run again, should return cached result */
|
|
|
|
HostResult result2;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result2);
|
|
|
|
Process();
|
|
|
|
|
|
|
|
std::stringstream ss2;
|
|
|
|
EXPECT_TRUE(result2.done_);
|
|
|
|
ss2 << result2.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss2.str());
|
|
|
|
|
|
|
|
EXPECT_EQ(1, sock_cb_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TCPPARALLELLOOKUPS 32
|
|
|
|
TEST_P(MockTCPChannelTest, GetHostByNameParallelLookups) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
// Get notified of new sockets so we can validate how many are created
|
|
|
|
int rc = ARES_SUCCESS;
|
|
|
|
ares_set_socket_callback(channel_, SocketConnectCallback, &rc);
|
|
|
|
sock_cb_count = 0;
|
|
|
|
|
|
|
|
HostResult result[TCPPARALLELLOOKUPS];
|
|
|
|
for (size_t i=0; i<TCPPARALLELLOOKUPS; i++) {
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Process();
|
|
|
|
|
|
|
|
EXPECT_EQ(1, sock_cb_count);
|
|
|
|
|
|
|
|
for (size_t i=0; i<TCPPARALLELLOOKUPS; i++) {
|
|
|
|
std::stringstream ss;
|
|
|
|
EXPECT_TRUE(result[i].done_);
|
|
|
|
ss << result[i].host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, MalformedResponse) {
|
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
|
|
|
std::vector<byte> one = {0x00};
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillByDefault(SetReplyData(&server_, one));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_EBADRESP, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_EFORMERR, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ESERVFAIL, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENOTIMP, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_EREFUSED, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockTCPChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENODATA, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
class MockExtraOptsTest
|
|
|
|
: public MockChannelOptsTest,
|
|
|
|
public ::testing::WithParamInterface< std::pair<int, bool> > {
|
|
|
|
public:
|
|
|
|
MockExtraOptsTest()
|
|
|
|
: 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(MockExtraOptsTest, 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));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
class MockFlagsChannelOptsTest
|
|
|
|
: public MockChannelOptsTest,
|
|
|
|
public ::testing::WithParamInterface< std::pair<int, bool> > {
|
|
|
|
public:
|
|
|
|
MockFlagsChannelOptsTest(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 MockNoCheckRespChannelTest : public MockFlagsChannelOptsTest {
|
|
|
|
public:
|
|
|
|
MockNoCheckRespChannelTest() : MockFlagsChannelOptsTest(ARES_FLAG_NOCHECKRESP) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(MockNoCheckRespChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ESERVFAIL, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockNoCheckRespChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENOTIMP, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockNoCheckRespChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_EREFUSED, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
class MockEDNSChannelTest : public MockFlagsChannelOptsTest {
|
|
|
|
public:
|
|
|
|
MockEDNSChannelTest() : MockFlagsChannelOptsTest(ARES_FLAG_EDNS) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(MockEDNSChannelTest, 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));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, 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));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Relies on retries so is UDP-only
|
|
|
|
TEST_P(MockUDPChannelTest, SearchDomainsWithResentReply) {
|
|
|
|
DNSPacket nofirst;
|
|
|
|
nofirst.set_response().set_aa().set_rcode(NXDOMAIN)
|
|
|
|
.add_question(new DNSQuestion("www.first.com", T_A));
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.first.com", T_A))
|
|
|
|
.WillOnce(SetReply(&server_, &nofirst));
|
|
|
|
DNSPacket nosecond;
|
|
|
|
nosecond.set_response().set_aa().set_rcode(NXDOMAIN)
|
|
|
|
.add_question(new DNSQuestion("www.second.org", T_A));
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.second.org", T_A))
|
|
|
|
.WillOnce(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}));
|
|
|
|
// Before sending the real answer, resend an earlier reply
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.third.gov", T_A))
|
|
|
|
.WillOnce(DoAll(SetReply(&server_, &nofirst),
|
|
|
|
SetReplyQID(&server_, 123)))
|
|
|
|
.WillOnce(DoAll(SetReply(&server_, &yesthird),
|
|
|
|
SetReplyQID(&server_, -1)));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchDomainsBare) {
|
|
|
|
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 nothird;
|
|
|
|
nothird.set_response().set_aa().set_rcode(NXDOMAIN)
|
|
|
|
.add_question(new DNSQuestion("www.third.gov", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www.third.gov", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, ¬hird));
|
|
|
|
DNSPacket yesbare;
|
|
|
|
yesbare.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www", T_A))
|
|
|
|
.add_answer(new DNSARR("www", 0x0200, {2, 3, 4, 5}));
|
|
|
|
ON_CALL(server_, OnRequest("www", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &yesbare));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchNoDataThenSuccess) {
|
|
|
|
// First two search domains recognize the name but have no A records.
|
|
|
|
DNSPacket nofirst;
|
|
|
|
nofirst.set_response().set_aa()
|
|
|
|
.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()
|
|
|
|
.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));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchNoDataThenNoDataBare) {
|
|
|
|
// First two search domains recognize the name but have no A records.
|
|
|
|
DNSPacket nofirst;
|
|
|
|
nofirst.set_response().set_aa()
|
|
|
|
.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()
|
|
|
|
.add_question(new DNSQuestion("www.second.org", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www.second.org", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &nosecond));
|
|
|
|
DNSPacket nothird;
|
|
|
|
nothird.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.third.gov", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www.third.gov", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, ¬hird));
|
|
|
|
DNSPacket nobare;
|
|
|
|
nobare.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &nobare));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENODATA, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchNoDataThenFail) {
|
|
|
|
// First two search domains recognize the name but have no A records.
|
|
|
|
DNSPacket nofirst;
|
|
|
|
nofirst.set_response().set_aa()
|
|
|
|
.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()
|
|
|
|
.add_question(new DNSQuestion("www.second.org", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www.second.org", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &nosecond));
|
|
|
|
DNSPacket nothird;
|
|
|
|
nothird.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.third.gov", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www.third.gov", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, ¬hird));
|
|
|
|
DNSPacket nobare;
|
|
|
|
nobare.set_response().set_aa().set_rcode(NXDOMAIN)
|
|
|
|
.add_question(new DNSQuestion("www", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("www", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &nobare));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENODATA, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchAllocFailure) {
|
|
|
|
SearchResult result;
|
|
|
|
SetAllocFail(1);
|
|
|
|
ares_search(channel_, "fully.qualified.", C_IN, T_A, SearchCallback, &result);
|
|
|
|
/* Already done */
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENOMEM, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SearchHighNdots) {
|
|
|
|
DNSPacket nobare;
|
|
|
|
nobare.set_response().set_aa().set_rcode(NXDOMAIN)
|
|
|
|
.add_question(new DNSQuestion("a.b.c.w.w.w", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("a.b.c.w.w.w", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &nobare));
|
|
|
|
DNSPacket yesfirst;
|
|
|
|
yesfirst.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("a.b.c.w.w.w.first.com", T_A))
|
|
|
|
.add_answer(new DNSARR("a.b.c.w.w.w.first.com", 0x0200, {2, 3, 4, 5}));
|
|
|
|
ON_CALL(server_, OnRequest("a.b.c.w.w.w.first.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &yesfirst));
|
|
|
|
|
|
|
|
SearchResult result;
|
|
|
|
ares_search(channel_, "a.b.c.w.w.w", C_IN, T_A, SearchCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, result.status_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << PacketToString(result.data_);
|
|
|
|
EXPECT_EQ("RSP QRY AA NOERROR Q:{'a.b.c.w.w.w.first.com' IN A} "
|
|
|
|
"A:{'a.b.c.w.w.w.first.com' IN A TTL=512 2.3.4.5}",
|
|
|
|
ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, V4WorksV6Timeout) {
|
|
|
|
std::vector<byte> nothing;
|
|
|
|
DNSPacket reply;
|
|
|
|
reply.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.google.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
|
|
|
|
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &reply));
|
|
|
|
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_AAAA))
|
|
|
|
.WillByDefault(SetReplyData(&server_, nothing));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_UNSPEC, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(1, result.timeouts_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef CARES_SYMBOL_HIDING
|
|
|
|
// Test case for Issue #662
|
|
|
|
TEST_P(MockChannelTest, PartialQueryCancel) {
|
|
|
|
std::vector<byte> nothing;
|
|
|
|
DNSPacket reply;
|
|
|
|
reply.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.google.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
|
|
|
|
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &reply));
|
|
|
|
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_AAAA))
|
|
|
|
.WillByDefault(SetReplyData(&server_, nothing));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_UNSPEC, HostCallback, &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_);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, UnspecifiedFamilyV6) {
|
|
|
|
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));
|
|
|
|
|
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
|
|
|
DNSPacket rsp4;
|
|
|
|
rsp4.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_A));
|
|
|
|
ON_CALL(server_, OnRequest("example.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &rsp4));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
// Default to IPv6 when both are available.
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[2121:0000:0000:0000:0000:0000:0000:0303]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, UnspecifiedFamilyV4) {
|
|
|
|
DNSPacket rsp6;
|
|
|
|
rsp6.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_AAAA));
|
|
|
|
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));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, UnspecifiedFamilyNoData) {
|
|
|
|
DNSPacket rsp6;
|
|
|
|
rsp6.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_AAAA))
|
|
|
|
.add_answer(new DNSCnameRR("example.com", 100, "elsewhere.com"));
|
|
|
|
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));
|
|
|
|
ON_CALL(server_, OnRequest("example.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &rsp4));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'' aliases=[] addrs=[]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, UnspecifiedFamilyCname6A4) {
|
|
|
|
DNSPacket rsp6;
|
|
|
|
rsp6.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_AAAA))
|
|
|
|
.add_answer(new DNSCnameRR("example.com", 100, "elsewhere.com"));
|
|
|
|
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, {1, 2, 3, 4}));
|
|
|
|
ON_CALL(server_, OnRequest("example.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &rsp4));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, ExplicitIP) {
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "1.2.3.4", AF_INET, HostCallback, &result);
|
|
|
|
EXPECT_TRUE(result.done_); // Immediate return
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, result.status_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'1.2.3.4' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, ExplicitIPAllocFail) {
|
|
|
|
HostResult result;
|
|
|
|
SetAllocSizeFail(strlen("1.2.3.4") + 1);
|
|
|
|
ares_gethostbyname(channel_, "1.2.3.4", AF_INET, HostCallback, &result);
|
|
|
|
EXPECT_TRUE(result.done_); // Immediate return
|
|
|
|
EXPECT_EQ(ARES_ENOMEM, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SortListV4) {
|
|
|
|
DNSPacket rsp;
|
|
|
|
rsp.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_A))
|
|
|
|
.add_answer(new DNSARR("example.com", 100, {22, 23, 24, 25}))
|
|
|
|
.add_answer(new DNSARR("example.com", 100, {12, 13, 14, 15}))
|
|
|
|
.add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5}));
|
|
|
|
ON_CALL(server_, OnRequest("example.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &rsp));
|
|
|
|
|
|
|
|
{
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "12.13.0.0/255.255.0.0 1234::5678"));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[12.13.14.15, 22.23.24.25, 2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "2.3.0.0/16 130.140.150.160/26"));
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[2.3.4.5, 22.23.24.25, 12.13.14.15]}", ss.str());
|
|
|
|
}
|
|
|
|
struct ares_options options;
|
|
|
|
memset(&options, 0, sizeof(options));
|
|
|
|
int optmask = 0;
|
|
|
|
EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel_, &options, &optmask));
|
|
|
|
EXPECT_TRUE((optmask & ARES_OPT_SORTLIST) == ARES_OPT_SORTLIST);
|
|
|
|
ares_destroy_options(&options);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, SortListV6) {
|
|
|
|
DNSPacket rsp;
|
|
|
|
rsp.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("example.com", T_AAAA))
|
|
|
|
.add_answer(new DNSAaaaRR("example.com", 100,
|
|
|
|
{0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02}))
|
|
|
|
.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_, &rsp));
|
|
|
|
|
|
|
|
{
|
|
|
|
ares_set_sortlist(channel_, "1111::/16 2.3.0.0/255.255.0.0");
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_INET6, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[1111:0000:0000:0000:0000:0000:0000:0202, "
|
|
|
|
"2121:0000:0000:0000:0000:0000:0000:0303]}", ss.str());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ares_set_sortlist(channel_, "2121::/8");
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "example.com.", AF_INET6, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'example.com' aliases=[] addrs=[2121:0000:0000:0000:0000:0000:0000:0303, "
|
|
|
|
"1111:0000:0000:0000:0000:0000:0000:0202]}", ss.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Relies on retries so is UDP-only
|
|
|
|
TEST_P(MockUDPChannelTest, SearchDomainsAllocFail) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
// Fail a variety of different memory allocations, and confirm
|
|
|
|
// that the operation either fails with ENOMEM or succeeds
|
|
|
|
// with the expected result.
|
|
|
|
const int kCount = 34;
|
|
|
|
HostResult results[kCount];
|
|
|
|
for (int ii = 1; ii <= kCount; ii++) {
|
|
|
|
HostResult* result = &(results[ii - 1]);
|
|
|
|
ClearFails();
|
|
|
|
SetAllocFail(ii);
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result->done_);
|
|
|
|
if (result->status_ == ARES_SUCCESS) {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result->host_;
|
|
|
|
EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()) << " failed alloc #" << ii;
|
|
|
|
if (verbose) std::cerr << "Succeeded despite failure of alloc #" << ii << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Explicitly destroy the channel now, so that the HostResult objects
|
|
|
|
// are still valid (in case any pending work refers to them).
|
|
|
|
ares_destroy(channel_);
|
|
|
|
channel_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Relies on retries so is UDP-only
|
|
|
|
TEST_P(MockUDPChannelTest, Resend) {
|
|
|
|
std::vector<byte> nothing;
|
|
|
|
DNSPacket reply;
|
|
|
|
reply.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.google.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
|
|
|
|
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(SetReplyData(&server_, nothing))
|
|
|
|
.WillOnce(SetReplyData(&server_, nothing))
|
|
|
|
.WillOnce(SetReply(&server_, &reply));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(2, result.timeouts_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, CancelImmediate) {
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
ares_cancel(channel_);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ECANCELLED, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, CancelImmediateGetHostByAddr) {
|
|
|
|
HostResult result;
|
|
|
|
struct in_addr addr;
|
|
|
|
addr.s_addr = htonl(0x08080808);
|
|
|
|
|
|
|
|
ares_gethostbyaddr(channel_, &addr, sizeof(addr), AF_INET, HostCallback, &result);
|
|
|
|
ares_cancel(channel_);
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ECANCELLED, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Relies on retries so is UDP-only
|
|
|
|
TEST_P(MockUDPChannelTest, CancelLater) {
|
|
|
|
std::vector<byte> nothing;
|
|
|
|
|
|
|
|
// On second request, cancel the channel.
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(SetReplyData(&server_, nothing))
|
|
|
|
.WillOnce(CancelChannel(&server_, channel_));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ECANCELLED, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, DisconnectFirstAttempt) {
|
|
|
|
DNSPacket reply;
|
|
|
|
reply.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.google.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
|
|
|
|
|
|
|
|
// On second request, cancel the channel.
|
|
|
|
EXPECT_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillOnce(Disconnect(&server_))
|
|
|
|
.WillOnce(SetReply(&server_, &reply));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, GetHostByNameDestroyAbsolute) {
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
|
|
|
|
|
|
|
|
ares_destroy(channel_);
|
|
|
|
channel_ = nullptr;
|
|
|
|
|
|
|
|
EXPECT_TRUE(result.done_); // Synchronous
|
|
|
|
EXPECT_EQ(ARES_EDESTRUCTION, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, GetHostByNameDestroyRelative) {
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
|
|
|
|
ares_destroy(channel_);
|
|
|
|
channel_ = nullptr;
|
|
|
|
|
|
|
|
EXPECT_TRUE(result.done_); // Synchronous
|
|
|
|
EXPECT_EQ(ARES_EDESTRUCTION, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, GetHostByNameCNAMENoData) {
|
|
|
|
DNSPacket response;
|
|
|
|
response.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("cname.first.com", T_A))
|
|
|
|
.add_answer(new DNSCnameRR("cname.first.com", 100, "a.first.com"));
|
|
|
|
ON_CALL(server_, OnRequest("cname.first.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &response));
|
|
|
|
|
|
|
|
HostResult result;
|
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
|
|
|
ares_gethostbyname(channel_, "cname.first.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_ENODATA, result.status_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, GetHostByAddrDestroy) {
|
|
|
|
unsigned char gdns_addr4[4] = {0x08, 0x08, 0x08, 0x08};
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyaddr(channel_, gdns_addr4, sizeof(gdns_addr4), AF_INET, HostCallback, &result);
|
|
|
|
|
|
|
|
ares_destroy(channel_);
|
|
|
|
channel_ = nullptr;
|
|
|
|
|
|
|
|
EXPECT_TRUE(result.done_); // Synchronous
|
|
|
|
EXPECT_EQ(ARES_EDESTRUCTION, result.status_);
|
|
|
|
EXPECT_EQ(0, result.timeouts_);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
TEST_P(MockChannelTest, HostAlias) {
|
|
|
|
DNSPacket reply;
|
|
|
|
reply.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.google.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
|
|
|
|
ON_CALL(server_, OnRequest("www.google.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &reply));
|
|
|
|
|
|
|
|
TempFile aliases("\n\n# www commentedout\nwww www.google.com\n");
|
|
|
|
EnvValue with_env("HOSTALIASES", aliases.filename());
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, HostAliasMissing) {
|
|
|
|
DNSPacket yesfirst;
|
|
|
|
yesfirst.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.first.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.first.com", 0x0200, {2, 3, 4, 5}));
|
|
|
|
ON_CALL(server_, OnRequest("www.first.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &yesfirst));
|
|
|
|
|
|
|
|
TempFile aliases("\n\n# www commentedout\nww www.google.com\n");
|
|
|
|
EnvValue with_env("HOSTALIASES", aliases.filename());
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.first.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, HostAliasMissingFile) {
|
|
|
|
DNSPacket yesfirst;
|
|
|
|
yesfirst.set_response().set_aa()
|
|
|
|
.add_question(new DNSQuestion("www.first.com", T_A))
|
|
|
|
.add_answer(new DNSARR("www.first.com", 0x0200, {2, 3, 4, 5}));
|
|
|
|
ON_CALL(server_, OnRequest("www.first.com", T_A))
|
|
|
|
.WillByDefault(SetReply(&server_, &yesfirst));
|
|
|
|
|
|
|
|
EnvValue with_env("HOSTALIASES", "bogus.mcfile");
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.first.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(MockChannelTest, HostAliasUnreadable) {
|
|
|
|
TempFile aliases("www www.google.com\n");
|
|
|
|
EXPECT_EQ(chmod(aliases.filename(), 0), 0);
|
|
|
|
|
|
|
|
/* Perform OS sanity checks. We are observing on Debian after the chmod(fn, 0)
|
|
|
|
* that we are still able to fopen() the file which is unexpected. Skip the
|
|
|
|
* test if we observe this behavior */
|
|
|
|
struct stat st;
|
|
|
|
EXPECT_EQ(stat(aliases.filename(), &st), 0);
|
|
|
|
EXPECT_EQ(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0);
|
|
|
|
FILE *fp = fopen(aliases.filename(), "r");
|
|
|
|
if (fp != NULL) {
|
|
|
|
if (verbose) std::cerr << "Skipping Test due to OS incompatibility (open file caching)" << std::endl;
|
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnvValue with_env("HOSTALIASES", aliases.filename());
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(ARES_EFILE, result.status_);
|
|
|
|
chmod(aliases.filename(), 0777);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class MockMultiServerChannelTest
|
|
|
|
: public MockChannelOptsTest,
|
|
|
|
public ::testing::WithParamInterface< std::pair<int, bool> > {
|
|
|
|
public:
|
|
|
|
MockMultiServerChannelTest(bool rotate)
|
|
|
|
: MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE) {}
|
|
|
|
void CheckExample() {
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class NoRotateMultiMockTest : public MockMultiServerChannelTest {
|
|
|
|
public:
|
|
|
|
NoRotateMultiMockTest() : MockMultiServerChannelTest(false) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(NoRotateMultiMockTest, 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(), ¬implrsp));
|
|
|
|
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(), ¬implrsp));
|
|
|
|
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(), ¬implrsp));
|
|
|
|
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(), ¬implrsp));
|
|
|
|
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
|
|
|
|
.WillOnce(SetReply(servers_[0].get(), &okrsp));
|
|
|
|
CheckExample();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(NoRotateMultiMockTest, ServerNoResponseFailover) {
|
|
|
|
std::vector<byte> nothing;
|
|
|
|
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}));
|
|
|
|
|
|
|
|
ON_CALL(*servers_[0], OnRequest("www.example.com", T_A))
|
|
|
|
.WillByDefault(SetReplyData(servers_[0].get(), nothing));
|
|
|
|
ON_CALL(*servers_[1], OnRequest("www.example.com", T_A))
|
|
|
|
.WillByDefault(SetReplyData(servers_[1].get(), nothing));
|
|
|
|
EXPECT_CALL(*servers_[2], OnRequest("www.example.com", T_A))
|
|
|
|
.WillOnce(SetReply(servers_[2].get(), &okrsp));
|
|
|
|
|
|
|
|
HostResult result;
|
|
|
|
ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result);
|
|
|
|
Process();
|
|
|
|
EXPECT_TRUE(result.done_);
|
|
|
|
EXPECT_EQ(2, result.timeouts_);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << result.host_;
|
|
|
|
EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockChannelTest, ::testing::ValuesIn(ares::test::families_modes));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPChannelTest, ::testing::ValuesIn(ares::test::families));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPMaxQueriesTest, ::testing::ValuesIn(ares::test::families));
|
|
|
|
|
Query Cache support (#625)
This PR implements a query cache at the lowest possible level, the actual dns request and response messages. Only successful and `NXDOMAIN` responses are cached. The lowest TTL in the response message determines the cache validity period for the response, and is capped at the configuration value for `qcache_max_ttl`. For `NXDOMAIN` responses, the SOA record is evaluated.
For a query to match the cache, the opcode, flags, and each question's class, type, and name are all evaluated. This is to prevent matching a cached entry for a subtly different query (such as if the RD flag is set on one request and not another).
For things like ares_getaddrinfo() or ares_search() that may spawn multiple queries, each individual message received is cached rather than the overarching response. This makes it possible for one query in the sequence to be purged from the cache while others still return cached results which means there is no chance of ever returning stale data.
We have had a lot of user requests to return TTLs on all the various parsers like `ares_parse_caa_reply()`, and likely this is because they want to implement caching mechanisms of their own, thus this PR should solve those issues as well.
Due to the internal data structures we have these days, this PR is less than 500 lines of new code.
Fixes #608
Fix By: Brad House (@bradh352)
1 year ago
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, CacheQueriesTest, ::testing::ValuesIn(ares::test::families));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockTCPChannelTest, ::testing::ValuesIn(ares::test::families));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockExtraOptsTest, ::testing::ValuesIn(ares::test::families_modes));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockNoCheckRespChannelTest, ::testing::ValuesIn(ares::test::families_modes));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockEDNSChannelTest, ::testing::ValuesIn(ares::test::families_modes));
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(TransportModes, NoRotateMultiMockTest, ::testing::ValuesIn(ares::test::families_modes));
|
|
|
|
|
|
|
|
} // namespace test
|
|
|
|
} // namespace ares
|