Loading TEST_MAPPING +1 −0 Original line number Diff line number Diff line { "presubmit": [ { "name": "resolv_integration_test" }, { "name": "resolv_gold_test" }, { "name": "resolv_unit_test" }, { "name": "resolv_stress_test" } ] Loading tests/Android.bp +44 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,50 @@ cc_test_library { ], } cc_library_static { name: "golddata_proto", defaults: ["netd_defaults"], proto: { export_proto_headers: true, type: "full", }, srcs: [ "golddata.proto", ], } cc_test { name: "resolv_gold_test", test_suites: ["device-tests"], require_root: true, defaults: ["netd_defaults"], data: ["testdata/*.pbtxt"], srcs: [ "resolv_gold_test.cpp", ], header_libs: [ "libnetd_resolv_internal_headers", ], shared_libs: [ "libcrypto", "libprotobuf-cpp-full", "libssl", ], static_libs: [ "dnsresolver_aidl_interface-cpp", "golddata_proto", "libbase", "libgmock", "liblog", "libnetd_resolv", "libnetd_test_dnsresponder", "libnetd_test_resolv_utils", "libnetdutils", "server_configurable_flags", "stats_proto", ], } cc_test { name: "resolv_stress_test", test_suites: ["device-tests"], Loading tests/dns_responder/dns_responder.cpp +88 −49 Original line number Diff line number Diff line Loading @@ -36,10 +36,12 @@ #include <android-base/logging.h> #include <android-base/strings.h> #include <netdutils/InternetAddresses.h> #include <netdutils/Slice.h> #include <netdutils/SocketOption.h> using android::netdutils::enableSockopt; using android::netdutils::ScopedAddrinfo; using android::netdutils::Slice; namespace test { Loading Loading @@ -402,6 +404,15 @@ char* DNSHeader::write(char* buffer, const char* buffer_end) const { return buffer_cur; } // TODO: convert all callers to this interface, then delete the old one. bool DNSHeader::write(std::vector<uint8_t>* out) const { char buffer[4096]; char* end = this->write(buffer, buffer + sizeof buffer); if (end == nullptr) return false; out->insert(out->end(), buffer, end); return true; } std::string DNSHeader::toString() const { // TODO return std::string(); Loading Loading @@ -446,55 +457,46 @@ 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) << "), previous address " << it->second << " new address " << addr; it->second = addr; return; } mappings_.try_emplace({name, type}, addr); mappings_[{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_[{name, type}] = header; } dnsheader_mappings_.try_emplace({name, type}, header); void DNSResponder::addMappingBinaryPacket(const std::vector<uint8_t>& query, const std::vector<uint8_t>& response) { std::lock_guard lock(mappings_mutex_); packet_mappings_[query] = response; } void DNSResponder::removeMapping(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = mappings_.find(QueryKey(name, type)); if (it != mappings_.end()) { mappings_.erase(it); return; } if (!mappings_.erase({name, type})) { LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), 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; } if (!dnsheader_mappings_.erase({name, type})) { LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present in registered DnsHeader mappings"; } } void DNSResponder::removeMappingBinaryPacket(const std::vector<uint8_t>& query) { std::lock_guard lock(mappings_mutex_); if (!packet_mappings_.erase(query)) { LOG(ERROR) << "Cannot remove mapping, not present in registered BinaryPacket mappings"; LOG(INFO) << "Hex dump:"; LOG(INFO) << android::netdutils::toHex( Slice(const_cast<uint8_t*>(query.data()), query.size()), 32); } } void DNSResponder::setResponseProbability(double response_probability) { response_probability_ = response_probability; Loading Loading @@ -706,6 +708,8 @@ bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* respo switch (mapping_type_) { case MappingType::DNS_HEADER: return makeResponseFromDnsHeader(&header, response, response_len); case MappingType::BINARY_PACKET: return makeResponseFromBinaryPacket(&header, response, response_len); case MappingType::ADDRESS_OR_HOSTNAME: default: return makeResponse(&header, response, response_len); Loading Loading @@ -809,6 +813,16 @@ bool DNSResponder::fillRdata(const std::string& rdatastr, DNSRecord& record) { return true; } bool DNSResponder::writePacket(const DNSHeader* header, char* response, size_t* response_len) const { char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { return false; } *response_len = response_cur - response; return true; } bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const { header->answers.clear(); Loading @@ -816,10 +830,7 @@ bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* re header->additionals.clear(); header->rcode = rcode; 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; return writePacket(header, response, response_len); } bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* response_len) const { Loading @@ -833,14 +844,8 @@ bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* respo return makeErrorResponse(header, ns_rcode::ns_r_servfail, response, response_len); } } 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; return writePacket(header, response, response_len); } bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, Loading Loading @@ -886,14 +891,46 @@ bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, // a response. header->qr = true; } return writePacket(header, response, response_len); } char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { bool DNSResponder::makeResponseFromBinaryPacket(DNSHeader* header, char* response, size_t* response_len) const { std::lock_guard guard(mappings_mutex_); // Build a search key of mapping from the query. // TODO: Perhaps pass the query packet buffer directly from the caller. std::vector<uint8_t> queryKey; if (!header->write(&queryKey)) return false; // Clear ID field (byte 0-1) because it is not required by the mapping key. queryKey[0] = 0; queryKey[1] = 0; const auto it = packet_mappings_.find(queryKey); if (it != packet_mappings_.end()) { if (it->second.size() > *response_len) { LOG(ERROR) << "buffer overflow on line " << __LINE__; return false; } *response_len = response_cur - response; } else { std::copy(it->second.begin(), it->second.end(), response); // Leave the "RD" flag assignment for testing. The "RD" flag of the response keep // using the one from the raw packet mapping but the received query. // Assign "ID" field from query to response. See RFC 1035 section 4.1.1. reinterpret_cast<uint16_t*>(response)[0] = htons(header->id); // bytes 0-1: id *response_len = it->second.size(); return true; } } else { // TODO: handle correctly. See also TODO in addAnswerRecords(). // TODO: Perhaps dump packet content to indicate which query failed. LOG(INFO) << "no mapping found, couldn't build a response from BinaryPacket 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; return writePacket(header, response, response_len); } } void DNSResponder::setDeferredResp(bool deferred_resp) { std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_); Loading Loading @@ -946,6 +983,8 @@ void DNSResponder::handleQuery() { PLOG(INFO) << "sendto() failed for " << host_str; } // Test that the response is actually a correct DNS message. // TODO: Make DNS message test to support name compression. Or it throws a warning for // a valid DNS message with name compression while the raw packet mapping is used. const char* response_end = response + len; DNSHeader header; const char* cur = header.read(response, response_end); Loading tests/dns_responder/dns_responder.h +37 −7 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ struct DNSHeader { std::vector<DNSRecord> additionals; const char* read(const char* buffer, const char* buffer_end); char* write(char* buffer, const char* buffer_end) const; bool write(std::vector<uint8_t>* out) const; std::string toString() const; private: Loading Loading @@ -129,13 +130,13 @@ class DNSResponder { 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(). // See also addMapping{, DnsHeader, BinaryPacket}, removeMapping{, DnsHeader, BinaryPacket}, // makeResponse{, FromDnsHeader, FromBinaryPacket}. // 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) BINARY_PACKET, // Use the mapping from (query packet) to (response packet) }; DNSResponder(std::string listen_address = kDefaultListenAddr, Loading @@ -152,11 +153,14 @@ class DNSResponder { ~DNSResponder(); // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}. 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 addMappingBinaryPacket(const std::vector<uint8_t>& query, const std::vector<uint8_t>& response); void removeMapping(const std::string& name, ns_type type); void removeMappingDnsHeader(const std::string& name, ns_type type); void removeMappingBinaryPacket(const std::vector<uint8_t>& query); void setResponseProbability(double response_probability); void setEdns(Edns edns); Loading @@ -173,6 +177,9 @@ class DNSResponder { void setDeferredResp(bool deferred_resp); static bool fillRdata(const std::string& rdatastr, DNSRecord& record); // TODO: Make DNSResponder record unknown queries in a vector for improving the debugging. // Unit test could dump the unexpected query for further debug if any unexpected failure. private: // Key used for accessing mappings. struct QueryKey { Loading @@ -194,6 +201,21 @@ class DNSResponder { } }; // Used for generating combined hash value of a vector. // std::hash<T> doesn't provide a specialization for std::vector<T>. struct QueryKeyVectorHash { std::size_t operator()(const std::vector<uint8_t>& v) const { std::size_t combined = 0; for (const uint8_t i : v) { // Hash combination comes from boost::hash_combine // See also system/extras/simpleperf/utils.h combined ^= std::hash<uint8_t>{}(i) + 0x9e3779b9 + (combined << 6) + (combined >> 2); } return combined; } }; void requestHandler(); // Parses and generates a response message for incoming DNS requests. Loading @@ -206,13 +228,18 @@ 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. // TODO: Change writePacket, makeErrorResponse and // makeResponse{, FromDnsHeader, FromBinaryPacket} to use C++ containers instead of the unsafe // pointer + length buffer. bool writePacket(const DNSHeader* header, char* response, size_t* response_len) const; bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}. bool makeResponse(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromBinaryPacket(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 Loading @@ -251,9 +278,12 @@ class DNSResponder { // 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). // - packet_mappings_: Mapping from (query packet) to (response packet). std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<QueryKey, DNSHeader, QueryKeyHash> dnsheader_mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<std::vector<uint8_t>, std::vector<uint8_t>, QueryKeyVectorHash> packet_mappings_ GUARDED_BY(mappings_mutex_); mutable std::mutex mappings_mutex_; // Query names received so far and the corresponding mutex. Loading tests/golddata.proto 0 → 100644 +195 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto3"; package android.net; // Used to indicate which call is invoked to send DNS lookups. enum CallType { CALL_GETADDRINFO = 0; CALL_GETHOSTBYNAME = 1; CALL_GETHOSTBYADDR = 2; CALL_GETNAMEINFO = 3; CALL_RES_NSEND = 4; } // Values from bionic/libc/include/sys/socket.h enum AddressFamily { option allow_alias = true; // for AF_ROUTE = AF_NETLINK GT_AF_UNSPEC = 0; GT_AF_UNIX = 1; GT_AF_LOCAL = 1; GT_AF_INET = 2; GT_AF_AX25 = 3; GT_AF_IPX = 4; GT_AF_APPLETALK = 5; GT_AF_NETROM = 6; GT_AF_BRIDGE = 7; GT_AF_ATMPVC= 8; GT_AF_X25 = 9; GT_AF_INET6 = 10; GT_AF_ROSE = 11; GT_AF_DECnet = 12; GT_AF_NETBEUI = 13; GT_AF_SECURITY = 14; GT_AF_KEY = 15; GT_AF_NETLINK = 16; GT_AF_ROUTE = 16; // AF_NETLINK GT_AF_PACKET = 17; GT_AF_ASH = 18; GT_AF_ECONET = 19; GT_AF_ATMSVC = 20; GT_AF_RDS = 21; GT_AF_SNA = 22; GT_AF_IRDA = 23; GT_AF_PPPOX = 24; GT_AF_WANPIPE = 25; GT_AF_LLC = 26; GT_AF_CAN = 29; GT_AF_TIPC = 30; GT_AF_BLUETOOTH = 31; GT_AF_IUCV = 32; GT_AF_RXRPC = 33; GT_AF_ISDN = 34; GT_AF_PHONET = 35; GT_AF_IEEE802154 = 36; GT_AF_CAIF = 37; GT_AF_ALG = 38; GT_AF_NFC = 39; GT_AF_VSOCK = 40; GT_AF_KCM = 41; GT_AF_QIPCRTR = 42; GT_AF_MAX = 43; } // Values from bionic/libc/include/sys/socket.h enum SocketType { GT_SOCK_ANY = 0; // See man getaddrinfo for more detail GT_SOCK_STREAM = 1; GT_SOCK_DGRAM = 2; GT_SOCK_RAW = 3; GT_SOCK_RDM = 4; GT_SOCK_SEQPACKET = 5; GT_SOCK_DCCP = 6; GT_SOCK_PACKET = 10; } // Values from bionic/libc/kernel/uapi/linux/in.h enum ProtocolType { GT_IPPROTO_IP = 0; GT_IPPROTO_ICMP = 1; GT_IPPROTO_IGMP = 2; GT_IPPROTO_IPIP = 4; GT_IPPROTO_TCP = 6; GT_IPPROTO_EGP = 8; GT_IPPROTO_PUP = 12; GT_IPPROTO_UDP = 17; GT_IPPROTO_IDP = 22; GT_IPPROTO_TP = 29; GT_IPPROTO_DCCP = 33; GT_IPPROTO_IPV6 = 41; GT_IPPROTO_RSVP = 46; GT_IPPROTO_GRE = 47; GT_IPPROTO_ESP = 50; GT_IPPROTO_AH = 51; GT_IPPROTO_MTP = 92; GT_IPPROTO_BEETPH = 94; GT_IPPROTO_ENCAP = 98; GT_IPPROTO_PIM = 103; GT_IPPROTO_COMP = 108; GT_IPPROTO_SCTP = 132; GT_IPPROTO_UDPLITE = 136; GT_IPPROTO_MPLS = 137; GT_IPPROTO_RAW = 255; GT_IPPROTO_MAX = 256; } // The return value of the DNS resolver for each DNS lookups. // Values from bionic/libc/include/netdb.h // Values from system/netd/resolv/include/netd_resolv/resolv.h enum ReturnCodeType { GT_EAI_NO_ERROR = 0; GT_EAI_ADDRFAMILY = 1; GT_EAI_AGAIN = 2; GT_EAI_BADFLAGS = 3; GT_EAI_FAIL = 4; GT_EAI_FAMILY = 5; GT_EAI_MEMORY = 6; GT_EAI_NODATA = 7; GT_EAI_NONAME = 8; GT_EAI_SERVICE = 9; GT_EAI_SOCKTYPE = 10; GT_EAI_SYSTEM = 11; GT_EAI_BADHINTS = 12; GT_EAI_PROTOCOL = 13; GT_EAI_OVERFLOW = 14; GT_RESOLV_TIMEOUT = 255; GT_EAI_MAX = 256; } // Describes the test configuration and expected result for gold test. // The unit test files a DNS query by the predefined |config|. Expect that the resolver sends the // query packet as the predefined packet in |packet_mapping.query|. When the DNS responser receives // the query packet, it returns the corresponding response packet from |packet_mapping.response|. // Finally, the unit test checks the return values if they are the same as |result|. Currently, // support getaddrinfo only. // TODO: Support gethostbyname, gethostbyaddr, and getnameinfo. message GoldTest { // The configuration of various DNS query calls. message Config { // The arguments used to send a DNS query by call type CALL_GETADDRINFO. message AddrInfo { string host = 1; AddressFamily family = 2; SocketType socktype = 3; ProtocolType protocol = 4; int32 ai_flags = 5; } // The call is used to send DNS lookups. CallType call = 1; // The arguments are used by the call. oneof Arg { // The arguments of call type CALL_GETADDRINFO. AddrInfo addrinfo = 2; } }; // The result is expected in DNS lookups. message Result { ReturnCodeType return_code = 1; repeated string addresses = 2; }; // Describes how the DNS responser handles and responses the DNS lookup packets. message PacketMapping { bytes query = 1; bytes response = 2; } // Configs used to send a DNS query via a DNS query API. Config config = 1; // Expected return values from DNS query API. Result result = 2; // Used to build the packet mapping (query, response) in DNS responser. See also // addMappingBinaryPacket() in // packages/modules/DnsResolver/tests/dns_responder/dns_responder.cpp. repeated PacketMapping packet_mapping = 3; } No newline at end of file Loading
TEST_MAPPING +1 −0 Original line number Diff line number Diff line { "presubmit": [ { "name": "resolv_integration_test" }, { "name": "resolv_gold_test" }, { "name": "resolv_unit_test" }, { "name": "resolv_stress_test" } ] Loading
tests/Android.bp +44 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,50 @@ cc_test_library { ], } cc_library_static { name: "golddata_proto", defaults: ["netd_defaults"], proto: { export_proto_headers: true, type: "full", }, srcs: [ "golddata.proto", ], } cc_test { name: "resolv_gold_test", test_suites: ["device-tests"], require_root: true, defaults: ["netd_defaults"], data: ["testdata/*.pbtxt"], srcs: [ "resolv_gold_test.cpp", ], header_libs: [ "libnetd_resolv_internal_headers", ], shared_libs: [ "libcrypto", "libprotobuf-cpp-full", "libssl", ], static_libs: [ "dnsresolver_aidl_interface-cpp", "golddata_proto", "libbase", "libgmock", "liblog", "libnetd_resolv", "libnetd_test_dnsresponder", "libnetd_test_resolv_utils", "libnetdutils", "server_configurable_flags", "stats_proto", ], } cc_test { name: "resolv_stress_test", test_suites: ["device-tests"], Loading
tests/dns_responder/dns_responder.cpp +88 −49 Original line number Diff line number Diff line Loading @@ -36,10 +36,12 @@ #include <android-base/logging.h> #include <android-base/strings.h> #include <netdutils/InternetAddresses.h> #include <netdutils/Slice.h> #include <netdutils/SocketOption.h> using android::netdutils::enableSockopt; using android::netdutils::ScopedAddrinfo; using android::netdutils::Slice; namespace test { Loading Loading @@ -402,6 +404,15 @@ char* DNSHeader::write(char* buffer, const char* buffer_end) const { return buffer_cur; } // TODO: convert all callers to this interface, then delete the old one. bool DNSHeader::write(std::vector<uint8_t>* out) const { char buffer[4096]; char* end = this->write(buffer, buffer + sizeof buffer); if (end == nullptr) return false; out->insert(out->end(), buffer, end); return true; } std::string DNSHeader::toString() const { // TODO return std::string(); Loading Loading @@ -446,55 +457,46 @@ 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) << "), previous address " << it->second << " new address " << addr; it->second = addr; return; } mappings_.try_emplace({name, type}, addr); mappings_[{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_[{name, type}] = header; } dnsheader_mappings_.try_emplace({name, type}, header); void DNSResponder::addMappingBinaryPacket(const std::vector<uint8_t>& query, const std::vector<uint8_t>& response) { std::lock_guard lock(mappings_mutex_); packet_mappings_[query] = response; } void DNSResponder::removeMapping(const std::string& name, ns_type type) { std::lock_guard lock(mappings_mutex_); auto it = mappings_.find(QueryKey(name, type)); if (it != mappings_.end()) { mappings_.erase(it); return; } if (!mappings_.erase({name, type})) { LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), 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; } if (!dnsheader_mappings_.erase({name, type})) { LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) << "), not present in registered DnsHeader mappings"; } } void DNSResponder::removeMappingBinaryPacket(const std::vector<uint8_t>& query) { std::lock_guard lock(mappings_mutex_); if (!packet_mappings_.erase(query)) { LOG(ERROR) << "Cannot remove mapping, not present in registered BinaryPacket mappings"; LOG(INFO) << "Hex dump:"; LOG(INFO) << android::netdutils::toHex( Slice(const_cast<uint8_t*>(query.data()), query.size()), 32); } } void DNSResponder::setResponseProbability(double response_probability) { response_probability_ = response_probability; Loading Loading @@ -706,6 +708,8 @@ bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* respo switch (mapping_type_) { case MappingType::DNS_HEADER: return makeResponseFromDnsHeader(&header, response, response_len); case MappingType::BINARY_PACKET: return makeResponseFromBinaryPacket(&header, response, response_len); case MappingType::ADDRESS_OR_HOSTNAME: default: return makeResponse(&header, response, response_len); Loading Loading @@ -809,6 +813,16 @@ bool DNSResponder::fillRdata(const std::string& rdatastr, DNSRecord& record) { return true; } bool DNSResponder::writePacket(const DNSHeader* header, char* response, size_t* response_len) const { char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { return false; } *response_len = response_cur - response; return true; } bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const { header->answers.clear(); Loading @@ -816,10 +830,7 @@ bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* re header->additionals.clear(); header->rcode = rcode; 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; return writePacket(header, response, response_len); } bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* response_len) const { Loading @@ -833,14 +844,8 @@ bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* respo return makeErrorResponse(header, ns_rcode::ns_r_servfail, response, response_len); } } 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; return writePacket(header, response, response_len); } bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, Loading Loading @@ -886,14 +891,46 @@ bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, // a response. header->qr = true; } return writePacket(header, response, response_len); } char* response_cur = header->write(response, response + *response_len); if (response_cur == nullptr) { bool DNSResponder::makeResponseFromBinaryPacket(DNSHeader* header, char* response, size_t* response_len) const { std::lock_guard guard(mappings_mutex_); // Build a search key of mapping from the query. // TODO: Perhaps pass the query packet buffer directly from the caller. std::vector<uint8_t> queryKey; if (!header->write(&queryKey)) return false; // Clear ID field (byte 0-1) because it is not required by the mapping key. queryKey[0] = 0; queryKey[1] = 0; const auto it = packet_mappings_.find(queryKey); if (it != packet_mappings_.end()) { if (it->second.size() > *response_len) { LOG(ERROR) << "buffer overflow on line " << __LINE__; return false; } *response_len = response_cur - response; } else { std::copy(it->second.begin(), it->second.end(), response); // Leave the "RD" flag assignment for testing. The "RD" flag of the response keep // using the one from the raw packet mapping but the received query. // Assign "ID" field from query to response. See RFC 1035 section 4.1.1. reinterpret_cast<uint16_t*>(response)[0] = htons(header->id); // bytes 0-1: id *response_len = it->second.size(); return true; } } else { // TODO: handle correctly. See also TODO in addAnswerRecords(). // TODO: Perhaps dump packet content to indicate which query failed. LOG(INFO) << "no mapping found, couldn't build a response from BinaryPacket 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; return writePacket(header, response, response_len); } } void DNSResponder::setDeferredResp(bool deferred_resp) { std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_); Loading Loading @@ -946,6 +983,8 @@ void DNSResponder::handleQuery() { PLOG(INFO) << "sendto() failed for " << host_str; } // Test that the response is actually a correct DNS message. // TODO: Make DNS message test to support name compression. Or it throws a warning for // a valid DNS message with name compression while the raw packet mapping is used. const char* response_end = response + len; DNSHeader header; const char* cur = header.read(response, response_end); Loading
tests/dns_responder/dns_responder.h +37 −7 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ struct DNSHeader { std::vector<DNSRecord> additionals; const char* read(const char* buffer, const char* buffer_end); char* write(char* buffer, const char* buffer_end) const; bool write(std::vector<uint8_t>* out) const; std::string toString() const; private: Loading Loading @@ -129,13 +130,13 @@ class DNSResponder { 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(). // See also addMapping{, DnsHeader, BinaryPacket}, removeMapping{, DnsHeader, BinaryPacket}, // makeResponse{, FromDnsHeader, FromBinaryPacket}. // 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) BINARY_PACKET, // Use the mapping from (query packet) to (response packet) }; DNSResponder(std::string listen_address = kDefaultListenAddr, Loading @@ -152,11 +153,14 @@ class DNSResponder { ~DNSResponder(); // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}. 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 addMappingBinaryPacket(const std::vector<uint8_t>& query, const std::vector<uint8_t>& response); void removeMapping(const std::string& name, ns_type type); void removeMappingDnsHeader(const std::string& name, ns_type type); void removeMappingBinaryPacket(const std::vector<uint8_t>& query); void setResponseProbability(double response_probability); void setEdns(Edns edns); Loading @@ -173,6 +177,9 @@ class DNSResponder { void setDeferredResp(bool deferred_resp); static bool fillRdata(const std::string& rdatastr, DNSRecord& record); // TODO: Make DNSResponder record unknown queries in a vector for improving the debugging. // Unit test could dump the unexpected query for further debug if any unexpected failure. private: // Key used for accessing mappings. struct QueryKey { Loading @@ -194,6 +201,21 @@ class DNSResponder { } }; // Used for generating combined hash value of a vector. // std::hash<T> doesn't provide a specialization for std::vector<T>. struct QueryKeyVectorHash { std::size_t operator()(const std::vector<uint8_t>& v) const { std::size_t combined = 0; for (const uint8_t i : v) { // Hash combination comes from boost::hash_combine // See also system/extras/simpleperf/utils.h combined ^= std::hash<uint8_t>{}(i) + 0x9e3779b9 + (combined << 6) + (combined >> 2); } return combined; } }; void requestHandler(); // Parses and generates a response message for incoming DNS requests. Loading @@ -206,13 +228,18 @@ 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. // TODO: Change writePacket, makeErrorResponse and // makeResponse{, FromDnsHeader, FromBinaryPacket} to use C++ containers instead of the unsafe // pointer + length buffer. bool writePacket(const DNSHeader* header, char* response, size_t* response_len) const; bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, size_t* response_len) const; // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}. // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}. bool makeResponse(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const; bool makeResponseFromBinaryPacket(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 Loading @@ -251,9 +278,12 @@ class DNSResponder { // 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). // - packet_mappings_: Mapping from (query packet) to (response packet). std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<QueryKey, DNSHeader, QueryKeyHash> dnsheader_mappings_ GUARDED_BY(mappings_mutex_); std::unordered_map<std::vector<uint8_t>, std::vector<uint8_t>, QueryKeyVectorHash> packet_mappings_ GUARDED_BY(mappings_mutex_); mutable std::mutex mappings_mutex_; // Query names received so far and the corresponding mutex. Loading
tests/golddata.proto 0 → 100644 +195 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto3"; package android.net; // Used to indicate which call is invoked to send DNS lookups. enum CallType { CALL_GETADDRINFO = 0; CALL_GETHOSTBYNAME = 1; CALL_GETHOSTBYADDR = 2; CALL_GETNAMEINFO = 3; CALL_RES_NSEND = 4; } // Values from bionic/libc/include/sys/socket.h enum AddressFamily { option allow_alias = true; // for AF_ROUTE = AF_NETLINK GT_AF_UNSPEC = 0; GT_AF_UNIX = 1; GT_AF_LOCAL = 1; GT_AF_INET = 2; GT_AF_AX25 = 3; GT_AF_IPX = 4; GT_AF_APPLETALK = 5; GT_AF_NETROM = 6; GT_AF_BRIDGE = 7; GT_AF_ATMPVC= 8; GT_AF_X25 = 9; GT_AF_INET6 = 10; GT_AF_ROSE = 11; GT_AF_DECnet = 12; GT_AF_NETBEUI = 13; GT_AF_SECURITY = 14; GT_AF_KEY = 15; GT_AF_NETLINK = 16; GT_AF_ROUTE = 16; // AF_NETLINK GT_AF_PACKET = 17; GT_AF_ASH = 18; GT_AF_ECONET = 19; GT_AF_ATMSVC = 20; GT_AF_RDS = 21; GT_AF_SNA = 22; GT_AF_IRDA = 23; GT_AF_PPPOX = 24; GT_AF_WANPIPE = 25; GT_AF_LLC = 26; GT_AF_CAN = 29; GT_AF_TIPC = 30; GT_AF_BLUETOOTH = 31; GT_AF_IUCV = 32; GT_AF_RXRPC = 33; GT_AF_ISDN = 34; GT_AF_PHONET = 35; GT_AF_IEEE802154 = 36; GT_AF_CAIF = 37; GT_AF_ALG = 38; GT_AF_NFC = 39; GT_AF_VSOCK = 40; GT_AF_KCM = 41; GT_AF_QIPCRTR = 42; GT_AF_MAX = 43; } // Values from bionic/libc/include/sys/socket.h enum SocketType { GT_SOCK_ANY = 0; // See man getaddrinfo for more detail GT_SOCK_STREAM = 1; GT_SOCK_DGRAM = 2; GT_SOCK_RAW = 3; GT_SOCK_RDM = 4; GT_SOCK_SEQPACKET = 5; GT_SOCK_DCCP = 6; GT_SOCK_PACKET = 10; } // Values from bionic/libc/kernel/uapi/linux/in.h enum ProtocolType { GT_IPPROTO_IP = 0; GT_IPPROTO_ICMP = 1; GT_IPPROTO_IGMP = 2; GT_IPPROTO_IPIP = 4; GT_IPPROTO_TCP = 6; GT_IPPROTO_EGP = 8; GT_IPPROTO_PUP = 12; GT_IPPROTO_UDP = 17; GT_IPPROTO_IDP = 22; GT_IPPROTO_TP = 29; GT_IPPROTO_DCCP = 33; GT_IPPROTO_IPV6 = 41; GT_IPPROTO_RSVP = 46; GT_IPPROTO_GRE = 47; GT_IPPROTO_ESP = 50; GT_IPPROTO_AH = 51; GT_IPPROTO_MTP = 92; GT_IPPROTO_BEETPH = 94; GT_IPPROTO_ENCAP = 98; GT_IPPROTO_PIM = 103; GT_IPPROTO_COMP = 108; GT_IPPROTO_SCTP = 132; GT_IPPROTO_UDPLITE = 136; GT_IPPROTO_MPLS = 137; GT_IPPROTO_RAW = 255; GT_IPPROTO_MAX = 256; } // The return value of the DNS resolver for each DNS lookups. // Values from bionic/libc/include/netdb.h // Values from system/netd/resolv/include/netd_resolv/resolv.h enum ReturnCodeType { GT_EAI_NO_ERROR = 0; GT_EAI_ADDRFAMILY = 1; GT_EAI_AGAIN = 2; GT_EAI_BADFLAGS = 3; GT_EAI_FAIL = 4; GT_EAI_FAMILY = 5; GT_EAI_MEMORY = 6; GT_EAI_NODATA = 7; GT_EAI_NONAME = 8; GT_EAI_SERVICE = 9; GT_EAI_SOCKTYPE = 10; GT_EAI_SYSTEM = 11; GT_EAI_BADHINTS = 12; GT_EAI_PROTOCOL = 13; GT_EAI_OVERFLOW = 14; GT_RESOLV_TIMEOUT = 255; GT_EAI_MAX = 256; } // Describes the test configuration and expected result for gold test. // The unit test files a DNS query by the predefined |config|. Expect that the resolver sends the // query packet as the predefined packet in |packet_mapping.query|. When the DNS responser receives // the query packet, it returns the corresponding response packet from |packet_mapping.response|. // Finally, the unit test checks the return values if they are the same as |result|. Currently, // support getaddrinfo only. // TODO: Support gethostbyname, gethostbyaddr, and getnameinfo. message GoldTest { // The configuration of various DNS query calls. message Config { // The arguments used to send a DNS query by call type CALL_GETADDRINFO. message AddrInfo { string host = 1; AddressFamily family = 2; SocketType socktype = 3; ProtocolType protocol = 4; int32 ai_flags = 5; } // The call is used to send DNS lookups. CallType call = 1; // The arguments are used by the call. oneof Arg { // The arguments of call type CALL_GETADDRINFO. AddrInfo addrinfo = 2; } }; // The result is expected in DNS lookups. message Result { ReturnCodeType return_code = 1; repeated string addresses = 2; }; // Describes how the DNS responser handles and responses the DNS lookup packets. message PacketMapping { bytes query = 1; bytes response = 2; } // Configs used to send a DNS query via a DNS query API. Config config = 1; // Expected return values from DNS query API. Result result = 2; // Used to build the packet mapping (query, response) in DNS responser. See also // addMappingBinaryPacket() in // packages/modules/DnsResolver/tests/dns_responder/dns_responder.cpp. repeated PacketMapping packet_mapping = 3; } No newline at end of file