Loading resolv_unit_test.cpp +128 −23 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ #define LOG_TAG "resolv" #include <gtest/gtest.h> #include <android-base/stringprintf.h> #include <arpa/inet.h> #include <gmock/gmock-matchers.h> #include <gtest/gtest.h> #include <netdb.h> #include <netdutils/InternetAddresses.h> Loading @@ -39,10 +39,14 @@ using android::base::StringPrintf; using android::net::NetworkDnsEventReported; using android::netdutils::ScopedAddrinfo; // Minimize class ResolverTest to be class TestBase because class TestBase doesn't need all member // functions of class ResolverTest and class DnsResponderClient. class TestBase : public ::testing::Test { protected: struct DnsMessage { std::string host_name; // host name ns_type type; // record type test::DNSHeader header; // dns header }; void SetUp() override { // Create cache for test resolv_create_cache_for_net(TEST_NETID); Loading @@ -52,7 +56,64 @@ class TestBase : public ::testing::Test { resolv_delete_cache_for_net(TEST_NETID); } int setResolvers() { test::DNSRecord MakeAnswerRecord(const std::string& name, unsigned rclass, unsigned rtype, const std::string& rdata, unsigned ttl = kAnswerRecordTtlSec) { test::DNSRecord record{ .name = {.name = name}, .rtype = rtype, .rclass = rclass, .ttl = ttl, }; EXPECT_TRUE(test::DNSResponder::fillAnswerRdata(rdata, record)); return record; } DnsMessage MakeDnsMessage(const std::string& qname, ns_type qtype, const std::vector<std::string>& rdata) { const unsigned qclass = ns_c_in; // Build a DNSHeader in the following format. // Question // <qname> IN <qtype> // Answer // <qname> IN <qtype> <rdata[0]> // .. // <qname> IN <qtype> <rdata[n]> // // Example: // Question // hello.example.com. IN A // Answer // hello.example.com. IN A 1.2.3.1 // .. // hello.example.com. IN A 1.2.3.9 test::DNSHeader header(kDefaultDnsHeader); // Question section test::DNSQuestion question{ .qname = {.name = qname}, .qtype = qtype, .qclass = qclass, }; header.questions.push_back(std::move(question)); // Answer section for (const auto& r : rdata) { test::DNSRecord record = MakeAnswerRecord(qname, qclass, qtype, r); header.answers.push_back(std::move(record)); } // TODO: Perhaps add support for authority RRs and additional RRs. return {qname, qtype, header}; } void StartDns(test::DNSResponder& dns, const std::vector<DnsMessage>& messages) { for (const auto& m : messages) { dns.addMappingDnsHeader(m.host_name, m.type, m.header); } ASSERT_TRUE(dns.startServer()); dns.clearQueries(); } int SetResolvers() { const std::vector<std::string> servers = {test::kDefaultListenAddr}; const std::vector<std::string> domains = {"example.com"}; const res_params params = { Loading Loading @@ -326,7 +387,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname_NoData) { test::DNSResponder dns; dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3"); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Want AAAA answer but DNS server has A answer only. addrinfo* result = nullptr; Loading @@ -348,7 +409,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname) { dns.addMapping(host_name, ns_type::ns_t_a, v4addr); dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { int ai_family; Loading Loading @@ -377,7 +438,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname) { TEST_F(ResolvGetAddrInfoTest, IllegalHostname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp. static constexpr char const* illegalHostnames[] = { Loading Loading @@ -440,7 +501,7 @@ TEST_F(ResolvGetAddrInfoTest, ServerResponseError) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and response preset rcode ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); addrinfo* result = nullptr; const addrinfo hints = {.ai_family = AF_UNSPEC}; Loading @@ -457,7 +518,7 @@ TEST_F(ResolvGetAddrInfoTest, ServerTimeout) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and don't response ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); addrinfo* result = nullptr; const addrinfo hints = {.ai_family = AF_UNSPEC}; Loading @@ -474,7 +535,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesNoIpAddress) { dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com."); dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -507,7 +568,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesNoIpAddress) { TEST_F(ResolvGetAddrInfoTest, CnamesBrokenChainByIllegalCname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -561,7 +622,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesInfiniteLoop) { dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com."); dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) { SCOPED_TRACE(StringPrintf("family: %d", family)); Loading @@ -576,6 +637,52 @@ TEST_F(ResolvGetAddrInfoTest, CnamesInfiniteLoop) { } } TEST_F(ResolvGetAddrInfoTest, MultiAnswerSections) { test::DNSResponder dns(test::DNSResponder::MappingType::DNS_HEADER); // Answer section for query type {A, AAAA} // Type A: // hello.example.com. IN A 1.2.3.1 // hello.example.com. IN A 1.2.3.2 // Type AAAA: // hello.example.com. IN AAAA 2001:db8::41 // hello.example.com. IN AAAA 2001:db8::42 StartDns(dns, {MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_a, {"1.2.3.1", "1.2.3.2"}), MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_aaaa, {"2001:db8::41", "2001:db8::42"})}); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) { SCOPED_TRACE(StringPrintf("family: %d", family)); addrinfo* res = nullptr; // If the socket type is not specified, every address will appear twice, once for // SOCK_STREAM and one for SOCK_DGRAM. Just pick one because the addresses for // the second query of different socket type are responded by the cache. const addrinfo hints = {.ai_family = family, .ai_socktype = SOCK_STREAM}; NetworkDnsEventReported event; int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &res, &event); ScopedAddrinfo result(res); ASSERT_NE(nullptr, result); ASSERT_EQ(0, rv); const std::vector<std::string> result_strs = ToStrings(result); if (family == AF_INET) { EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom)); EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray({"1.2.3.1", "1.2.3.2"})); } else if (family == AF_INET6) { EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom)); EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray({"2001:db8::41", "2001:db8::42"})); } else if (family == AF_UNSPEC) { EXPECT_EQ(0U, GetNumQueries(dns, kHelloExampleCom)); // no query because of the cache EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray( {"1.2.3.1", "1.2.3.2", "2001:db8::41", "2001:db8::42"})); } dns.clearQueries(); } } TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { constexpr char host_name[] = "jiababuei.example.com."; constexpr char v4addr[] = "1.2.3.4"; Loading @@ -585,7 +692,7 @@ TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { dns.addMapping(host_name, ns_type::ns_t_a, v4addr); dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { int ai_family; Loading Loading @@ -613,7 +720,7 @@ TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { TEST_F(GetHostByNameForNetContextTest, IllegalHostname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp. static constexpr char const* illegalHostnames[] = { Loading Loading @@ -655,7 +762,7 @@ TEST_F(GetHostByNameForNetContextTest, NoData) { test::DNSResponder dns; dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3"); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); dns.clearQueries(); // Want AAAA answer but DNS server has A answer only. Loading Loading @@ -695,7 +802,7 @@ TEST_F(GetHostByNameForNetContextTest, ServerResponseError) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and response preset rcode ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); hostent* hp = nullptr; NetworkDnsEventReported event; Loading @@ -712,7 +819,7 @@ TEST_F(GetHostByNameForNetContextTest, ServerTimeout) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and don't response ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); hostent* hp = nullptr; NetworkDnsEventReported event; Loading @@ -728,7 +835,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesNoIpAddress) { dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com."); dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -756,7 +863,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesNoIpAddress) { TEST_F(GetHostByNameForNetContextTest, CnamesBrokenChainByIllegalCname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -809,7 +916,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesInfiniteLoop) { dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com."); dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6}) { SCOPED_TRACE(StringPrintf("family: %d", family)); Loading @@ -825,8 +932,6 @@ TEST_F(GetHostByNameForNetContextTest, CnamesInfiniteLoop) { // Note that local host file function, files_getaddrinfo(), of resolv_getaddrinfo() // is not tested because it only returns a boolean (success or failure) without any error number. // TODO: Simplify the DNS server configuration, DNSResponder and resolv_set_nameservers, as // ResolverTest does. // TODO: Add test for resolv_getaddrinfo(). // - DNS response message parsing. // - Unexpected type of resource record (RR). Loading tests/dns_responder/dns_responder.cpp +93 −5 Original line number Diff line number Diff line Loading @@ -434,10 +434,11 @@ const char* DNSHeader::readHeader(const char* buffer, const char* buffer_end, un /* DNS responder */ DNSResponder::DNSResponder(std::string listen_address, std::string listen_service, ns_rcode error_rcode) ns_rcode error_rcode, MappingType mapping_type) : listen_address_(std::move(listen_address)), listen_service_(std::move(listen_service)), error_rcode_(error_rcode) {} error_rcode_(error_rcode), mapping_type_(mapping_type) {} DNSResponder::~DNSResponder() { stopServer(); Loading @@ -445,6 +446,7 @@ DNSResponder::~DNSResponder() { void DNSResponder::addMapping(const std::string& name, ns_type type, const std::string& addr) { std::lock_guard lock(mappings_mutex_); // TODO: Consider using std::map::insert_or_assign(). auto it = mappings_.find(QueryKey(name, type)); if (it != mappings_.end()) { LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type) Loading @@ -455,6 +457,23 @@ void DNSResponder::addMapping(const std::string& name, ns_type type, const std:: mappings_.try_emplace({name, type}, addr); } void DNSResponder::addMappingDnsHeader(const std::string& name, ns_type type, const DNSHeader& header) { std::lock_guard lock(mappings_mutex_); // TODO: Consider using std::map::insert_or_assign(). auto it = dnsheader_mappings_.find(QueryKey(name, type)); if (it != dnsheader_mappings_.end()) { // TODO: Perhaps replace header pointer with header content once DNSHeader::toString() has // been implemented. LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type) << "), previous header " << (void*)&it->second << " new header " << (void*)&header; it->second = header; return; } dnsheader_mappings_.try_emplace({name, type}, header); } void DNSResponder::removeMapping(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = mappings_.find(QueryKey(name, type)); Loading @@ -463,7 +482,18 @@ void DNSResponder::removeMapping(const std::string& name, ns_type type) { return; } LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present"; << "), not present in registered mappings"; } void DNSResponder::removeMappingDnsHeader(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = dnsheader_mappings_.find(QueryKey(name, type)); if (it != dnsheader_mappings_.end()) { dnsheader_mappings_.erase(it); return; } LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present in registered DnsHeader mappings"; } void DNSResponder::setResponseProbability(double response_probability) { Loading Loading @@ -669,8 +699,14 @@ bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* respo // Make the response. The query has been read into |header| which is used to build and return // the response as well. switch (mapping_type_) { case MappingType::DNS_HEADER: return makeResponseFromDnsHeader(&header, response, response_len); case MappingType::ADDRESS_OR_HOSTNAME: default: return makeResponse(&header, response, response_len); } } bool DNSResponder::addAnswerRecords(const DNSQuestion& question, std::vector<DNSRecord>* answers) const { Loading Loading @@ -700,7 +736,7 @@ bool DNSResponder::addAnswerRecords(const DNSQuestion& question, .name = {.name = it->first.name}, .rtype = it->first.type, .rclass = ns_class::ns_c_in, .ttl = 5, // seconds .ttl = kAnswerRecordTtlSec, // seconds }; if (!fillAnswerRdata(it->second, record)) return false; answers->push_back(std::move(record)); Loading Loading @@ -802,6 +838,58 @@ bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* respo return true; } bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const { std::lock_guard guard(mappings_mutex_); // Support single question record only. It should be okay because res_mkquery() sets "qdcount" // as one for the operation QUERY and handleDNSRequest() checks ns_opcode::ns_o_query before // making a response. In other words, only need to handle the query which has single question // section. See also res_mkquery() in system/netd/resolv/res_mkquery.cpp. // TODO: Perhaps add support for multi-question records. const std::vector<DNSQuestion>& questions = header->questions; if (questions.size() != 1) { LOG(INFO) << "unsupported question count " << questions.size(); return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); } if (questions[0].qclass != ns_class::ns_c_in && questions[0].qclass != ns_class::ns_c_any) { LOG(INFO) << "unsupported question class " << questions[0].qclass; return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); } const std::string name = questions[0].qname.name; const int qtype = questions[0].qtype; const auto it = dnsheader_mappings_.find(QueryKey(name, qtype)); if (it != dnsheader_mappings_.end()) { // Store both "id" and "rd" which comes from query. const unsigned id = header->id; const bool rd = header->rd; // Build a response from the registered DNSHeader mapping. *header = it->second; // Assign both "ID" and "RD" fields from query to response. See RFC 1035 section 4.1.1. header->id = id; header->rd = rd; } else { // TODO: handle correctly. See also TODO in addAnswerRecords(). LOG(INFO) << "no mapping found for " << name << " " << dnstype2str(qtype) << ", couldn't build a response from DNSHeader mapping"; // Note that do nothing as makeResponse() if no mapping is found. It just changes the QR // flag from query (0) to response (1) in the query. Then, send the modified query back as // a response. header->qr = true; } char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { return false; } *response_len = response_cur - response; return true; } void DNSResponder::setDeferredResp(bool deferred_resp) { std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_); deferred_resp_ = deferred_resp; Loading tests/dns_responder/dns_responder.h +38 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,9 @@ #include <android-base/thread_annotations.h> #include "android-base/unique_fd.h" // Default TTL of the DNS answer record. constexpr unsigned kAnswerRecordTtlSec = 5; namespace test { struct DNSName { Loading Loading @@ -73,6 +76,8 @@ struct DNSRecord { char* writeIntFields(unsigned rdlen, char* buffer, const char* buffer_end) const; }; // TODO: Perhaps rename to DNSMessage. Per RFC 1035 section 4.1, struct DNSHeader more likes a // message because it has not only header section but also question section and other RRs. struct DNSHeader { unsigned id; bool ra; Loading Loading @@ -108,6 +113,7 @@ struct DNSHeader { inline const std::string kDefaultListenAddr = "127.0.0.3"; inline const std::string kDefaultListenService = "53"; inline const ns_rcode kDefaultErrorCode = ns_rcode::ns_r_servfail; /* * Simple DNS responder, which replies to queries with the registered response Loading @@ -122,18 +128,36 @@ class DNSResponder { FORMERR_UNCOND, // DNS server reply FORMERR unconditionally DROP // DNS server not supporting EDNS will not do any response. }; // Indicate which mapping the DNS server used to build the response. // See also addMapping(), addMappingDnsHeader(), removeMapping(), removeMappingDnsHeader(), // makeResponse(), makeResponseFromDnsHeader(). // TODO: Perhaps break class DNSResponder for each mapping. // TODO: Add the mapping from (raw dns query) to (raw dns response). enum class MappingType : uint8_t { ADDRESS_OR_HOSTNAME, // Use the mapping from (name, type) to (address or hostname) DNS_HEADER, // Use the mapping from (name, type) to (DNSHeader) }; DNSResponder(std::string listen_address = kDefaultListenAddr, std::string listen_service = kDefaultListenService, ns_rcode error_rcode = ns_rcode::ns_r_servfail); ns_rcode error_rcode = kDefaultErrorCode, DNSResponder::MappingType mapping_type = MappingType::ADDRESS_OR_HOSTNAME); DNSResponder(ns_rcode error_rcode) : DNSResponder(kDefaultListenAddr, kDefaultListenService, error_rcode){}; DNSResponder(MappingType mapping_type) : DNSResponder(kDefaultListenAddr, kDefaultListenService, kDefaultErrorCode, mapping_type){}; ~DNSResponder(); // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. void addMapping(const std::string& name, ns_type type, const std::string& addr); void addMappingDnsHeader(const std::string& name, ns_type type, const DNSHeader& header); void removeMapping(const std::string& name, ns_type type); void removeMappingDnsHeader(const std::string& name, ns_type type); void setResponseProbability(double response_probability); void setEdns(Edns edns); bool running() const; Loading Loading @@ -182,9 +206,13 @@ class DNSResponder { bool generateErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // TODO: Change makeErrorResponse and makeResponse{, FromDnsHeader} to use C++ containers // instead of the unsafe pointer + length buffer. bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. bool makeResponse(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const; // Add a new file descriptor to be polled by the handler thread. bool addFd(int fd, uint32_t events); Loading @@ -204,6 +232,8 @@ class DNSResponder { const std::string listen_service_; // Error code to return for requests for an unknown name. const ns_rcode error_rcode_; // Mapping type the DNS server used to build the response. const MappingType mapping_type_; // Probability that a valid response is being sent instead of being sent // instead of returning error_rcode_. std::atomic<double> response_probability_ = 1.0; Loading @@ -217,9 +247,14 @@ class DNSResponder { // ignoring the requests. std::atomic<Edns> edns_ = Edns::ON; // Mappings from (name, type) to registered response and the // mutex protecting them. // Mappings used for building the DNS response by registered mapping items. |mapping_type_| // decides which mapping is used. See also makeResponse{, FromDnsHeader}. // - mappings_: Mapping from (name, type) to (address or hostname). // - dnsheader_mappings_: Mapping from (name, type) to (DNSHeader). std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<QueryKey, DNSHeader, QueryKeyHash> dnsheader_mappings_ GUARDED_BY(mappings_mutex_); mutable std::mutex mappings_mutex_; // Query names received so far and the corresponding mutex. mutable std::vector<std::pair<std::string, ns_type>> queries_ GUARDED_BY(queries_mutex_); Loading tests/resolv_test_utils.h +14 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,20 @@ static constexpr char kBadCharBeforePeriodHost[] = "hello.example^.com."; static constexpr char kBadCharAtTheEndHost[] = "hello.example.com^."; static constexpr char kBadCharInTheMiddleOfLabelHost[] = "hello.ex^ample.com."; static const test::DNSHeader kDefaultDnsHeader = { // Don't need to initialize the flag "id" and "rd" because DNS responder assigns them from // query to response. See RFC 1035 section 4.1.1. .id = 0, // unused. should be assigned from query to response .ra = false, // recursive query support is not available .rcode = ns_r_noerror, // no error .qr = true, // message is a response .opcode = QUERY, // a standard query .aa = false, // answer/authority portion was not authenticated by the server .tr = false, // message is not truncated .rd = false, // unused. should be assigned from query to response .ad = false, // non-authenticated data is unacceptable }; size_t GetNumQueries(const test::DNSResponder& dns, const char* name); size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name); std::string ToString(const hostent* he); Loading Loading
resolv_unit_test.cpp +128 −23 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ #define LOG_TAG "resolv" #include <gtest/gtest.h> #include <android-base/stringprintf.h> #include <arpa/inet.h> #include <gmock/gmock-matchers.h> #include <gtest/gtest.h> #include <netdb.h> #include <netdutils/InternetAddresses.h> Loading @@ -39,10 +39,14 @@ using android::base::StringPrintf; using android::net::NetworkDnsEventReported; using android::netdutils::ScopedAddrinfo; // Minimize class ResolverTest to be class TestBase because class TestBase doesn't need all member // functions of class ResolverTest and class DnsResponderClient. class TestBase : public ::testing::Test { protected: struct DnsMessage { std::string host_name; // host name ns_type type; // record type test::DNSHeader header; // dns header }; void SetUp() override { // Create cache for test resolv_create_cache_for_net(TEST_NETID); Loading @@ -52,7 +56,64 @@ class TestBase : public ::testing::Test { resolv_delete_cache_for_net(TEST_NETID); } int setResolvers() { test::DNSRecord MakeAnswerRecord(const std::string& name, unsigned rclass, unsigned rtype, const std::string& rdata, unsigned ttl = kAnswerRecordTtlSec) { test::DNSRecord record{ .name = {.name = name}, .rtype = rtype, .rclass = rclass, .ttl = ttl, }; EXPECT_TRUE(test::DNSResponder::fillAnswerRdata(rdata, record)); return record; } DnsMessage MakeDnsMessage(const std::string& qname, ns_type qtype, const std::vector<std::string>& rdata) { const unsigned qclass = ns_c_in; // Build a DNSHeader in the following format. // Question // <qname> IN <qtype> // Answer // <qname> IN <qtype> <rdata[0]> // .. // <qname> IN <qtype> <rdata[n]> // // Example: // Question // hello.example.com. IN A // Answer // hello.example.com. IN A 1.2.3.1 // .. // hello.example.com. IN A 1.2.3.9 test::DNSHeader header(kDefaultDnsHeader); // Question section test::DNSQuestion question{ .qname = {.name = qname}, .qtype = qtype, .qclass = qclass, }; header.questions.push_back(std::move(question)); // Answer section for (const auto& r : rdata) { test::DNSRecord record = MakeAnswerRecord(qname, qclass, qtype, r); header.answers.push_back(std::move(record)); } // TODO: Perhaps add support for authority RRs and additional RRs. return {qname, qtype, header}; } void StartDns(test::DNSResponder& dns, const std::vector<DnsMessage>& messages) { for (const auto& m : messages) { dns.addMappingDnsHeader(m.host_name, m.type, m.header); } ASSERT_TRUE(dns.startServer()); dns.clearQueries(); } int SetResolvers() { const std::vector<std::string> servers = {test::kDefaultListenAddr}; const std::vector<std::string> domains = {"example.com"}; const res_params params = { Loading Loading @@ -326,7 +387,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname_NoData) { test::DNSResponder dns; dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3"); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Want AAAA answer but DNS server has A answer only. addrinfo* result = nullptr; Loading @@ -348,7 +409,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname) { dns.addMapping(host_name, ns_type::ns_t_a, v4addr); dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { int ai_family; Loading Loading @@ -377,7 +438,7 @@ TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname) { TEST_F(ResolvGetAddrInfoTest, IllegalHostname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp. static constexpr char const* illegalHostnames[] = { Loading Loading @@ -440,7 +501,7 @@ TEST_F(ResolvGetAddrInfoTest, ServerResponseError) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and response preset rcode ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); addrinfo* result = nullptr; const addrinfo hints = {.ai_family = AF_UNSPEC}; Loading @@ -457,7 +518,7 @@ TEST_F(ResolvGetAddrInfoTest, ServerTimeout) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and don't response ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); addrinfo* result = nullptr; const addrinfo hints = {.ai_family = AF_UNSPEC}; Loading @@ -474,7 +535,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesNoIpAddress) { dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com."); dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -507,7 +568,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesNoIpAddress) { TEST_F(ResolvGetAddrInfoTest, CnamesBrokenChainByIllegalCname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -561,7 +622,7 @@ TEST_F(ResolvGetAddrInfoTest, CnamesInfiniteLoop) { dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com."); dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) { SCOPED_TRACE(StringPrintf("family: %d", family)); Loading @@ -576,6 +637,52 @@ TEST_F(ResolvGetAddrInfoTest, CnamesInfiniteLoop) { } } TEST_F(ResolvGetAddrInfoTest, MultiAnswerSections) { test::DNSResponder dns(test::DNSResponder::MappingType::DNS_HEADER); // Answer section for query type {A, AAAA} // Type A: // hello.example.com. IN A 1.2.3.1 // hello.example.com. IN A 1.2.3.2 // Type AAAA: // hello.example.com. IN AAAA 2001:db8::41 // hello.example.com. IN AAAA 2001:db8::42 StartDns(dns, {MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_a, {"1.2.3.1", "1.2.3.2"}), MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_aaaa, {"2001:db8::41", "2001:db8::42"})}); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) { SCOPED_TRACE(StringPrintf("family: %d", family)); addrinfo* res = nullptr; // If the socket type is not specified, every address will appear twice, once for // SOCK_STREAM and one for SOCK_DGRAM. Just pick one because the addresses for // the second query of different socket type are responded by the cache. const addrinfo hints = {.ai_family = family, .ai_socktype = SOCK_STREAM}; NetworkDnsEventReported event; int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &res, &event); ScopedAddrinfo result(res); ASSERT_NE(nullptr, result); ASSERT_EQ(0, rv); const std::vector<std::string> result_strs = ToStrings(result); if (family == AF_INET) { EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom)); EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray({"1.2.3.1", "1.2.3.2"})); } else if (family == AF_INET6) { EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom)); EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray({"2001:db8::41", "2001:db8::42"})); } else if (family == AF_UNSPEC) { EXPECT_EQ(0U, GetNumQueries(dns, kHelloExampleCom)); // no query because of the cache EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray( {"1.2.3.1", "1.2.3.2", "2001:db8::41", "2001:db8::42"})); } dns.clearQueries(); } } TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { constexpr char host_name[] = "jiababuei.example.com."; constexpr char v4addr[] = "1.2.3.4"; Loading @@ -585,7 +692,7 @@ TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { dns.addMapping(host_name, ns_type::ns_t_a, v4addr); dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { int ai_family; Loading Loading @@ -613,7 +720,7 @@ TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) { TEST_F(GetHostByNameForNetContextTest, IllegalHostname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp. static constexpr char const* illegalHostnames[] = { Loading Loading @@ -655,7 +762,7 @@ TEST_F(GetHostByNameForNetContextTest, NoData) { test::DNSResponder dns; dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3"); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); dns.clearQueries(); // Want AAAA answer but DNS server has A answer only. Loading Loading @@ -695,7 +802,7 @@ TEST_F(GetHostByNameForNetContextTest, ServerResponseError) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and response preset rcode ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); hostent* hp = nullptr; NetworkDnsEventReported event; Loading @@ -712,7 +819,7 @@ TEST_F(GetHostByNameForNetContextTest, ServerTimeout) { dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4"); dns.setResponseProbability(0.0); // always ignore requests and don't response ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); hostent* hp = nullptr; NetworkDnsEventReported event; Loading @@ -728,7 +835,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesNoIpAddress) { dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com."); dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -756,7 +863,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesNoIpAddress) { TEST_F(GetHostByNameForNetContextTest, CnamesBrokenChainByIllegalCname) { test::DNSResponder dns; ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); static const struct TestConfig { const char* name; Loading Loading @@ -809,7 +916,7 @@ TEST_F(GetHostByNameForNetContextTest, CnamesInfiniteLoop) { dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com."); dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com."); ASSERT_TRUE(dns.startServer()); ASSERT_EQ(0, setResolvers()); ASSERT_EQ(0, SetResolvers()); for (const auto& family : {AF_INET, AF_INET6}) { SCOPED_TRACE(StringPrintf("family: %d", family)); Loading @@ -825,8 +932,6 @@ TEST_F(GetHostByNameForNetContextTest, CnamesInfiniteLoop) { // Note that local host file function, files_getaddrinfo(), of resolv_getaddrinfo() // is not tested because it only returns a boolean (success or failure) without any error number. // TODO: Simplify the DNS server configuration, DNSResponder and resolv_set_nameservers, as // ResolverTest does. // TODO: Add test for resolv_getaddrinfo(). // - DNS response message parsing. // - Unexpected type of resource record (RR). Loading
tests/dns_responder/dns_responder.cpp +93 −5 Original line number Diff line number Diff line Loading @@ -434,10 +434,11 @@ const char* DNSHeader::readHeader(const char* buffer, const char* buffer_end, un /* DNS responder */ DNSResponder::DNSResponder(std::string listen_address, std::string listen_service, ns_rcode error_rcode) ns_rcode error_rcode, MappingType mapping_type) : listen_address_(std::move(listen_address)), listen_service_(std::move(listen_service)), error_rcode_(error_rcode) {} error_rcode_(error_rcode), mapping_type_(mapping_type) {} DNSResponder::~DNSResponder() { stopServer(); Loading @@ -445,6 +446,7 @@ DNSResponder::~DNSResponder() { void DNSResponder::addMapping(const std::string& name, ns_type type, const std::string& addr) { std::lock_guard lock(mappings_mutex_); // TODO: Consider using std::map::insert_or_assign(). auto it = mappings_.find(QueryKey(name, type)); if (it != mappings_.end()) { LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type) Loading @@ -455,6 +457,23 @@ void DNSResponder::addMapping(const std::string& name, ns_type type, const std:: mappings_.try_emplace({name, type}, addr); } void DNSResponder::addMappingDnsHeader(const std::string& name, ns_type type, const DNSHeader& header) { std::lock_guard lock(mappings_mutex_); // TODO: Consider using std::map::insert_or_assign(). auto it = dnsheader_mappings_.find(QueryKey(name, type)); if (it != dnsheader_mappings_.end()) { // TODO: Perhaps replace header pointer with header content once DNSHeader::toString() has // been implemented. LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type) << "), previous header " << (void*)&it->second << " new header " << (void*)&header; it->second = header; return; } dnsheader_mappings_.try_emplace({name, type}, header); } void DNSResponder::removeMapping(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = mappings_.find(QueryKey(name, type)); Loading @@ -463,7 +482,18 @@ void DNSResponder::removeMapping(const std::string& name, ns_type type) { return; } LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present"; << "), not present in registered mappings"; } void DNSResponder::removeMappingDnsHeader(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = dnsheader_mappings_.find(QueryKey(name, type)); if (it != dnsheader_mappings_.end()) { dnsheader_mappings_.erase(it); return; } LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present in registered DnsHeader mappings"; } void DNSResponder::setResponseProbability(double response_probability) { Loading Loading @@ -669,8 +699,14 @@ bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* respo // Make the response. The query has been read into |header| which is used to build and return // the response as well. switch (mapping_type_) { case MappingType::DNS_HEADER: return makeResponseFromDnsHeader(&header, response, response_len); case MappingType::ADDRESS_OR_HOSTNAME: default: return makeResponse(&header, response, response_len); } } bool DNSResponder::addAnswerRecords(const DNSQuestion& question, std::vector<DNSRecord>* answers) const { Loading Loading @@ -700,7 +736,7 @@ bool DNSResponder::addAnswerRecords(const DNSQuestion& question, .name = {.name = it->first.name}, .rtype = it->first.type, .rclass = ns_class::ns_c_in, .ttl = 5, // seconds .ttl = kAnswerRecordTtlSec, // seconds }; if (!fillAnswerRdata(it->second, record)) return false; answers->push_back(std::move(record)); Loading Loading @@ -802,6 +838,58 @@ bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* respo return true; } bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const { std::lock_guard guard(mappings_mutex_); // Support single question record only. It should be okay because res_mkquery() sets "qdcount" // as one for the operation QUERY and handleDNSRequest() checks ns_opcode::ns_o_query before // making a response. In other words, only need to handle the query which has single question // section. See also res_mkquery() in system/netd/resolv/res_mkquery.cpp. // TODO: Perhaps add support for multi-question records. const std::vector<DNSQuestion>& questions = header->questions; if (questions.size() != 1) { LOG(INFO) << "unsupported question count " << questions.size(); return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); } if (questions[0].qclass != ns_class::ns_c_in && questions[0].qclass != ns_class::ns_c_any) { LOG(INFO) << "unsupported question class " << questions[0].qclass; return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); } const std::string name = questions[0].qname.name; const int qtype = questions[0].qtype; const auto it = dnsheader_mappings_.find(QueryKey(name, qtype)); if (it != dnsheader_mappings_.end()) { // Store both "id" and "rd" which comes from query. const unsigned id = header->id; const bool rd = header->rd; // Build a response from the registered DNSHeader mapping. *header = it->second; // Assign both "ID" and "RD" fields from query to response. See RFC 1035 section 4.1.1. header->id = id; header->rd = rd; } else { // TODO: handle correctly. See also TODO in addAnswerRecords(). LOG(INFO) << "no mapping found for " << name << " " << dnstype2str(qtype) << ", couldn't build a response from DNSHeader mapping"; // Note that do nothing as makeResponse() if no mapping is found. It just changes the QR // flag from query (0) to response (1) in the query. Then, send the modified query back as // a response. header->qr = true; } char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { return false; } *response_len = response_cur - response; return true; } void DNSResponder::setDeferredResp(bool deferred_resp) { std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_); deferred_resp_ = deferred_resp; Loading
tests/dns_responder/dns_responder.h +38 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,9 @@ #include <android-base/thread_annotations.h> #include "android-base/unique_fd.h" // Default TTL of the DNS answer record. constexpr unsigned kAnswerRecordTtlSec = 5; namespace test { struct DNSName { Loading Loading @@ -73,6 +76,8 @@ struct DNSRecord { char* writeIntFields(unsigned rdlen, char* buffer, const char* buffer_end) const; }; // TODO: Perhaps rename to DNSMessage. Per RFC 1035 section 4.1, struct DNSHeader more likes a // message because it has not only header section but also question section and other RRs. struct DNSHeader { unsigned id; bool ra; Loading Loading @@ -108,6 +113,7 @@ struct DNSHeader { inline const std::string kDefaultListenAddr = "127.0.0.3"; inline const std::string kDefaultListenService = "53"; inline const ns_rcode kDefaultErrorCode = ns_rcode::ns_r_servfail; /* * Simple DNS responder, which replies to queries with the registered response Loading @@ -122,18 +128,36 @@ class DNSResponder { FORMERR_UNCOND, // DNS server reply FORMERR unconditionally DROP // DNS server not supporting EDNS will not do any response. }; // Indicate which mapping the DNS server used to build the response. // See also addMapping(), addMappingDnsHeader(), removeMapping(), removeMappingDnsHeader(), // makeResponse(), makeResponseFromDnsHeader(). // TODO: Perhaps break class DNSResponder for each mapping. // TODO: Add the mapping from (raw dns query) to (raw dns response). enum class MappingType : uint8_t { ADDRESS_OR_HOSTNAME, // Use the mapping from (name, type) to (address or hostname) DNS_HEADER, // Use the mapping from (name, type) to (DNSHeader) }; DNSResponder(std::string listen_address = kDefaultListenAddr, std::string listen_service = kDefaultListenService, ns_rcode error_rcode = ns_rcode::ns_r_servfail); ns_rcode error_rcode = kDefaultErrorCode, DNSResponder::MappingType mapping_type = MappingType::ADDRESS_OR_HOSTNAME); DNSResponder(ns_rcode error_rcode) : DNSResponder(kDefaultListenAddr, kDefaultListenService, error_rcode){}; DNSResponder(MappingType mapping_type) : DNSResponder(kDefaultListenAddr, kDefaultListenService, kDefaultErrorCode, mapping_type){}; ~DNSResponder(); // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. void addMapping(const std::string& name, ns_type type, const std::string& addr); void addMappingDnsHeader(const std::string& name, ns_type type, const DNSHeader& header); void removeMapping(const std::string& name, ns_type type); void removeMappingDnsHeader(const std::string& name, ns_type type); void setResponseProbability(double response_probability); void setEdns(Edns edns); bool running() const; Loading Loading @@ -182,9 +206,13 @@ class DNSResponder { bool generateErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // TODO: Change makeErrorResponse and makeResponse{, FromDnsHeader} to use C++ containers // instead of the unsafe pointer + length buffer. bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. bool makeResponse(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const; // Add a new file descriptor to be polled by the handler thread. bool addFd(int fd, uint32_t events); Loading @@ -204,6 +232,8 @@ class DNSResponder { const std::string listen_service_; // Error code to return for requests for an unknown name. const ns_rcode error_rcode_; // Mapping type the DNS server used to build the response. const MappingType mapping_type_; // Probability that a valid response is being sent instead of being sent // instead of returning error_rcode_. std::atomic<double> response_probability_ = 1.0; Loading @@ -217,9 +247,14 @@ class DNSResponder { // ignoring the requests. std::atomic<Edns> edns_ = Edns::ON; // Mappings from (name, type) to registered response and the // mutex protecting them. // Mappings used for building the DNS response by registered mapping items. |mapping_type_| // decides which mapping is used. See also makeResponse{, FromDnsHeader}. // - mappings_: Mapping from (name, type) to (address or hostname). // - dnsheader_mappings_: Mapping from (name, type) to (DNSHeader). std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<QueryKey, DNSHeader, QueryKeyHash> dnsheader_mappings_ GUARDED_BY(mappings_mutex_); mutable std::mutex mappings_mutex_; // Query names received so far and the corresponding mutex. mutable std::vector<std::pair<std::string, ns_type>> queries_ GUARDED_BY(queries_mutex_); Loading
tests/resolv_test_utils.h +14 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,20 @@ static constexpr char kBadCharBeforePeriodHost[] = "hello.example^.com."; static constexpr char kBadCharAtTheEndHost[] = "hello.example.com^."; static constexpr char kBadCharInTheMiddleOfLabelHost[] = "hello.ex^ample.com."; static const test::DNSHeader kDefaultDnsHeader = { // Don't need to initialize the flag "id" and "rd" because DNS responder assigns them from // query to response. See RFC 1035 section 4.1.1. .id = 0, // unused. should be assigned from query to response .ra = false, // recursive query support is not available .rcode = ns_r_noerror, // no error .qr = true, // message is a response .opcode = QUERY, // a standard query .aa = false, // answer/authority portion was not authenticated by the server .tr = false, // message is not truncated .rd = false, // unused. should be assigned from query to response .ad = false, // non-authenticated data is unacceptable }; size_t GetNumQueries(const test::DNSResponder& dns, const char* name); size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name); std::string ToString(const hostent* he); Loading