Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2147a9c7 authored by Nucca Chen's avatar Nucca Chen Committed by android-build-merger
Browse files

Merge changes from topic "resolv_gold_test"

am: 90d4e68d

Change-Id: Id70ed3094f48ff59a4f741c8431b1173acf77c9c
parents b5c22411 90d4e68d
Loading
Loading
Loading
Loading
+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" }
    ]
+44 −0
Original line number Diff line number Diff line
@@ -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"],
+88 −49
Original line number Diff line number Diff line
@@ -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 {

@@ -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();
@@ -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;
@@ -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);
@@ -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();
@@ -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 {
@@ -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,
@@ -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_);
@@ -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);
+37 −7
Original line number Diff line number Diff line
@@ -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:
@@ -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,
@@ -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);
@@ -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 {
@@ -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.
@@ -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);
@@ -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.

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