You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
15 KiB
368 lines
15 KiB
/*
|
|
* Copyright (C) 2016 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.
|
|
*
|
|
*/
|
|
|
|
#ifndef DNS_RESPONDER_H
|
|
#define DNS_RESPONDER_H
|
|
|
|
#include <arpa/nameser.h>
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <android-base/thread_annotations.h>
|
|
#include "android-base/unique_fd.h"
|
|
|
|
// Default TTL of the DNS answer record.
|
|
constexpr unsigned kAnswerRecordTtlSec = 5;
|
|
|
|
// The maximum UDP response size in bytes the DNS responder allows to send. It is used in non-EDNS
|
|
// case. See RFC 1035 section 4.2.1.
|
|
constexpr unsigned kMaximumUdpSize = 512;
|
|
|
|
namespace test {
|
|
|
|
struct DNSName {
|
|
std::string name;
|
|
const char* read(const char* buffer, const char* buffer_end);
|
|
char* write(char* buffer, const char* buffer_end) const;
|
|
|
|
private:
|
|
const char* parseField(const char* buffer, const char* buffer_end, bool* last);
|
|
};
|
|
|
|
struct DNSQuestion {
|
|
DNSName qname;
|
|
unsigned qtype;
|
|
unsigned qclass;
|
|
const char* read(const char* buffer, const char* buffer_end);
|
|
char* write(char* buffer, const char* buffer_end) const;
|
|
std::string toString() const;
|
|
};
|
|
|
|
struct DNSRecord {
|
|
DNSName name;
|
|
unsigned rtype;
|
|
unsigned rclass;
|
|
unsigned ttl;
|
|
std::vector<char> rdata;
|
|
const char* read(const char* buffer, const char* buffer_end);
|
|
char* write(char* buffer, const char* buffer_end) const;
|
|
std::string toString() const;
|
|
|
|
private:
|
|
struct IntFields {
|
|
uint16_t rtype;
|
|
uint16_t rclass;
|
|
uint32_t ttl;
|
|
uint16_t rdlen;
|
|
} __attribute__((__packed__));
|
|
|
|
const char* readIntFields(const char* buffer, const char* buffer_end, unsigned* rdlen);
|
|
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;
|
|
uint8_t rcode;
|
|
bool qr;
|
|
uint8_t opcode;
|
|
bool aa;
|
|
bool tr;
|
|
bool rd;
|
|
bool ad;
|
|
std::vector<DNSQuestion> questions;
|
|
std::vector<DNSRecord> answers;
|
|
std::vector<DNSRecord> authorities;
|
|
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:
|
|
struct Header {
|
|
uint16_t id;
|
|
uint8_t flags0;
|
|
uint8_t flags1;
|
|
uint16_t qdcount;
|
|
uint16_t ancount;
|
|
uint16_t nscount;
|
|
uint16_t arcount;
|
|
} __attribute__((__packed__));
|
|
|
|
const char* readHeader(const char* buffer, const char* buffer_end, unsigned* qdcount,
|
|
unsigned* ancount, unsigned* nscount, unsigned* arcount);
|
|
};
|
|
|
|
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
|
|
* for that type. Class is assumed to be IN. If no response is registered, the
|
|
* default error response code is returned.
|
|
*/
|
|
class DNSResponder {
|
|
public:
|
|
enum class Edns {
|
|
ON,
|
|
FORMERR_ON_EDNS, // DNS server not supporting EDNS will reply FORMERR.
|
|
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{, DnsHeader, BinaryPacket}, removeMapping{, DnsHeader, BinaryPacket},
|
|
// makeResponse{, FromDnsHeader, FromBinaryPacket}.
|
|
// TODO: Perhaps break class DNSResponder for each mapping.
|
|
enum class MappingType {
|
|
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)
|
|
};
|
|
|
|
struct QueryInfo {
|
|
std::string name;
|
|
ns_type type;
|
|
int protocol; // Either IPPROTO_TCP or IPPROTO_UDP
|
|
};
|
|
|
|
DNSResponder(std::string listen_address = kDefaultListenAddr,
|
|
std::string listen_service = kDefaultListenService,
|
|
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(unsigned netId, std::string listen_address) : DNSResponder(listen_address) {
|
|
mNetId = netId;
|
|
};
|
|
|
|
~DNSResponder();
|
|
|
|
// 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 setResponseProbability(double response_probability, int protocol);
|
|
void setResponseDelayMs(unsigned);
|
|
void setErrorRcode(ns_rcode error_rcode) { error_rcode_ = error_rcode; }
|
|
void setEdns(Edns edns);
|
|
void setTtl(unsigned ttl);
|
|
bool running() const;
|
|
bool startServer();
|
|
bool stopServer();
|
|
const std::string& listen_address() const { return listen_address_; }
|
|
const std::string& listen_service() const { return listen_service_; }
|
|
std::vector<QueryInfo> queries() const;
|
|
std::string dumpQueries() const;
|
|
void clearQueries();
|
|
std::condition_variable& getCv() { return cv; }
|
|
std::mutex& getCvMutex() { return cv_mutex_; }
|
|
void setDeferredResp(bool deferred_resp);
|
|
static bool fillRdata(const std::string& rdatastr, DNSRecord& record);
|
|
|
|
// These functions are helpers for binding the listening sockets to a specific network, which
|
|
// is necessary only for multinetwork tests. Since binding sockets to a network requires
|
|
// the dependency of libnetd_client, and DNSResponder is also widely used in other tests like
|
|
// resolv_unit_test which doesn't need that dependency, so expose the socket fds to let the
|
|
// callers perform binding operations by themselves. Callers MUST not close the fds.
|
|
void setNetwork(unsigned netId) { mNetId = netId; }
|
|
std::optional<unsigned> getNetwork() const { return mNetId; }
|
|
int getUdpSocket() const { return udp_socket_.get(); }
|
|
int getTcpSocket() const { return tcp_socket_.get(); }
|
|
|
|
// 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 {
|
|
std::string name;
|
|
unsigned type;
|
|
|
|
QueryKey(std::string n, unsigned t) : name(move(n)), type(t) {}
|
|
bool operator==(const QueryKey& o) const { return name == o.name && type == o.type; }
|
|
bool operator<(const QueryKey& o) const {
|
|
if (name < o.name) return true;
|
|
if (name > o.name) return false;
|
|
return type < o.type;
|
|
}
|
|
};
|
|
|
|
struct QueryKeyHash {
|
|
size_t operator()(const QueryKey& key) const {
|
|
return std::hash<std::string>()(key.name) + static_cast<size_t>(key.type);
|
|
}
|
|
};
|
|
|
|
// 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();
|
|
|
|
// Check if any OPT Pseudo RR in the additional section.
|
|
bool hasOptPseudoRR(DNSHeader* header) const;
|
|
|
|
// Parses and generates a response message for incoming DNS requests.
|
|
// Returns false to ignore the request, which might be due to either parsing error
|
|
// or unresponsiveness.
|
|
bool handleDNSRequest(const char* buffer, ssize_t buffer_len, int protocol, char* response,
|
|
size_t* response_len) const;
|
|
|
|
bool addAnswerRecords(const DNSQuestion& question, std::vector<DNSRecord>* answers) const;
|
|
|
|
bool generateErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
|
|
size_t* response_len) const;
|
|
|
|
// TODO: Change writePacket, makeErrorResponse, makeTruncatedResponse and
|
|
// makeResponse{, FromAddressOrHostname, 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;
|
|
// Build an error response with a given rcode.
|
|
bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
|
|
size_t* response_len) const;
|
|
// Build a truncated response.
|
|
bool makeTruncatedResponse(DNSHeader* header, char* response, size_t* response_len) const;
|
|
// Build a response.
|
|
bool makeResponse(DNSHeader* header, int protocol, char* response, size_t* response_len) const;
|
|
// Helper for building a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}.
|
|
bool makeResponseFromAddressOrHostname(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);
|
|
|
|
// Read the query sent from the client and send the answer back to the client. It
|
|
// makes sure the I/O communicated with the client is correct.
|
|
void handleQuery(int protocol);
|
|
|
|
// Trigger the handler thread to terminate.
|
|
bool sendToEventFd();
|
|
|
|
// Used in the handler thread for the termination signal.
|
|
void handleEventFd();
|
|
|
|
// TODO: Move createListeningSocket to resolv_test_utils.h
|
|
android::base::unique_fd createListeningSocket(int socket_type);
|
|
|
|
double getResponseProbability(int protocol) const;
|
|
|
|
// Address and service to listen on TCP and UDP.
|
|
const std::string listen_address_;
|
|
const std::string listen_service_;
|
|
|
|
// TODO: Consider refactoring atomic members of this class to a single big mutex.
|
|
// Error code to return for requests for an unknown name.
|
|
ns_rcode error_rcode_;
|
|
// Mapping type the DNS server used to build the response.
|
|
const MappingType mapping_type_;
|
|
// Probability that a valid response on TCP is being sent instead of
|
|
// returning error_rcode_ or no response.
|
|
std::atomic<double> response_probability_tcp_ = 1.0;
|
|
// Probability that a valid response on UDP is being sent instead of
|
|
// returning error_rcode_ or no response.
|
|
std::atomic<double> response_probability_udp_ = 1.0;
|
|
|
|
std::atomic<unsigned> answer_record_ttl_sec_ = kAnswerRecordTtlSec;
|
|
|
|
std::atomic<unsigned> response_delayed_ms_ = 0;
|
|
|
|
// Maximum number of fds for epoll.
|
|
const int EPOLL_MAX_EVENTS = 2;
|
|
|
|
// Control how the DNS server behaves when it receives the requests containing OPT RR.
|
|
// If it's set Edns::ON, the server can recognize and reply the response; if it's set
|
|
// Edns::FORMERR_ON_EDNS, the server behaves like an old DNS server that doesn't support EDNS0,
|
|
// and replying FORMERR; if it's Edns::DROP, the server doesn't support EDNS0 either, and
|
|
// ignoring the requests.
|
|
std::atomic<Edns> edns_ = Edns::ON;
|
|
|
|
// 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).
|
|
// - 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.
|
|
mutable std::vector<QueryInfo> queries_ GUARDED_BY(queries_mutex_);
|
|
mutable std::mutex queries_mutex_;
|
|
// Socket on which the server is listening.
|
|
android::base::unique_fd udp_socket_;
|
|
android::base::unique_fd tcp_socket_;
|
|
// File descriptor for epoll.
|
|
android::base::unique_fd epoll_fd_;
|
|
// Eventfd used to signal for the handler thread termination.
|
|
android::base::unique_fd event_fd_;
|
|
// Thread for handling incoming threads.
|
|
std::thread handler_thread_ GUARDED_BY(update_mutex_);
|
|
std::mutex update_mutex_;
|
|
std::condition_variable cv;
|
|
std::mutex cv_mutex_;
|
|
|
|
std::condition_variable cv_for_deferred_resp_;
|
|
std::mutex cv_mutex_for_deferred_resp_;
|
|
bool deferred_resp_ GUARDED_BY(cv_mutex_for_deferred_resp_) = false;
|
|
|
|
// The network to which the listening sockets will be bound.
|
|
std::optional<unsigned> mNetId;
|
|
};
|
|
|
|
} // namespace test
|
|
|
|
#endif // DNS_RESPONDER_H
|