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.

228 lines
9.0 KiB

/* MIT License
*
* Copyright (c) The c-ares project and its contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include "ares-test.h"
#include "dns-proto.h"
#include <sstream>
#include <vector>
namespace ares {
namespace test {
TEST_F(LibraryTest, ParseAaaaReplyOK) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSAaaaRR("example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}))
.add_answer(new DNSARR("example.com", 0x01020304, {2,3,4,5}));
std::vector<byte> data = pkt.data();
struct hostent *host = nullptr;
struct ares_addr6ttl info[5];
int count = 5;
EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(1, count);
EXPECT_EQ(100, info[0].ttl);
EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]);
EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]);
ASSERT_NE(nullptr, host);
std::stringstream ss;
ss << HostEnt(host);
EXPECT_EQ("{'example.com' aliases=[] addrs=[0101:0101:0202:0202:0303:0303:0404:0404]}", ss.str());
ares_free_hostent(host);
// Repeat without providing places to put the results
count = 0;
EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), (int)data.size(),
nullptr, info, &count));
}
TEST_F(LibraryTest, ParseAaaaReplyCname) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSCnameRR("example.com", 50, "c.example.com"))
.add_answer(new DNSAaaaRR("c.example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}));
std::vector<byte> data = pkt.data();
struct hostent *host = nullptr;
struct ares_addr6ttl info[5];
int count = 5;
EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(1, count);
// CNAME TTL overrides AAAA TTL.
EXPECT_EQ(50, info[0].ttl);
EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]);
EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]);
ASSERT_NE(nullptr, host);
std::stringstream ss;
ss << HostEnt(host);
EXPECT_EQ("{'c.example.com' aliases=[example.com] addrs=[0101:0101:0202:0202:0303:0303:0404:0404]}", ss.str());
ares_free_hostent(host);
// Repeat without providing a hostent
count = 5;
EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), (int)data.size(),
nullptr, info, &count));
EXPECT_EQ(1, count);
EXPECT_EQ(50, info[0].ttl);
EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]);
EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]);
}
TEST_F(LibraryTest, ParseAaaaReplyNoData) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA));
std::vector<byte> data = pkt.data();
struct hostent *host = nullptr;
struct ares_addr6ttl info[2];
int count = 2;
EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(0, count);
EXPECT_EQ(nullptr, host);
// Again but with a CNAME.
pkt.add_answer(new DNSCnameRR("example.com", 200, "c.example.com"));
EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(0, count);
EXPECT_EQ(nullptr, host);
}
TEST_F(LibraryTest, ParseAaaaReplyErrors) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSAaaaRR("example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}));
std::vector<byte> data;
struct hostent *host = nullptr;
struct ares_addr6ttl info[2];
int count = 2;
// No question.
pkt.questions_.clear();
data = pkt.data();
EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(nullptr, host);
pkt.add_question(new DNSQuestion("example.com", T_AAAA));
// Question != answer, this is ok as of Issue #683
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("Axample.com", T_AAAA));
data = pkt.data();
EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
ASSERT_NE(nullptr, host);
std::stringstream ss;
ss << HostEnt(host);
DNS 0x20 implementation (#800) This PR enables DNS 0x20 as per https://datatracker.ietf.org/doc/html/draft-vixie-dnsext-dns0x20-00 . DNS 0x20 adds additional entropy to the request by randomly altering the case of the DNS question to help prevent cache poisoning attacks. Google DNS has implemented this support as of 2023, even though this is a proposed and expired standard from 2008: https://groups.google.com/g/public-dns-discuss/c/KxIDPOydA5M There have been documented cases of name server and caching server non-conformance, though it is expected to become more rare, especially since Google has started using this. This can be enabled via the `ARES_FLAG_DNS0x20` flag, which is currently disabled by default. The test cases do however enable this flag to validate this feature. Implementors using this flag will notice that responses will retain the mixed case, but since DNS names are case-insensitive, any proper implementation should not be impacted. There is currently no fallback mechanism implemented as it isn't immediately clear how this may affect a stub resolver like c-ares where we aren't querying the authoritative name server, but instead an intermediate recursive resolver where some domains may return invalid results while others return valid results, all while querying the same nameserver. Likely using DNS cookies as suggested by #620 is a better mechanism to fight cache poisoning attacks for stub resolvers. TCP queries do not use this feature even if the `ARES_FLAG_DNS0x20` flag is specified since they are not subject to cache poisoning attacks. Fixes Issue: #795 Fix By: Brad House (@bradh352)
5 months ago
EXPECT_EQ("{'axample.com' aliases=[] addrs=[0101:0101:0202:0202:0303:0303:0404:0404]}", ss.str());
ares_free_hostent(host);
host = nullptr;
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("example.com", T_AAAA));
// Two questions.
pkt.add_question(new DNSQuestion("example.com", T_AAAA));
data = pkt.data();
EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(nullptr, host);
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("example.com", T_AAAA));
// Wrong sort of answer.
pkt.answers_.clear();
pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com"));
data = pkt.data();
EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(nullptr, host);
pkt.answers_.clear();
pkt.add_answer(new DNSAaaaRR("example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}));
// No answer.
pkt.answers_.clear();
data = pkt.data();
EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count));
EXPECT_EQ(nullptr, host);
pkt.add_answer(new DNSAaaaRR("example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}));
// Truncated packets.
data = pkt.data();
for (size_t len = 1; len < data.size(); len++) {
EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), (int)len,
&host, info, &count));
EXPECT_EQ(nullptr, host);
EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), (int)len,
nullptr, info, &count));
}
Coverage code annotations for identification of desirable paths that need testing (#775) Add code annotations for ignoring specific code paths for coverage calculations. The primary purpose of this is to make it easy to see the code paths that we could (and probably should) write test cases for, as these would have the most impact on delivery of a stable product. The annotations used are: `LCOV_EXCL_LINE: <designation>`, `LCOV_EXCL_START: <designation>`, `LCOV_EXCL_STOP` Unfortunately `LCOV_EXCL_BR_LINE` does not appear to be supported by coveralls as it would have been a more elegant solution over START/STOP. We specifically include the `<designation>` not just for future reference but because it makes it easy to identify in case we want to address these conditions in a different way in the future. The main areas designated for exclusion are: 1. `OutOfMemory` - these are hard to test cases, and on modern systems, are likely to never occur due to optimistic memory allocations, which can then later cause the kernel to terminate your application due to memory not actually being available. c-ares does have *some* testing framework for this, if we wish to expand in the future, we can easily use sed to get rid of of these annotations. 2. `DefensiveCoding` - these are impossible to reach paths at the point in time the code was written. They are there for defensive coding in case code is refactored in the future to prevent unexpected behavior. 3. `UntestablePath` - these are code paths that aren't possible to test, such as failure of a system call. 4. `FallbackCode` - This is an entire set of code that is untestable because its not able to simulate a failure of the primary path. This PR also does add some actual coverage in the test cases where it is easy to do. Fix By: Brad House (@bradh352)
6 months ago
// Negative length
EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), -1,
&host, info, &count));
}
TEST_F(LibraryTest, ParseAaaaReplyAllocFail) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_AAAA))
.add_answer(new DNSCnameRR("example.com", 300, "c.example.com"))
.add_answer(new DNSAaaaRR("c.example.com", 100,
{0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}));
std::vector<byte> data = pkt.data();
struct hostent *host = nullptr;
struct ares_addr6ttl info[2];
int count = 2;
for (int ii = 1; ii <= 8; ii++) {
ClearFails();
SetAllocFail(ii);
EXPECT_EQ(ARES_ENOMEM, ares_parse_aaaa_reply(data.data(), (int)data.size(),
&host, info, &count)) << ii;
EXPECT_EQ(nullptr, host);
}
}
} // namespace test
} // namespace ares