Snap for 6198741 from 731dc404a45c92211c926f3e4dff1fdd06603342 to sdk-release
Change-Id: Ia7156f1e2cfe11e090f39276ca4b12e260f7de78
diff --git a/Android.bp b/Android.bp
index d05881f..a79b14b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,11 +3,8 @@
export_include_dirs: ["include"],
}
-//For the test to use
-//TODO: 1. Need to Refactor ResolverStats related code(ex: ResolverController::getDnsInfo).
-// Then removing this library header file.
-// 2. Change the imports path in netd/TEST_MAPPING after migrating from
-// system/netd/resolv to packages/modules/resolv.
+// Used only by dns_responder_client_ndk.cpp for ResolverStats.h
+// TODO: refactor stats to use a Parcel and eliminate this dependency
cc_library_headers {
name: "libnetd_resolv_internal_headers",
export_include_dirs: ["."],
@@ -18,6 +15,8 @@
local_include_dir: "binder",
srcs: [
"binder/android/net/IDnsResolver.aidl",
+ "binder/android/net/ResolverHostsParcel.aidl",
+ "binder/android/net/ResolverExperimentalOptionsParcel.aidl",
"binder/android/net/ResolverParamsParcel.aidl",
],
imports: [
@@ -28,16 +27,22 @@
gen_log: true,
},
},
- api_dir: "aidl/dnsresolver",
versions: [
"1",
"2",
+ "3",
],
}
cc_library {
name: "libnetd_resolv",
version_script: "libnetd_resolv.map.txt",
+ stubs: {
+ versions: [
+ "1",
+ ],
+ symbol_file: "libnetd_resolv.map.txt",
+ },
defaults: ["netd_defaults"],
srcs: [
"getaddrinfo.cpp",
@@ -50,12 +55,14 @@
"res_mkquery.cpp",
"res_query.cpp",
"res_send.cpp",
- "res_state.cpp",
"res_stats.cpp",
+ "util.cpp",
"Dns64Configuration.cpp",
"DnsProxyListener.cpp",
+ "DnsQueryLog.cpp",
"DnsResolver.cpp",
"DnsResolverService.cpp",
+ "DnsStats.cpp",
"DnsTlsDispatcher.cpp",
"DnsTlsQueryMap.cpp",
"DnsTlsTransport.cpp",
@@ -69,7 +76,7 @@
// Link most things statically to minimize our dependence on system ABIs.
stl: "libc++_static",
static_libs: [
- "dnsresolver_aidl_interface-ndk_platform",
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
"libbase",
"libcutils",
"libjsoncpp",
@@ -77,10 +84,10 @@
"libnetdutils",
"libprotobuf-cpp-lite",
"libstatslog_resolv",
- "libstatssocket",
+ "libstatspush_compat",
"libsysutils",
- "libutils", // Used by libstatslog_resolv
- "netd_event_listener_interface-V1-ndk_platform",
+ "libutils", // Used by libstatslog_resolv
+ "netd_event_listener_interface-ndk_platform",
"server_configurable_flags",
"stats_proto",
],
@@ -106,6 +113,16 @@
],
},
},
+ header_abi_checker: {
+ enabled: true,
+ symbol_file: "libnetd_resolv.map.txt",
+ },
+ sanitize: {
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
}
cc_library_static {
@@ -132,7 +149,7 @@
genrule {
name: "statslog_resolv.cpp",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv --namespace android,net,stats --importHeader statslog_resolv.h",
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv --namespace android,net,stats --importHeader statslog_resolv.h --supportQ",
out: [
"statslog_resolv.cpp",
],
@@ -146,63 +163,25 @@
export_generated_headers: ["statslog_resolv.h"],
static_libs: [
"libcutils",
- "libstatssocket",
+ "libgtest_prod", // Used by libstatspush_compat
+ "libstatspush_compat",
],
}
-cc_test {
- name: "resolv_integration_test",
- test_suites: ["device-tests"],
- require_root: true,
- defaults: ["netd_defaults"],
- tidy: false, // cuts test build time by > 1m30s
- srcs: [
- "tests/dns_responder/dns_responder.cpp",
- "dnsresolver_binder_test.cpp",
- "resolv_integration_test.cpp",
- ],
- header_libs: [
- "libnetd_resolv_headers",
- ],
- shared_libs: [
- "libbpf_android",
- "libbinder",
- "libcrypto",
- "liblog",
- "libnetd_client",
- "libssl",
- "libutils",
- ],
- static_libs: [
- "dnsresolver_aidl_interface-cpp",
- "libbase",
- "libgmock",
- "libnetd_test_dnsresponder",
- "libnetd_test_metrics_listener",
- "libnetd_test_resolv_utils",
- "libnetd_test_tun_interface",
- "libnetd_test_utils",
- "libnetdutils",
- "netd_aidl_interface-V2-cpp",
- "netd_event_listener_interface-V1-cpp",
- ],
- compile_multilib: "both",
- sanitize: {
- address: true,
- recover: ["all"],
- },
-}
-
+// TODO: Move this test to tests/
cc_test {
name: "resolv_unit_test",
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
require_root: true,
//TODO: drop root privileges and make it be an real unit test.
defaults: ["netd_defaults"],
srcs: [
"resolv_cache_unit_test.cpp",
+ "resolv_callback_unit_test.cpp",
"resolv_tls_unit_test.cpp",
"resolv_unit_test.cpp",
+ "DnsStatsTest.cpp",
+ "DnsQueryLogTest.cpp",
],
shared_libs: [
"libbase",
@@ -212,9 +191,8 @@
"libbinder_ndk",
],
static_libs: [
- "dnsresolver_aidl_interface-V2-cpp",
- "dnsresolver_aidl_interface-V2-ndk_platform",
- "netd_event_listener_interface-V1-ndk_platform",
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "netd_event_listener_interface-ndk_platform",
"libgmock",
"liblog",
"libnetd_resolv",
@@ -222,7 +200,21 @@
"libnetd_test_resolv_utils",
"libnetdutils",
"libprotobuf-cpp-lite",
+ "libstatslog_resolv",
+ "libstatspush_compat",
+ "libsysutils",
+ "libutils",
+ "resolv_stats_test_utils",
"server_configurable_flags",
"stats_proto",
],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
}
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index ce6081d..2046c34 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -24,6 +24,7 @@
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <resolv.h> // b64_pton()
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -54,10 +55,10 @@
#include "ResolverEventReporter.h"
#include "getaddrinfo.h"
#include "gethnamaddr.h"
-#include "netd_resolv/stats.h" // RCODE_TIMEOUT
#include "res_send.h"
#include "resolv_cache.h"
#include "resolv_private.h"
+#include "stats.h" // RCODE_TIMEOUT
#include "stats.pb.h"
using aidl::android::net::metrics::INetdEventListener;
@@ -74,10 +75,6 @@
// Limits the number of outstanding DNS queries by client UID.
constexpr int MAX_QUERIES_PER_UID = 256;
-// Max packet size for answer, sync with getaddrinfo.c
-// TODO: switch to dynamically allocated buffers with std::vector
-constexpr int MAXPACKET = 8 * 1024;
-
android::netdutils::OperationLimiter<uid_t> queryLimiter(MAX_QUERIES_PER_UID);
void logArguments(int argc, char** argv) {
@@ -147,7 +144,7 @@
return false;
}
-void maybeFixupNetContext(android_net_context* ctx) {
+void maybeFixupNetContext(android_net_context* ctx, pid_t pid) {
if (requestingUseLocalNameservers(ctx->flags) && !hasPermissionToBypassPrivateDns(ctx->uid)) {
// Not permitted; clear the flag.
ctx->flags &= ~NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
@@ -161,6 +158,7 @@
ctx->flags |= NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS;
}
}
+ ctx->pid = pid;
}
void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
@@ -293,7 +291,23 @@
return true;
}
-void initDnsEvent(NetworkDnsEventReported* event) {
+// Note: Even if it returns PDM_OFF, it doesn't mean there's no DoT stats in the message
+// because Private DNS mode can change at any time.
+PrivateDnsModes getPrivateDnsModeForMetrics(uint32_t netId) {
+ switch (gPrivateDnsConfiguration.getStatus(netId).mode) {
+ case PrivateDnsMode::OFF:
+ // It can also be due to netId not found.
+ return PrivateDnsModes::PDM_OFF;
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return PrivateDnsModes::PDM_OPPORTUNISTIC;
+ case PrivateDnsMode::STRICT:
+ return PrivateDnsModes::PDM_STRICT;
+ default:
+ return PrivateDnsModes::PDM_UNKNOWN;
+ }
+}
+
+void initDnsEvent(NetworkDnsEventReported* event, const android_net_context& netContext) {
// The value 0 has the special meaning of unset/unknown in Westworld atoms. So, we set both
// flags to -1 as default value.
// 1. The hints flag is only used in resolv_getaddrinfo. When user set it to -1 in
@@ -304,6 +318,7 @@
// resolv_res_nsend,res_nsend will do nothing special by the setting.
event->set_hints_ai_flags(-1);
event->set_res_nsend_flags(-1);
+ event->set_private_dns_modes(getPrivateDnsModeForMetrics(netContext.dns_netid));
}
// Return 0 if the event should not be logged.
@@ -315,6 +330,24 @@
return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0;
}
+void maybeLogQuery(int eventType, const android_net_context& netContext,
+ const NetworkDnsEventReported& event, const std::string& query_name,
+ const std::vector<std::string>& ip_addrs) {
+ // Skip reverse queries.
+ if (eventType == INetdEventListener::EVENT_GETHOSTBYADDR) return;
+
+ for (const auto& query : event.dns_query_events().dns_query_event()) {
+ // Log it when the cache misses.
+ if (query.cache_hit() != CS_FOUND) {
+ const int timeTakenMs = event.latency_micros() / 1000;
+ DnsQueryLog::Record record(netContext.dns_netid, netContext.uid, netContext.pid,
+ query_name, ip_addrs, timeTakenMs);
+ gDnsResolv->dnsQueryLog().push(std::move(record));
+ return;
+ }
+ }
+}
+
void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
int returnCode, NetworkDnsEventReported& event, const std::string& query_name,
const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
@@ -329,6 +362,8 @@
event.private_dns_modes(), dnsQueryBytesField, rate);
}
+ maybeLogQuery(eventType, netContext, event, query_name, ip_addrs);
+
const auto& listeners = ResolverEventReporter::getInstance().getListeners();
if (listeners.size() == 0) {
LOG(ERROR) << __func__
@@ -538,6 +573,13 @@
free(mHints);
}
+static bool evaluate_domain_name(const android_net_context &netcontext,
+ const char *host) {
+ if (!gResNetdCallbacks.evaluate_domain_name)
+ return true;
+ return gResNetdCallbacks.evaluate_domain_name(netcontext, host);
+}
+
static bool sendBE32(SocketClient* c, uint32_t data) {
uint32_t be_data = htonl(data);
return c->sendData(&be_data, sizeof(be_data)) == 0;
@@ -669,13 +711,18 @@
addrinfo* result = nullptr;
Stopwatch s;
- maybeFixupNetContext(&mNetContext);
+ maybeFixupNetContext(&mNetContext, mClient->getPid());
const uid_t uid = mClient->getUid();
int32_t rv = 0;
NetworkDnsEventReported event;
- initDnsEvent(&event);
+ initDnsEvent(&event, mNetContext);
if (queryLimiter.start(uid)) {
- rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result, &event);
+ if (evaluate_domain_name(mNetContext, mHost)) {
+ rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result,
+ &event);
+ } else {
+ rv = EAI_SYSTEM;
+ }
queryLimiter.finish(uid);
} else {
// Note that this error code is currently not passed down to the client.
@@ -856,7 +903,7 @@
<< mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}";
Stopwatch s;
- maybeFixupNetContext(&mNetContext);
+ maybeFixupNetContext(&mNetContext, mClient->getPid());
// Decode
std::vector<uint8_t> msg(MAXPACKET, 0);
@@ -885,12 +932,17 @@
// Send DNS query
std::vector<uint8_t> ansBuf(MAXPACKET, 0);
- int arcode, nsendAns = -1;
+ int rcode = ns_r_noerror;
+ int nsendAns = -1;
NetworkDnsEventReported event;
- initDnsEvent(&event);
+ initDnsEvent(&event, mNetContext);
if (queryLimiter.start(uid)) {
- nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
- &arcode, static_cast<ResNsendFlags>(mFlags), &event);
+ if (evaluate_domain_name(mNetContext, rr_name.c_str())) {
+ nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
+ &rcode, static_cast<ResNsendFlags>(mFlags), &event);
+ } else {
+ nsendAns = -EAI_SYSTEM;
+ }
queryLimiter.finish(uid);
} else {
LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid
@@ -908,13 +960,13 @@
sendBE32(mClient, nsendAns);
if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
- resNSendToAiError(nsendAns, arcode), event, rr_name);
+ resNSendToAiError(nsendAns, rcode), event, rr_name);
}
return;
}
// Send rcode
- if (!sendBE32(mClient, arcode)) {
+ if (!sendBE32(mClient, rcode)) {
PLOG(WARNING) << "ResNSendHandler::run: resnsend: failed to send rcode to uid " << uid;
return;
}
@@ -931,7 +983,7 @@
const int total_ip_addr_count =
extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs);
reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
- resNSendToAiError(nsendAns, arcode), event, rr_name, ip_addrs,
+ resNSendToAiError(nsendAns, rcode), event, rr_name, ip_addrs,
total_ip_addr_count);
}
}
@@ -1073,17 +1125,21 @@
void DnsProxyListener::GetHostByNameHandler::run() {
Stopwatch s;
- maybeFixupNetContext(&mNetContext);
+ maybeFixupNetContext(&mNetContext, mClient->getPid());
const uid_t uid = mClient->getUid();
hostent* hp = nullptr;
hostent hbuf;
char tmpbuf[MAXPACKET];
int32_t rv = 0;
NetworkDnsEventReported event;
- initDnsEvent(&event);
+ initDnsEvent(&event, mNetContext);
if (queryLimiter.start(uid)) {
- rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp,
- &event);
+ if (evaluate_domain_name(mNetContext, mName)) {
+ rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp,
+ &event);
+ } else {
+ rv = EAI_SYSTEM;
+ }
queryLimiter.finish(uid);
} else {
rv = EAI_MEMORY;
@@ -1236,14 +1292,14 @@
void DnsProxyListener::GetHostByAddrHandler::run() {
Stopwatch s;
- maybeFixupNetContext(&mNetContext);
+ maybeFixupNetContext(&mNetContext, mClient->getPid());
const uid_t uid = mClient->getUid();
hostent* hp = nullptr;
hostent hbuf;
char tmpbuf[MAXPACKET];
int32_t rv = 0;
NetworkDnsEventReported event;
- initDnsEvent(&event);
+ initDnsEvent(&event, mNetContext);
if (queryLimiter.start(uid)) {
rv = resolv_gethostbyaddr(mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf,
sizeof tmpbuf, &mNetContext, &hp, &event);
diff --git a/DnsQueryLog.cpp b/DnsQueryLog.cpp
new file mode 100644
index 0000000..6f0e179
--- /dev/null
+++ b/DnsQueryLog.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ *
+ */
+
+#include "DnsQueryLog.h"
+
+#include <android-base/stringprintf.h>
+
+namespace android::net {
+
+namespace {
+
+std::string maskHostname(const std::string& hostname) {
+ // Boundary issue is handled in substr().
+ return hostname.substr(0, 1) + "***";
+}
+
+// Return the string of masked addresses of the first v4 address and the first v6 address.
+std::string maskIps(const std::vector<std::string>& ips) {
+ std::string ret;
+ bool v4Found = false, v6Found = false;
+ for (const auto& ip : ips) {
+ if (auto pos = ip.find_first_of(':'); pos != ip.npos && !v6Found) {
+ ret += ip.substr(0, pos + 1) + "***, ";
+ v6Found = true;
+ } else if (auto pos = ip.find_first_of('.'); pos != ip.npos && !v4Found) {
+ ret += ip.substr(0, pos + 1) + "***, ";
+ v4Found = true;
+ }
+ if (v6Found && v4Found) break;
+ }
+ return ret.empty() ? "" : ret.substr(0, ret.length() - 2);
+}
+
+// Return the readable string format "hr:min:sec.ms".
+std::string timestampToString(const std::chrono::system_clock::time_point& ts) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ const auto time_sec = std::chrono::system_clock::to_time_t(ts);
+ char buf[32];
+ std::strftime(buf, sizeof(buf), "%H:%M:%S", std::localtime(&time_sec));
+ int ms = duration_cast<milliseconds>(ts.time_since_epoch()).count() % 1000;
+ return android::base::StringPrintf("%s.%03d", buf, ms);
+}
+
+} // namespace
+
+void DnsQueryLog::push(Record&& record) {
+ std::lock_guard guard(mLock);
+ mQueue.push_back(std::move(record));
+ if (mQueue.size() > mCapacity) {
+ mQueue.pop_front();
+ }
+}
+
+void DnsQueryLog::dump(netdutils::DumpWriter& dw) const {
+ dw.println("DNS query log (last %lld minutes):", (mValidityTimeMs / 60000).count());
+ netdutils::ScopedIndent indentStats(dw);
+ const auto now = std::chrono::system_clock::now();
+
+ std::lock_guard guard(mLock);
+ for (const auto& record : mQueue) {
+ if (now - record.timestamp > mValidityTimeMs) continue;
+
+ const std::string maskedHostname = maskHostname(record.hostname);
+ const std::string maskedIpsStr = maskIps(record.addrs);
+ const std::string time = timestampToString(record.timestamp);
+ dw.println("time=%s netId=%u uid=%u pid=%d hostname=%s answer=[%s] (%dms)", time.c_str(),
+ record.netId, record.uid, record.pid, maskedHostname.c_str(),
+ maskedIpsStr.c_str(), record.timeTaken);
+ }
+}
+
+} // namespace android::net
diff --git a/DnsQueryLog.h b/DnsQueryLog.h
new file mode 100644
index 0000000..c19f8db
--- /dev/null
+++ b/DnsQueryLog.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/DumpWriter.h>
+
+namespace android::net {
+
+// A circular buffer based class used for query logging. It's thread-safe for concurrent access.
+class DnsQueryLog {
+ public:
+ static constexpr std::string_view DUMP_KEYWORD = "querylog";
+
+ struct Record {
+ Record(uint32_t netId, uid_t uid, pid_t pid, const std::string& hostname,
+ const std::vector<std::string>& addrs, int timeTaken)
+ : netId(netId),
+ uid(uid),
+ pid(pid),
+ hostname(hostname),
+ addrs(addrs),
+ timeTaken(timeTaken) {}
+ const uint32_t netId;
+ const uid_t uid;
+ const pid_t pid;
+ const std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
+ const std::string hostname;
+ const std::vector<std::string> addrs;
+ const int timeTaken;
+ };
+
+ // Allow the tests to set the capacity and the validaty time in milliseconds.
+ DnsQueryLog(size_t size = kDefaultLogSize,
+ std::chrono::milliseconds time = kDefaultValidityMinutes)
+ : mCapacity(size), mValidityTimeMs(time) {}
+
+ void push(Record&& record) EXCLUDES(mLock);
+ void dump(netdutils::DumpWriter& dw) const EXCLUDES(mLock);
+
+ private:
+ mutable std::mutex mLock;
+ std::deque<Record> mQueue GUARDED_BY(mLock);
+ const size_t mCapacity;
+ const std::chrono::milliseconds mValidityTimeMs;
+
+ // The capacity of the circular buffer.
+ static constexpr size_t kDefaultLogSize = 200;
+
+ // Limit to dump the queries within last |kDefaultValidityMinutes| minutes.
+ static constexpr std::chrono::minutes kDefaultValidityMinutes{60};
+};
+
+} // namespace android::net
diff --git a/DnsQueryLogTest.cpp b/DnsQueryLogTest.cpp
new file mode 100644
index 0000000..b652412
--- /dev/null
+++ b/DnsQueryLogTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#include <regex>
+#include <thread>
+
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "DnsQueryLog.h"
+
+using namespace std::chrono_literals;
+
+namespace android::net {
+
+namespace {
+
+// Dump the log to STDOUT and capture it.
+std::string captureDumpOutput(const DnsQueryLog& queryLog) {
+ netdutils::DumpWriter dw(STDOUT_FILENO);
+ CapturedStdout captured;
+ queryLog.dump(dw);
+ return captured.str();
+}
+
+// A simple check for the dump result by checking the netIds one by one.
+void verifyDumpOutput(const std::string& dumpLog, const std::vector<int>& expectedNetIds) {
+ // Capture three matches: netId, hostname, and answer (empty allowed).
+ static const std::regex pattern(
+ R"(netId=(\d+).* hostname=([\w\*]+) answer=\[([\w:,\.\*\s]*)\])");
+
+ std::string str(dumpLog);
+ std::smatch sm;
+ for (const auto& netId : expectedNetIds) {
+ SCOPED_TRACE(netId);
+ EXPECT_TRUE(std::regex_search(str, sm, pattern));
+ EXPECT_EQ(sm[1], std::to_string(netId));
+ str = sm.suffix();
+ }
+
+ // Ensure the dumpLog is exactly as expected.
+ EXPECT_FALSE(std::regex_search(str, sm, pattern));
+}
+
+} // namespace
+
+class DnsQueryLogTest : public ::testing::Test {
+ protected:
+ const std::vector<std::string> serversV4 = {"127.0.0.1", "1.2.3.4"};
+ const std::vector<std::string> serversV4V6 = {"127.0.0.1", "1.2.3.4", "2001:db8::1",
+ "fe80:1::2%testnet"};
+};
+
+TEST_F(DnsQueryLogTest, Push) {
+ std::vector<DnsQueryLog::Record> records = {
+ DnsQueryLog::Record(30, 1000, 1000, "example.com", serversV4, 10),
+ DnsQueryLog::Record(31, 1000, 1000, "", serversV4, 10), // Empty hostname.
+ DnsQueryLog::Record(32, 1000, 1000, "example.com", {}, 10), // No answer.
+ DnsQueryLog::Record(33, 1000, 1000, "example.com", serversV4V6, 10),
+ };
+ DnsQueryLog queryLog;
+ for (auto& r : records) {
+ queryLog.push(std::move(r));
+ }
+
+ std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, {30, 31, 32, 33});
+}
+
+TEST_F(DnsQueryLogTest, PushStressTest) {
+ const int threadNum = 100;
+ const int pushNum = 1000;
+ const size_t size = 500;
+ DnsQueryLog queryLog(size);
+ std::vector<std::thread> threads(threadNum);
+
+ // Launch 'threadNum' threads to push the same queryLog 'pushNum' times.
+ for (auto& thread : threads) {
+ thread = std::thread([&]() {
+ for (int i = 0; i < pushNum; i++) {
+ DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10);
+ queryLog.push(std::move(record));
+ }
+ });
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ // Verify there are exact 'size' records in queryLog.
+ std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, std::vector(size, 30));
+}
+
+TEST_F(DnsQueryLogTest, ZeroSize) {
+ const size_t size = 0;
+ DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10);
+ DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10);
+ DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10);
+
+ DnsQueryLog queryLog(size);
+ queryLog.push(std::move(r1));
+ queryLog.push(std::move(r2));
+ queryLog.push(std::move(r3));
+
+ std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, {});
+}
+
+TEST_F(DnsQueryLogTest, CapacityFull) {
+ const size_t size = 3;
+ DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10);
+ DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10);
+ DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10);
+ DnsQueryLog::Record r4(33, 1000, 1000, "www.example4.com", serversV4V6, 10);
+ const std::vector<int> expectedNetIds = {31, 32, 33};
+
+ DnsQueryLog queryLog(size);
+ queryLog.push(std::move(r1));
+ queryLog.push(std::move(r2));
+ queryLog.push(std::move(r3));
+ queryLog.push(std::move(r4));
+
+ std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, expectedNetIds);
+}
+
+TEST_F(DnsQueryLogTest, ValidityTime) {
+ DnsQueryLog::Record r1(30, 1000, 1000, "www.example.com", serversV4, 10);
+ DnsQueryLog queryLog(3, 100ms);
+ queryLog.push(std::move(r1));
+
+ // Dump the output and verify the correctness by checking netId.
+ std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, {30});
+
+ std::this_thread::sleep_for(150ms);
+
+ // The record is expired thus not shown in the output.
+ output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, {});
+
+ // Push another record to ensure it still works.
+ DnsQueryLog::Record r2(31, 1000, 1000, "example.com", serversV4V6, 10);
+ queryLog.push(std::move(r2));
+ output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, {31});
+}
+
+} // namespace android::net
diff --git a/DnsResolver.cpp b/DnsResolver.cpp
index 70ecc7e..9dba693 100644
--- a/DnsResolver.cpp
+++ b/DnsResolver.cpp
@@ -38,6 +38,7 @@
gResNetdCallbacks.log = callbacks->log;
if (gApiLevel >= 30) {
gResNetdCallbacks.tagSocket = callbacks->tagSocket;
+ gResNetdCallbacks.evaluate_domain_name = callbacks->evaluate_domain_name;
}
android::net::gDnsResolv = android::net::DnsResolver::getInstance();
return android::net::gDnsResolv->start();
diff --git a/DnsResolver.h b/DnsResolver.h
index 2efa341..863b096 100644
--- a/DnsResolver.h
+++ b/DnsResolver.h
@@ -18,6 +18,7 @@
#define _DNS_RESOLVER_H_
#include "DnsProxyListener.h"
+#include "DnsQueryLog.h"
#include "ResolverController.h"
#include "netd_resolv/resolv.h"
#include "netdutils/Log.h"
@@ -34,11 +35,14 @@
DnsResolver(DnsResolver const&) = delete;
void operator=(DnsResolver const&) = delete;
+ DnsQueryLog& dnsQueryLog() { return mQueryLog; }
+
ResolverController resolverCtrl;
private:
DnsResolver() {}
DnsProxyListener mDnsProxyListener;
+ DnsQueryLog mQueryLog;
};
extern DnsResolver* gDnsResolv;
diff --git a/DnsResolverService.cpp b/DnsResolverService.cpp
index 3b83381..c4b42bc 100644
--- a/DnsResolverService.cpp
+++ b/DnsResolverService.cpp
@@ -92,7 +92,7 @@
return STATUS_OK;
}
-binder_status_t DnsResolverService::dump(int fd, const char**, uint32_t) {
+binder_status_t DnsResolverService::dump(int fd, const char** args, uint32_t numArgs) {
auto dump_permission = checkAnyPermission({PERM_DUMP});
if (!dump_permission.isOk()) {
return STATUS_PERMISSION_DENIED;
@@ -101,6 +101,14 @@
// This method does not grab any locks. If individual classes need locking
// their dump() methods MUST handle locking appropriately.
DumpWriter dw(fd);
+
+ if (numArgs == 1 && std::string(args[0]) == DnsQueryLog::DUMP_KEYWORD) {
+ dw.blankline();
+ gDnsResolv->dnsQueryLog().dump(dw);
+ dw.blankline();
+ return STATUS_OK;
+ }
+
for (auto netId : resolv_list_caches()) {
dw.println("NetId: %u", netId);
gDnsResolv->resolverCtrl.dump(dw, netId);
@@ -166,6 +174,13 @@
const ResolverParamsParcel& resolverParams) {
// Locking happens in PrivateDnsConfiguration and res_* functions.
ENFORCE_INTERNAL_PERMISSIONS();
+
+ uid_t uid = AIBinder_getCallingUid();
+ if (resolverParams.caCertificate.size() != 0 && uid == AID_SYSTEM) {
+ auto err = StringPrintf("UID %d is not authorized to set a non-empty CA certificate", uid);
+ return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(EX_SECURITY, err.c_str()));
+ }
+
// TODO: Remove this log after AIDL gen_log supporting more types, b/129732660
auto entry =
gDnsResolverLog.newEntry()
@@ -249,5 +264,14 @@
return statusFromErrcode(res);
}
+::ndk::ScopedAStatus DnsResolverService::flushNetworkCache(int netId) {
+ // Locking happens in res_cache.cpp functions.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = gDnsResolv->resolverCtrl.flushNetworkCache(netId);
+
+ return statusFromErrcode(res);
+}
+
} // namespace net
} // namespace android
diff --git a/DnsResolverService.h b/DnsResolverService.h
index 1a1c9e7..e484b3c 100644
--- a/DnsResolverService.h
+++ b/DnsResolverService.h
@@ -50,6 +50,7 @@
std::vector<int32_t>* wait_for_pending_req_timeout_count) override;
::ndk::ScopedAStatus destroyNetworkCache(int32_t netId) override;
::ndk::ScopedAStatus createNetworkCache(int32_t netId) override;
+ ::ndk::ScopedAStatus flushNetworkCache(int32_t netId) override;
// DNS64-related commands
::ndk::ScopedAStatus startPrefix64Discovery(int32_t netId) override;
diff --git a/DnsStats.cpp b/DnsStats.cpp
new file mode 100644
index 0000000..970e30e
--- /dev/null
+++ b/DnsStats.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ *
+ */
+
+#define LOG_TAG "resolv"
+
+#include "DnsStats.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android::net {
+
+using base::StringPrintf;
+using netdutils::DumpWriter;
+using netdutils::IPAddress;
+using netdutils::IPSockAddr;
+using netdutils::ScopedIndent;
+using std::chrono::duration_cast;
+using std::chrono::microseconds;
+using std::chrono::milliseconds;
+using std::chrono::seconds;
+
+namespace {
+
+static constexpr IPAddress INVALID_IPADDRESS = IPAddress();
+
+std::string rcodeToName(int rcode) {
+ // clang-format off
+ switch (rcode) {
+ case NS_R_NO_ERROR: return "NOERROR";
+ case NS_R_FORMERR: return "FORMERR";
+ case NS_R_SERVFAIL: return "SERVFAIL";
+ case NS_R_NXDOMAIN: return "NXDOMAIN";
+ case NS_R_NOTIMPL: return "NOTIMP";
+ case NS_R_REFUSED: return "REFUSED";
+ case NS_R_YXDOMAIN: return "YXDOMAIN";
+ case NS_R_YXRRSET: return "YXRRSET";
+ case NS_R_NXRRSET: return "NXRRSET";
+ case NS_R_NOTAUTH: return "NOTAUTH";
+ case NS_R_NOTZONE: return "NOTZONE";
+ case NS_R_INTERNAL_ERROR: return "INTERNAL_ERROR";
+ case NS_R_TIMEOUT: return "TIMEOUT";
+ default: return StringPrintf("UNKNOWN(%d)", rcode);
+ }
+ // clang-format on
+}
+
+bool ensureNoInvalidIp(const std::vector<IPSockAddr>& servers) {
+ for (const auto& server : servers) {
+ if (server.ip() == INVALID_IPADDRESS || server.port() == 0) {
+ LOG(WARNING) << "Invalid server: " << server;
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+// The comparison ignores the last update time.
+bool StatsData::operator==(const StatsData& o) const {
+ return std::tie(serverSockAddr, total, rcodeCounts, latencyUs) ==
+ std::tie(o.serverSockAddr, o.total, o.rcodeCounts, o.latencyUs);
+}
+
+std::string StatsData::toString() const {
+ if (total == 0) return StringPrintf("%s <no data>", serverSockAddr.ip().toString().c_str());
+
+ const auto now = std::chrono::steady_clock::now();
+ const int meanLatencyMs = duration_cast<milliseconds>(latencyUs).count() / total;
+ const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count();
+ std::string buf;
+ for (const auto& [rcode, counts] : rcodeCounts) {
+ if (counts != 0) {
+ buf += StringPrintf("%s:%d ", rcodeToName(rcode).c_str(), counts);
+ }
+ }
+ return StringPrintf("%s (%d, %dms, [%s], %ds)", serverSockAddr.ip().toString().c_str(), total,
+ meanLatencyMs, buf.c_str(), lastUpdateSec);
+}
+
+StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size)
+ : mCapacity(size), mStatsData(ipSockAddr) {}
+
+void StatsRecords::push(const Record& record) {
+ updateStatsData(record, true);
+ mRecords.push_back(record);
+
+ if (mRecords.size() > mCapacity) {
+ updateStatsData(mRecords.front(), false);
+ mRecords.pop_front();
+ }
+}
+
+void StatsRecords::updateStatsData(const Record& record, const bool add) {
+ const int rcode = record.rcode;
+ if (add) {
+ mStatsData.total += 1;
+ mStatsData.rcodeCounts[rcode] += 1;
+ mStatsData.latencyUs += record.latencyUs;
+ } else {
+ mStatsData.total -= 1;
+ mStatsData.rcodeCounts[rcode] -= 1;
+ mStatsData.latencyUs -= record.latencyUs;
+ }
+ mStatsData.lastUpdate = std::chrono::steady_clock::now();
+}
+
+bool DnsStats::setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol) {
+ if (!ensureNoInvalidIp(servers)) return false;
+
+ ServerStatsMap& statsMap = mStats[protocol];
+ for (const auto& server : servers) {
+ statsMap.try_emplace(server, StatsRecords(server, kLogSize));
+ }
+
+ // Clean up the map to eliminate the nodes not belonging to the given list of servers.
+ const auto cleanup = [&](ServerStatsMap* statsMap) {
+ ServerStatsMap tmp;
+ for (const auto& server : servers) {
+ if (statsMap->find(server) != statsMap->end()) {
+ tmp.insert(statsMap->extract(server));
+ }
+ }
+ statsMap->swap(tmp);
+ };
+
+ cleanup(&statsMap);
+
+ return true;
+}
+
+bool DnsStats::addStats(const IPSockAddr& ipSockAddr, const DnsQueryEvent& record) {
+ if (ipSockAddr.ip() == INVALID_IPADDRESS) return false;
+
+ for (auto& [serverSockAddr, statsRecords] : mStats[record.protocol()]) {
+ if (serverSockAddr == ipSockAddr) {
+ const StatsRecords::Record rec = {
+ .rcode = record.rcode(),
+ .latencyUs = microseconds(record.latency_micros()),
+ };
+ statsRecords.push(rec);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<StatsData> DnsStats::getStats(Protocol protocol) const {
+ std::vector<StatsData> ret;
+
+ if (mStats.find(protocol) != mStats.end()) {
+ for (const auto& [_, statsRecords] : mStats.at(protocol)) {
+ ret.push_back(statsRecords.getStatsData());
+ }
+ }
+ return ret;
+}
+
+void DnsStats::dump(DumpWriter& dw) {
+ const auto dumpStatsMap = [&](ServerStatsMap& statsMap) {
+ ScopedIndent indentLog(dw);
+ if (statsMap.size() == 0) {
+ dw.println("<no server>");
+ return;
+ }
+ for (const auto& [_, statsRecords] : statsMap) {
+ dw.println("%s", statsRecords.getStatsData().toString().c_str());
+ }
+ };
+
+ dw.println("Server statistics: (total, RTT avg, {rcode:counts}, last update)");
+ ScopedIndent indentStats(dw);
+
+ dw.println("over UDP");
+ dumpStatsMap(mStats[PROTO_UDP]);
+
+ dw.println("over TLS");
+ dumpStatsMap(mStats[PROTO_DOT]);
+
+ dw.println("over TCP");
+ dumpStatsMap(mStats[PROTO_TCP]);
+}
+
+} // namespace android::net
diff --git a/DnsStats.h b/DnsStats.h
new file mode 100644
index 0000000..40dad94
--- /dev/null
+++ b/DnsStats.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <chrono>
+#include <deque>
+#include <map>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/DumpWriter.h>
+#include <netdutils/InternetAddresses.h>
+
+#include "ResolverStats.h"
+#include "stats.pb.h"
+
+namespace android::net {
+
+// The overall information of a StatsRecords.
+struct StatsData {
+ StatsData(const netdutils::IPSockAddr& ipSockAddr) : serverSockAddr(ipSockAddr) {
+ lastUpdate = std::chrono::steady_clock::now();
+ };
+
+ // Server socket address.
+ netdutils::IPSockAddr serverSockAddr;
+
+ // The most recent number of records being accumulated.
+ int total = 0;
+
+ // The map used to store the number of each rcode.
+ std::map<int, int> rcodeCounts;
+
+ // The aggregated RTT in microseconds.
+ // For DNS-over-TCP, it includes TCP handshake.
+ // For DNS-over-TLS, it might include TCP handshake plus SSL handshake.
+ std::chrono::microseconds latencyUs = {};
+
+ // The last update timestamp.
+ std::chrono::time_point<std::chrono::steady_clock> lastUpdate;
+
+ std::string toString() const;
+
+ // For testing.
+ bool operator==(const StatsData& o) const;
+ friend std::ostream& operator<<(std::ostream& os, const StatsData& data) {
+ return os << data.toString();
+ }
+};
+
+// A circular buffer based class used to store the statistics for a server with a protocol.
+class StatsRecords {
+ public:
+ struct Record {
+ int rcode;
+ std::chrono::microseconds latencyUs;
+ };
+
+ StatsRecords(const netdutils::IPSockAddr& ipSockAddr, size_t size);
+
+ void push(const Record& record);
+
+ const StatsData& getStatsData() const { return mStatsData; }
+
+ private:
+ void updateStatsData(const Record& record, const bool add);
+
+ std::deque<Record> mRecords;
+ size_t mCapacity;
+ StatsData mStatsData;
+};
+
+// DnsStats class manages the statistics of DNS servers per netId.
+// The class itself is not thread-safe.
+class DnsStats {
+ public:
+ using ServerStatsMap = std::map<netdutils::IPSockAddr, StatsRecords>;
+
+ // Add |servers| to the map, and remove no-longer-used servers.
+ // Return true if they are successfully added; otherwise, return false.
+ bool setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol);
+
+ // Return true if |record| is successfully added into |server|'s stats; otherwise, return false.
+ bool addStats(const netdutils::IPSockAddr& server, const DnsQueryEvent& record);
+
+ void dump(netdutils::DumpWriter& dw);
+
+ // For testing.
+ std::vector<StatsData> getStats(Protocol protocol) const;
+
+ // TODO: Compatible support for getResolverInfo().
+ // TODO: Support getSortedServers().
+
+ static constexpr size_t kLogSize = 128;
+
+ private:
+ std::map<Protocol, ServerStatsMap> mStats;
+};
+
+} // namespace android::net
diff --git a/DnsStatsTest.cpp b/DnsStatsTest.cpp
new file mode 100644
index 0000000..419e6db
--- /dev/null
+++ b/DnsStatsTest.cpp
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ */
+
+#include <array>
+
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DnsStats.h"
+
+namespace android::net {
+
+using namespace std::chrono_literals;
+using android::netdutils::IPSockAddr;
+using std::chrono::milliseconds;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAreArray;
+
+namespace {
+
+DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode,
+ const milliseconds& latency) {
+ DnsQueryEvent event;
+ event.set_protocol(protocol);
+ event.set_rcode(rcode);
+ event.set_latency_micros(latency.count() * 1000);
+ return event;
+}
+
+StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs,
+ const std::map<int, int>& rcodeCounts) {
+ StatsData ret(server);
+ ret.total = total;
+ ret.latencyUs = latencyMs;
+ ret.rcodeCounts = rcodeCounts;
+ return ret;
+}
+
+} // namespace
+
+class StatsRecordsTest : public ::testing::Test {};
+
+TEST_F(StatsRecordsTest, PushRecord) {
+ const IPSockAddr server = IPSockAddr::toIPSockAddr("127.0.0.2", 53);
+ constexpr size_t size = 3;
+ const StatsRecords::Record recordNoError = {NS_R_NO_ERROR, 10ms};
+ const StatsRecords::Record recordTimeout = {NS_R_TIMEOUT, 250ms};
+
+ StatsRecords sr(server, size);
+ EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 0, 0ms, {}));
+
+ sr.push(recordNoError);
+ EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 1, 10ms, {{NS_R_NO_ERROR, 1}}));
+
+ sr.push(recordNoError);
+ EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 2, 20ms, {{NS_R_NO_ERROR, 2}}));
+
+ sr.push(recordTimeout);
+ EXPECT_EQ(sr.getStatsData(),
+ makeStatsData(server, 3, 270ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 1}}));
+
+ sr.push(recordTimeout);
+ EXPECT_EQ(sr.getStatsData(),
+ makeStatsData(server, 3, 510ms, {{NS_R_NO_ERROR, 1}, {NS_R_TIMEOUT, 2}}));
+
+ sr.push(recordTimeout);
+ EXPECT_EQ(sr.getStatsData(),
+ makeStatsData(server, 3, 750ms, {{NS_R_NO_ERROR, 0}, {NS_R_TIMEOUT, 3}}));
+}
+
+class DnsStatsTest : public ::testing::Test {
+ protected:
+ std::string captureDumpOutput() {
+ netdutils::DumpWriter dw(STDOUT_FILENO);
+ CapturedStdout captured;
+ mDnsStats.dump(dw);
+ return captured.str();
+ }
+
+ // Get the output string from dump() and check the content.
+ void verifyDumpOutput(const std::vector<StatsData>& tcpData,
+ const std::vector<StatsData>& udpData,
+ const std::vector<StatsData>& dotData) {
+ // A simple pattern to capture two matches:
+ // server address (empty allowed) and its statistics.
+ const std::regex pattern(R"(\s{4,}([0-9a-fA-F:\.]*) ([<(].*[>)]))");
+ std::string dumpString = captureDumpOutput();
+
+ const auto check = [&](const std::vector<StatsData>& statsData, const std::string& protocol,
+ std::string* dumpString) {
+ SCOPED_TRACE(protocol);
+ ASSERT_NE(dumpString->find(protocol), std::string::npos);
+ std::smatch sm;
+
+ // Expect to show something even if none of servers is set.
+ if (statsData.empty()) {
+ ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
+ EXPECT_TRUE(sm[1].str().empty());
+ EXPECT_EQ(sm[2], "<no server>");
+ *dumpString = sm.suffix();
+ return;
+ }
+
+ for (const auto& stats : statsData) {
+ ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
+ EXPECT_EQ(sm[1], stats.serverSockAddr.ip().toString());
+ EXPECT_FALSE(sm[2].str().empty());
+ *dumpString = sm.suffix();
+ }
+ };
+
+ check(udpData, "UDP", &dumpString);
+ check(dotData, "TLS", &dumpString);
+ check(tcpData, "TCP", &dumpString);
+
+ // Ensure the whole string has been checked.
+ EXPECT_EQ(dumpString, "\n");
+ }
+
+ DnsStats mDnsStats;
+};
+
+TEST_F(DnsStatsTest, SetServers) {
+ // Check before any operation to mDnsStats.
+ verifyDumpOutput({}, {}, {});
+
+ static const struct {
+ std::vector<std::string> servers;
+ std::vector<std::string> expectation;
+ bool isSuccess;
+ } tests[] = {
+ // Normal case.
+ {
+ {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
+ {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
+ true,
+ },
+ // Duplicate servers.
+ {
+ {"127.0.0.1", "2001:db8::2", "127.0.0.1", "2001:db8::2"},
+ {"127.0.0.1", "2001:db8::2"},
+ true,
+ },
+ // Invalid server addresses. The state remains in previous state.
+ {
+ {"not_an_ip", "127.0.0.3", "127.a.b.2"},
+ {"127.0.0.1", "2001:db8::2"},
+ false,
+ },
+ // Clean up the old servers 127.0.0.1 and 127.0.0.2.
+ {
+ {"127.0.0.4", "2001:db8::5"},
+ {"127.0.0.4", "2001:db8::5"},
+ true,
+ },
+ // Empty list.
+ {{}, {}, true},
+ };
+
+ for (const auto& [servers, expectation, isSuccess] : tests) {
+ std::vector<IPSockAddr> ipSockAddrs;
+ ipSockAddrs.reserve(servers.size());
+ for (const auto& server : servers) {
+ ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53));
+ }
+
+ EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_TCP) == isSuccess);
+ EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_UDP) == isSuccess);
+ EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_DOT) == isSuccess);
+
+ std::vector<StatsData> expectedStats;
+ expectedStats.reserve(expectation.size());
+ for (const auto& exp : expectation) {
+ expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {}));
+ }
+
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+ }
+
+ verifyDumpOutput({}, {}, {});
+}
+
+TEST_F(DnsStatsTest, SetServersDifferentPorts) {
+ const std::vector<IPSockAddr> servers = {
+ IPSockAddr::toIPSockAddr("127.0.0.1", 0), IPSockAddr::toIPSockAddr("fe80::1", 0),
+ IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 5353),
+ IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("fe80::1", 53),
+ IPSockAddr::toIPSockAddr("fe80::1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 853),
+ };
+
+ // Servers setup fails due to port unset.
+ EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_TCP));
+ EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP));
+ EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT));
+
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+ verifyDumpOutput({}, {}, {});
+
+ EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP));
+ EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_UDP));
+ EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_DOT));
+
+ const std::vector<StatsData> expectedStats = {
+ makeStatsData(servers[2], 0, 0ms, {}), makeStatsData(servers[3], 0, 0ms, {}),
+ makeStatsData(servers[4], 0, 0ms, {}), makeStatsData(servers[5], 0, 0ms, {}),
+ makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}),
+ };
+
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+ verifyDumpOutput(expectedStats, expectedStats, expectedStats);
+}
+
+TEST_F(DnsStatsTest, AddStatsAndClear) {
+ const std::vector<IPSockAddr> servers = {
+ IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+ };
+ const DnsQueryEvent record = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
+
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+
+ // Fail to add stats because of incorrect arguments.
+ EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.0.0.4", 53), record));
+ EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.a.b.4", 53), record));
+
+ EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
+ EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
+ EXPECT_TRUE(mDnsStats.addStats(servers[1], record));
+
+ const std::vector<StatsData> expectedStatsForTcp = {
+ makeStatsData(servers[0], 0, 0ms, {}),
+ makeStatsData(servers[1], 0, 0ms, {}),
+ };
+ const std::vector<StatsData> expectedStatsForUdp = {
+ makeStatsData(servers[0], 2, 20ms, {{NS_R_NO_ERROR, 2}}),
+ makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
+ };
+
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+ verifyDumpOutput(expectedStatsForTcp, expectedStatsForUdp, {});
+
+ // Clear stats.
+ EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP));
+ EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP));
+ EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+ verifyDumpOutput({}, {}, {});
+}
+
+TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
+ std::vector<IPSockAddr> servers = {
+ IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+ };
+ const DnsQueryEvent recordNoError = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
+ const DnsQueryEvent recordTimeout = makeDnsQueryEvent(PROTO_UDP, NS_R_TIMEOUT, 250ms);
+
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+
+ // Add a record to 127.0.0.1.
+ EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
+
+ // Add four records to 127.0.0.2.
+ EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
+ EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
+ EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
+ EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
+
+ std::vector<StatsData> expectedStats = {
+ makeStatsData(servers[0], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
+ makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
+ };
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ verifyDumpOutput({}, expectedStats, {});
+
+ // Update the server list, the stats of 127.0.0.2 will remain.
+ servers = {
+ IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.3", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.4", 53),
+ };
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+ expectedStats = {
+ makeStatsData(servers[0], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
+ makeStatsData(servers[1], 0, 0ms, {}),
+ makeStatsData(servers[2], 0, 0ms, {}),
+ };
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ verifyDumpOutput({}, expectedStats, {});
+
+ // Let's add a record to 127.0.0.2 again.
+ EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
+ expectedStats = {
+ makeStatsData(servers[0], 5, 530ms, {{NS_R_NO_ERROR, 3}, {NS_R_TIMEOUT, 2}}),
+ makeStatsData(servers[1], 0, 0ms, {}),
+ makeStatsData(servers[2], 0, 0ms, {}),
+ };
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ verifyDumpOutput({}, expectedStats, {});
+}
+
+TEST_F(DnsStatsTest, AddStatsRecords_100000) {
+ constexpr size_t operations = 100000;
+ constexpr size_t logSize = DnsStats::kLogSize;
+ constexpr size_t rcodeNum = 4; // A value by which kLogSize is divisible.
+ ASSERT_EQ(logSize % rcodeNum, 0U);
+
+ const std::vector<IPSockAddr> servers = {
+ IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.3", 53),
+ IPSockAddr::toIPSockAddr("127.0.0.4", 53),
+ };
+
+ // To test unknown rcode in rcodeToName(), store the elements as type int.
+ const std::array<int, rcodeNum> rcodes = {
+ NS_R_NO_ERROR, // NOERROR
+ NS_R_NXDOMAIN, // NXDOMAIN
+ 99, // UNKNOWN(99)
+ NS_R_INTERNAL_ERROR, // INTERNAL_ERROR
+ };
+
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+ EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_DOT));
+
+ for (size_t i = 0; i < operations; i++) {
+ const NsRcode rcode = static_cast<NsRcode>(rcodes[i % rcodeNum]);
+ const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, rcode, 10ms);
+ const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, rcode, 10ms);
+ const auto eventDot = makeDnsQueryEvent(PROTO_DOT, rcode, 10ms);
+ for (const auto& server : servers) {
+ SCOPED_TRACE(server.toString() + "-" + std::to_string(i));
+ ASSERT_TRUE(mDnsStats.addStats(server, eventTcp));
+ ASSERT_TRUE(mDnsStats.addStats(server, eventUdp));
+ ASSERT_TRUE(mDnsStats.addStats(server, eventDot));
+ }
+ }
+
+ std::map<int, int> expectedRcodeCounts;
+ for (const auto& rcode : rcodes) {
+ expectedRcodeCounts.try_emplace(rcode, 32);
+ }
+ const std::vector<StatsData> expectedStats = {
+ makeStatsData(servers[0], logSize, logSize * 10ms, expectedRcodeCounts),
+ makeStatsData(servers[1], logSize, logSize * 10ms, expectedRcodeCounts),
+ makeStatsData(servers[2], logSize, logSize * 10ms, expectedRcodeCounts),
+ makeStatsData(servers[3], logSize, logSize * 10ms, expectedRcodeCounts),
+ };
+
+ EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+ EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+ verifyDumpOutput(expectedStats, expectedStats, expectedStats);
+}
+
+} // namespace android::net
diff --git a/DnsTlsDispatcher.cpp b/DnsTlsDispatcher.cpp
index 58ef2a7..8747e46 100644
--- a/DnsTlsDispatcher.cpp
+++ b/DnsTlsDispatcher.cpp
@@ -17,8 +17,11 @@
#define LOG_TAG "resolv"
#include "DnsTlsDispatcher.h"
+
#include <netdutils/Stopwatch.h>
+
#include "DnsTlsSocketFactory.h"
+#include "resolv_cache.h"
#include "resolv_private.h"
#include "stats.pb.h"
@@ -27,6 +30,7 @@
namespace android {
namespace net {
+using android::netdutils::IPSockAddr;
using android::netdutils::Stopwatch;
using netdutils::Slice;
@@ -97,14 +101,17 @@
for (const auto& server : orderedServers) {
DnsQueryEvent* dnsQueryEvent =
statp->event->mutable_dns_query_events()->add_dns_query_event();
+
+ bool connectTriggered = false;
Stopwatch queryStopwatch;
- code = this->query(server, statp->_mark, query, ans, resplen);
+ code = this->query(server, statp->_mark, query, ans, resplen, &connectTriggered);
dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
dnsQueryEvent->set_dns_server_index(serverCount++);
dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(server.ss.ss_family));
dnsQueryEvent->set_protocol(PROTO_DOT);
dnsQueryEvent->set_type(getQueryType(query.base(), query.size()));
+ dnsQueryEvent->set_connected(connectTriggered);
switch (code) {
// These response codes are valid responses and not expected to
@@ -112,19 +119,23 @@
case DnsTlsTransport::Response::success:
dnsQueryEvent->set_rcode(
static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode));
+ resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
return code;
case DnsTlsTransport::Response::limit_error:
dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
+ resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
return code;
// These response codes might differ when trying other servers, so
// keep iterating to see if we can get a different (better) result.
case DnsTlsTransport::Response::network_error:
// Sync from res_tls_send in res_send.cpp
dnsQueryEvent->set_rcode(NS_R_TIMEOUT);
- continue;
+ resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
+ break;
case DnsTlsTransport::Response::internal_error:
dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
- continue;
+ resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
+ break;
// No "default" statement.
}
}
@@ -133,8 +144,9 @@
}
DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned mark,
- const Slice query,
- const Slice ans, int *resplen) {
+ const Slice query, const Slice ans, int* resplen,
+ bool* connectTriggered) {
+ int connectCounter;
const Key key = std::make_pair(mark, server);
Transport* xport;
{
@@ -147,6 +159,7 @@
xport = it->second.get();
}
++xport->useCount;
+ connectCounter = xport->transport.getConnectCounter();
}
LOG(DEBUG) << "Sending query of length " << query.size();
@@ -170,6 +183,7 @@
auto now = std::chrono::steady_clock::now();
{
std::lock_guard guard(sLock);
+ *connectTriggered = (xport->transport.getConnectCounter() > connectCounter);
--xport->useCount;
xport->lastUsed = now;
cleanup(now);
diff --git a/DnsTlsDispatcher.h b/DnsTlsDispatcher.h
index b8e9968..9eb6dfe 100644
--- a/DnsTlsDispatcher.h
+++ b/DnsTlsDispatcher.h
@@ -54,11 +54,12 @@
const netdutils::Slice ans, int* _Nonnull resplen);
// Given a |query|, sends it to the server on the network indicated by |mark|,
- // and writes the response into |ans|, and indicates
- // the number of bytes written in |resplen|. Returns a success or error code.
+ // and writes the response into |ans|, and indicates the number of bytes written in |resplen|.
+ // If the whole procedure above triggers (or experiences) any new connection, |connectTriggered|
+ // is set. Returns a success or error code.
DnsTlsTransport::Response query(const DnsTlsServer& server, unsigned mark,
const netdutils::Slice query, const netdutils::Slice ans,
- int* _Nonnull resplen);
+ int* _Nonnull resplen, bool* _Nonnull connectTriggered);
private:
// This lock is static so that it can be used to annotate the Transport struct.
diff --git a/DnsTlsQueryMap.h b/DnsTlsQueryMap.h
index c5ab023..a6227f4 100644
--- a/DnsTlsQueryMap.h
+++ b/DnsTlsQueryMap.h
@@ -74,6 +74,9 @@
// Returns true if there are no pending queries.
bool empty();
+ // The maximum number of times we will send a query before abandoning it.
+ static constexpr int kMaxTries = 3;
+
private:
std::mutex mLock;
@@ -87,9 +90,6 @@
std::promise<Result> result;
};
- // The maximum number of times we will send a query before abandoning it.
- static constexpr int kMaxTries = 3;
-
// Outstanding queries by newId.
std::map<uint16_t, QueryPromise> mQueries GUARDED_BY(mLock);
diff --git a/DnsTlsServer.h b/DnsTlsServer.h
index f036b68..82d1a45 100644
--- a/DnsTlsServer.h
+++ b/DnsTlsServer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _DNS_DNSTLSSERVER_H
-#define _DNS_DNSTLSSERVER_H
+#pragma once
#include <chrono>
#include <set>
@@ -24,7 +23,7 @@
#include <netinet/in.h>
-#include <netd_resolv/params.h>
+#include <params.h>
namespace android {
namespace net {
@@ -62,7 +61,9 @@
// The time to wait for the attempt on connecting to the server.
// Set the default value 127 seconds to be consistent with TCP connect timeout.
// (presume net.ipv4.tcp_syn_retries = 6)
- std::chrono::milliseconds connectTimeout = std::chrono::milliseconds(127 * 1000);
+ static constexpr std::chrono::milliseconds kDotConnectTimeoutMs =
+ std::chrono::milliseconds(127 * 1000);
+ std::chrono::milliseconds connectTimeout = kDotConnectTimeoutMs;
// Exact comparison of DnsTlsServer objects
bool operator<(const DnsTlsServer& other) const;
@@ -78,5 +79,3 @@
} // namespace net
} // namespace android
-
-#endif // _DNS_DNSTLSSERVER_H
diff --git a/DnsTlsSocket.cpp b/DnsTlsSocket.cpp
index bb43ed9..e133ccc 100644
--- a/DnsTlsSocket.cpp
+++ b/DnsTlsSocket.cpp
@@ -38,6 +38,7 @@
#include <netdutils/SocketOption.h>
#include <netdutils/ThreadUtil.h>
+#include "netd_resolv/resolv.h"
#include "private/android_filesystem_config.h" // AID_DNS
#include "resolv_private.h"
@@ -95,7 +96,7 @@
return Status(errno);
}
- resolv_tag_socket(mSslFd.get(), AID_DNS);
+ resolv_tag_socket(mSslFd.get(), AID_DNS, NET_CONTEXT_INVALID_PID);
const socklen_t len = sizeof(mMark);
if (setsockopt(mSslFd.get(), SOL_SOCKET, SO_MARK, &mMark, len) == -1) {
@@ -159,7 +160,9 @@
// For discussion of alternative, sustainable approaches see b/71909242.
if (RESOLV_INJECT_CA_CERTIFICATE && !mServer.certificate.empty()) {
// Inject test CA certs from ResolverParamsParcel.caCertificate for internal testing.
- LOG(WARNING) << "test CA certificate is valid";
+ // This is only allowed by DnsResolverService if the caller is not AID_SYSTEM, and on
+ // debug builds.
+ LOG(WARNING) << "Setting test CA certificate. This should never happen in production code.";
if (!setTestCaCertificate()) {
LOG(ERROR) << "Failed to set test CA certificate";
return false;
diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp
index 3453765..9766b65 100644
--- a/DnsTlsTransport.cpp
+++ b/DnsTlsTransport.cpp
@@ -51,6 +51,11 @@
return std::move(record->result);
}
+int DnsTlsTransport::getConnectCounter() const {
+ std::lock_guard guard(mLock);
+ return mConnectCounter;
+}
+
bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
// Strip off the ID number and send the new ID instead.
bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
@@ -63,6 +68,7 @@
void DnsTlsTransport::doConnect() {
LOG(DEBUG) << "Constructing new socket";
mSocket = mFactory->createDnsTlsSocket(mServer, mMark, this, &mCache);
+ mConnectCounter++;
if (mSocket) {
auto queries = mQueries.getAll();
diff --git a/DnsTlsTransport.h b/DnsTlsTransport.h
index 3e43c7e..5f959da 100644
--- a/DnsTlsTransport.h
+++ b/DnsTlsTransport.h
@@ -57,12 +57,14 @@
// on networks where it doesn't actually work.
static bool validate(const DnsTlsServer& server, unsigned netid, uint32_t mark);
+ int getConnectCounter() const EXCLUDES(mLock);
+
// Implement IDnsTlsSocketObserver
void onResponse(std::vector<uint8_t> response) override;
void onClosed() override EXCLUDES(mLock);
private:
- std::mutex mLock;
+ mutable std::mutex mLock;
DnsTlsSessionCache mCache;
DnsTlsQueryMap mQueries;
@@ -85,6 +87,9 @@
// Send a query to the socket.
bool sendQuery(const DnsTlsQueryMap::Query q) REQUIRES(mLock);
+
+ // The number of times an attempt to connect the nameserver.
+ int mConnectCounter GUARDED_BY(mLock) = 0;
};
} // end of namespace net
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c8dbf77..27eac94 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[Builtin Hooks]
clang_format = true
+commit_msg_test_field = false
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 9242222..bfac68a 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -28,12 +28,23 @@
#include "ResolverEventReporter.h"
#include "netd_resolv/resolv.h"
#include "netdutils/BackoffSequence.h"
+#include "resolv_cache.h"
+#include "util.h"
using std::chrono::milliseconds;
namespace android {
namespace net {
+namespace {
+
+milliseconds getExperimentTimeout(const std::string& flagName, const milliseconds defaultValue) {
+ int val = getExperimentFlagInt(flagName, defaultValue.count());
+ return milliseconds((val < 1000) ? 1000 : val);
+}
+
+} // namespace
+
std::string addrToString(const sockaddr_storage* addr) {
char out[INET6_ADDRSTRLEN] = {0};
getnameinfo((const sockaddr*) addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
@@ -61,30 +72,24 @@
int PrivateDnsConfiguration::set(int32_t netId, uint32_t mark,
const std::vector<std::string>& servers, const std::string& name,
- const std::string& caCert, int32_t connectTimeoutMs) {
+ const std::string& caCert) {
LOG(DEBUG) << "PrivateDnsConfiguration::set(" << netId << ", 0x" << std::hex << mark << std::dec
- << ", " << servers.size() << ", " << name << ", " << connectTimeoutMs << "ms)";
+ << ", " << servers.size() << ", " << name << ")";
// Parse the list of servers that has been passed in
std::set<DnsTlsServer> tlsServers;
- for (size_t i = 0; i < servers.size(); ++i) {
+ for (const auto& s : servers) {
sockaddr_storage parsed;
- if (!parseServer(servers[i].c_str(), &parsed)) {
+ if (!parseServer(s.c_str(), &parsed)) {
return -EINVAL;
}
DnsTlsServer server(parsed);
server.name = name;
server.certificate = caCert;
-
- // connectTimeoutMs = 0: use the default timeout value.
- // connectTimeoutMs < 0: invalid timeout value.
- if (connectTimeoutMs > 0) {
- // Set a specific timeout value but limit it to be at least 1 second.
- server.connectTimeout =
- (connectTimeoutMs < 1000) ? milliseconds(1000) : milliseconds(connectTimeoutMs);
- }
-
+ server.connectTimeout =
+ getExperimentTimeout("dot_connect_timeout_ms", DnsTlsServer::kDotConnectTimeoutMs);
tlsServers.insert(server);
+ LOG(DEBUG) << "Set DoT connect timeout " << server.connectTimeout.count() << "ms for " << s;
}
std::lock_guard guard(mPrivateDnsLock);
@@ -95,6 +100,8 @@
} else {
mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
mPrivateDnsTransports.erase(netId);
+ resolv_stats_set_servers_for_dot(netId, {});
+ mPrivateDnsValidateThreads.erase(netId);
return 0;
}
@@ -126,7 +133,8 @@
validatePrivateDnsProvider(server, tracker, netId, mark);
}
}
- return 0;
+
+ return resolv_stats_set_servers_for_dot(netId, servers);
}
PrivateDnsStatus PrivateDnsConfiguration::getStatus(unsigned netId) {
@@ -152,6 +160,7 @@
std::lock_guard guard(mPrivateDnsLock);
mPrivateDnsModes.erase(netId);
mPrivateDnsTransports.erase(netId);
+ mPrivateDnsValidateThreads.erase(netId);
}
void PrivateDnsConfiguration::validatePrivateDnsProvider(const DnsTlsServer& server,
@@ -160,6 +169,10 @@
tracker[server] = Validation::in_process;
LOG(DEBUG) << "Server " << addrToString(&server.ss) << " marked as in_process on netId "
<< netId << ". Tracker now has size " << tracker.size();
+ // This judge must be after "tracker[server] = Validation::in_process;"
+ if (!needValidateThread(server, netId)) {
+ return;
+ }
// Note that capturing |server| and |netId| in this lambda create copies.
std::thread validate_thread([this, server, netId, mark] {
@@ -202,6 +215,7 @@
break;
}
}
+ this->cleanValidateThreadTracker(server, netId);
});
validate_thread.detach();
}
@@ -276,6 +290,50 @@
return reevaluationStatus;
}
+bool PrivateDnsConfiguration::needValidateThread(const DnsTlsServer& server, unsigned netId)
+ REQUIRES(mPrivateDnsLock) {
+ // Create the thread tracker if it was not present
+ auto threadPair = mPrivateDnsValidateThreads.find(netId);
+ if (threadPair == mPrivateDnsValidateThreads.end()) {
+ // No thread tracker yet for this netId.
+ bool added;
+ std::tie(threadPair, added) = mPrivateDnsValidateThreads.emplace(netId, ThreadTracker());
+ if (!added) {
+ LOG(ERROR) << "Memory error while needValidateThread for netId " << netId;
+ return true;
+ }
+ }
+ auto& threadTracker = threadPair->second;
+ if (threadTracker.count(server)) {
+ LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+ << " validate thread is already running. Thread tracker now has size "
+ << threadTracker.size();
+ return false;
+ } else {
+ threadTracker.insert(server);
+ LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+ << " validate thread is not running. Thread tracker now has size "
+ << threadTracker.size();
+ return true;
+ }
+}
+
+void PrivateDnsConfiguration::cleanValidateThreadTracker(const DnsTlsServer& server,
+ unsigned netId) {
+ std::lock_guard<std::mutex> guard(mPrivateDnsLock);
+ LOG(DEBUG) << "cleanValidateThreadTracker Server " << addrToString(&(server.ss))
+ << " validate thread is stopped.";
+
+ auto threadPair = mPrivateDnsValidateThreads.find(netId);
+ if (threadPair != mPrivateDnsValidateThreads.end()) {
+ auto& threadTracker = threadPair->second;
+ threadTracker.erase(server);
+ LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+ << " validate thread is stopped. Thread tracker now has size "
+ << threadTracker.size();
+ }
+}
+
// Start validation for newly added servers as well as any servers that have
// landed in Validation::fail state. Note that servers that have failed
// multiple validation attempts but for which there is still a validating
diff --git a/PrivateDnsConfiguration.h b/PrivateDnsConfiguration.h
index 6c50604..b66859c 100644
--- a/PrivateDnsConfiguration.h
+++ b/PrivateDnsConfiguration.h
@@ -53,8 +53,7 @@
class PrivateDnsConfiguration {
public:
int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers,
- const std::string& name, const std::string& caCert, int32_t connectTimeoutMs)
- EXCLUDES(mPrivateDnsLock);
+ const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock);
PrivateDnsStatus getStatus(unsigned netId) EXCLUDES(mPrivateDnsLock);
@@ -62,12 +61,16 @@
private:
typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
+ typedef std::set<DnsTlsServer, AddressComparator> ThreadTracker;
void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock);
bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success);
+ bool needValidateThread(const DnsTlsServer& server, unsigned netId) REQUIRES(mPrivateDnsLock);
+ void cleanValidateThreadTracker(const DnsTlsServer& server, unsigned netId);
+
// Start validation for newly added servers as well as any servers that have
// landed in Validation::fail state. Note that servers that have failed
// multiple validation attempts but for which there is still a validating
@@ -79,6 +82,7 @@
// Structure for tracking the validation status of servers on a specific netId.
// Using the AddressComparator ensures at most one entry per IP address.
std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
+ std::map<unsigned, ThreadTracker> mPrivateDnsValidateThreads GUARDED_BY(mPrivateDnsLock);
};
extern PrivateDnsConfiguration gPrivateDnsConfiguration;
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 6927e5f..c59676a 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -34,8 +34,8 @@
#include "PrivateDnsConfiguration.h"
#include "ResolverEventReporter.h"
#include "ResolverStats.h"
-#include "netd_resolv/stats.h"
#include "resolv_cache.h"
+#include "stats.h"
using aidl::android::net::ResolverParamsParcel;
@@ -194,6 +194,11 @@
return resolv_create_cache_for_net(netId);
}
+int ResolverController::flushNetworkCache(unsigned netId) {
+ LOG(VERBOSE) << __func__ << ": netId = " << netId;
+ return resolv_flush_cache_for_net(netId);
+}
+
int ResolverController::setResolverConfiguration(const ResolverParamsParcel& resolverParams) {
using aidl::android::net::IDnsResolver;
@@ -210,9 +215,9 @@
if (tlsServers.size() > MAXNS) {
tlsServers.resize(MAXNS);
}
- const int err = gPrivateDnsConfiguration.set(
- resolverParams.netId, fwmark.intValue, tlsServers, resolverParams.tlsName,
- resolverParams.caCertificate, resolverParams.tlsConnectTimeoutMs);
+ const int err =
+ gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers,
+ resolverParams.tlsName, resolverParams.caCertificate);
if (err != 0) {
return err;
@@ -227,7 +232,8 @@
res_params.retry_count = resolverParams.retryCount;
return resolv_set_nameservers(resolverParams.netId, resolverParams.servers,
- resolverParams.domains, res_params);
+ resolverParams.domains, res_params,
+ resolverParams.experimentalOptions);
}
int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers,
@@ -356,6 +362,8 @@
dw.decIndent();
}
dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count[0]);
+ resolv_stats_dump(dw, netId);
+ resolv_oem_options_dump(dw, netId);
}
dw.decIndent();
}
diff --git a/ResolverController.h b/ResolverController.h
index 4ff395b..c1c6f0f 100644
--- a/ResolverController.h
+++ b/ResolverController.h
@@ -38,6 +38,7 @@
void destroyNetworkCache(unsigned netid);
int createNetworkCache(unsigned netid);
+ int flushNetworkCache(unsigned netid);
int getPrefix64(unsigned netId, netdutils::IPPrefix* prefix);
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 382bfc3..10533ed 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,9 @@
{
"presubmit": [
{ "name": "resolv_integration_test" },
+ { "name": "resolv_gold_test" },
{ "name": "resolv_unit_test" },
- { "name": "resolv_stress_test" }
+ { "name": "resolv_stress_test" },
+ { "name": "resolv_stats_test_utils_test" }
]
}
diff --git a/aidl/dnsresolver/1/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/1/android/net/IDnsResolver.aidl
similarity index 100%
rename from aidl/dnsresolver/1/android/net/IDnsResolver.aidl
rename to aidl_api/dnsresolver_aidl_interface/1/android/net/IDnsResolver.aidl
diff --git a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
similarity index 87%
rename from aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
rename to aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
index b808bae..5333f31 100644
--- a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
@@ -11,5 +11,5 @@
@utf8InCpp String[] domains;
@utf8InCpp String tlsName;
@utf8InCpp String[] tlsServers;
- @utf8InCpp String[] tlsFingerprints;
+ @utf8InCpp String[] tlsFingerprints = {};
}
diff --git a/aidl/dnsresolver/2/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/2/android/net/IDnsResolver.aidl
similarity index 100%
rename from aidl/dnsresolver/2/android/net/IDnsResolver.aidl
rename to aidl_api/dnsresolver_aidl_interface/2/android/net/IDnsResolver.aidl
diff --git a/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
similarity index 96%
rename from aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
rename to aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
index a37f938..5c81616 100644
--- a/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
@@ -28,5 +28,5 @@
@utf8InCpp String[] domains;
@utf8InCpp String tlsName;
@utf8InCpp String[] tlsServers;
- @utf8InCpp String[] tlsFingerprints;
+ @utf8InCpp String[] tlsFingerprints = {};
}
diff --git a/aidl_api/dnsresolver_aidl_interface/3/.hash b/aidl_api/dnsresolver_aidl_interface/3/.hash
new file mode 100644
index 0000000..c6a5015
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/3/.hash
@@ -0,0 +1 @@
+e3cbaaa75f8ec9ff0ee35c2fa9b928121aeb9956 -
diff --git a/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..7ab7003
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl
@@ -0,0 +1,33 @@
+package android.net;
+interface IDnsResolver {
+ boolean isAlive();
+ void registerEventListener(android.net.metrics.INetdEventListener listener);
+ void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams);
+ void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count);
+ void startPrefix64Discovery(int netId);
+ void stopPrefix64Discovery(int netId);
+ @utf8InCpp String getPrefix64(int netId);
+ void createNetworkCache(int netId);
+ void destroyNetworkCache(int netId);
+ void setLogSeverity(int logSeverity);
+ const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+ const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+ const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+ const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+ const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+ const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+ const int RESOLVER_PARAMS_COUNT = 6;
+ const int RESOLVER_STATS_SUCCESSES = 0;
+ const int RESOLVER_STATS_ERRORS = 1;
+ const int RESOLVER_STATS_TIMEOUTS = 2;
+ const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+ const int RESOLVER_STATS_RTT_AVG = 4;
+ const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+ const int RESOLVER_STATS_USABLE = 6;
+ const int RESOLVER_STATS_COUNT = 7;
+ const int DNS_RESOLVER_LOG_VERBOSE = 0;
+ const int DNS_RESOLVER_LOG_DEBUG = 1;
+ const int DNS_RESOLVER_LOG_INFO = 2;
+ const int DNS_RESOLVER_LOG_WARNING = 3;
+ const int DNS_RESOLVER_LOG_ERROR = 4;
+}
diff --git a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
similarity index 73%
copy from aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
copy to aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
index b808bae..ce31f1a 100644
--- a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
@@ -11,5 +11,7 @@
@utf8InCpp String[] domains;
@utf8InCpp String tlsName;
@utf8InCpp String[] tlsServers;
- @utf8InCpp String[] tlsFingerprints;
+ @utf8InCpp String[] tlsFingerprints = {};
+ @utf8InCpp String caCertificate = "";
+ int tlsConnectTimeoutMs = 0;
}
diff --git a/apex/Android.bp b/apex/Android.bp
index c1d33e1..3191b0a 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,6 +26,8 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: "AndroidManifest.xml",
+
+ legacy_android10_support: true,
}
apex_key {
diff --git a/apex/manifest.json b/apex/manifest.json
index 6739a4d..f3f5b02 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.resolv",
- "version": 290000000
+ "version": 299900000
}
diff --git a/binder/android/net/IDnsResolver.aidl b/binder/android/net/IDnsResolver.aidl
index f6f4092..c85e5cf 100644
--- a/binder/android/net/IDnsResolver.aidl
+++ b/binder/android/net/IDnsResolver.aidl
@@ -161,4 +161,25 @@
* POSIX errno.
*/
void setLogSeverity(int logSeverity);
+
+ /**
+ * Flush cache for the given network.
+ *
+ * @param netId the network ID of the network to flush.
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * POSIX errno.
+ */
+ void flushNetworkCache(int netId);
+
+ /**
+ * Values for {@code tcMode} of {@code ResolverExperimentalOptionsParcel}. It controls the way DNS
+ * resolver handles truncated DNS response from UDP connection.
+ *
+ * TC_MODE_DEFAULT: resolver retries on TCP-only on each name server.
+ * TC_MODE_UDP_TCP: resolver retries on TCP on the same server, falls back to UDP from next.
+ * TC_MODE_MAX: any value smaller than TC_MODE_DEFAULT or greater than TC_MODE_MAX is invalid.
+ */
+ const int TC_MODE_DEFAULT = 0;
+ const int TC_MODE_UDP_TCP = 1;
+ const int TC_MODE_MAX = 2;
}
diff --git a/binder/android/net/ResolverExperimentalOptionsParcel.aidl b/binder/android/net/ResolverExperimentalOptionsParcel.aidl
new file mode 100644
index 0000000..f8fd589
--- /dev/null
+++ b/binder/android/net/ResolverExperimentalOptionsParcel.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.net.ResolverHostsParcel;
+
+/**
+ * Knobs for OEM to control alternative behavior.
+ *
+ * {@hide}
+ */
+parcelable ResolverExperimentalOptionsParcel {
+ /**
+ * An IP/hostname mapping table for DNS local lookup customization.
+ * WARNING: this is intended for local testing and other special situations.
+ * Future versions of the DnsResolver module may break your assumptions.
+ * Injecting static mappings for public hostnames is generally A VERY BAD IDEA,
+ * since it makes it impossible for the domain owners to migrate the domain.
+ * It is also not an effective domain blocking mechanism, because apps can
+ * easily hardcode IPs or bypass the system DNS resolver.
+ */
+ ResolverHostsParcel[] hosts = {};
+
+ /**
+ * Truncated UDP DNS response handling mode. Handling of UDP responses with the TC (truncated)
+ * bit set. The values are defined in {@code IDnsResolver.aidl}
+ * 0: TC_MODE_DEFAULT
+ * 1: TC_MODE_UDP_TCP
+ * Other values are invalid.
+ */
+ int tcMode = 0;
+}
diff --git a/binder/android/net/ResolverHostsParcel.aidl b/binder/android/net/ResolverHostsParcel.aidl
new file mode 100644
index 0000000..6b372b1
--- /dev/null
+++ b/binder/android/net/ResolverHostsParcel.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+/**
+ * A mapping list which translates hostnames or domain names to IP addresses
+ * locally. Mapping multiple addresses to one hostname is supported.
+ * It's similar to /etc/hosts file.
+ *
+ * {@hide}
+ */
+parcelable ResolverHostsParcel {
+ /**
+ * The IPv4 or IPv6 address corresponding to |hostName| field.
+ */
+ @utf8InCpp String ipAddr;
+
+ /**
+ * The hostname is a FQDN.
+ */
+ @utf8InCpp String hostName = "";
+}
diff --git a/binder/android/net/ResolverParamsParcel.aidl b/binder/android/net/ResolverParamsParcel.aidl
index 94576b2..c42367f 100644
--- a/binder/android/net/ResolverParamsParcel.aidl
+++ b/binder/android/net/ResolverParamsParcel.aidl
@@ -16,6 +16,8 @@
package android.net;
+import android.net.ResolverExperimentalOptionsParcel;
+
/**
* Configuration for a resolver parameters.
*
@@ -81,18 +83,25 @@
* An array containing TLS public key fingerprints (pins) of which each server must match
* at least one, or empty if there are no pinned keys.
*/
- // DEPRECATED: no longer to use it
- @utf8InCpp String[] tlsFingerprints;
+ // DEPRECATED:Superseded by caCertificate below.
+ @utf8InCpp String[] tlsFingerprints = {};
/**
* Certificate authority that signed the certificate; only used by DNS-over-TLS tests.
*
*/
- @utf8InCpp String caCertificate;
+ @utf8InCpp String caCertificate = "";
/**
* The timeout for the connection attempt to a Private DNS server.
* It's initialized to 0 to use the predefined default value.
+ * Setting a non-zero value to it takes no effect anymore. The timeout is configurable only
+ * by the experiement flag.
*/
int tlsConnectTimeoutMs = 0;
+
+ /**
+ * Knobs for OEM to control alternative behavior.
+ */
+ ResolverExperimentalOptionsParcel experimentalOptions;
}
diff --git a/getaddrinfo.cpp b/getaddrinfo.cpp
index 4207251..3683920 100644
--- a/getaddrinfo.cpp
+++ b/getaddrinfo.cpp
@@ -56,6 +56,9 @@
#include <android-base/logging.h>
#include "netd_resolv/resolv.h"
+#include "res_comp.h"
+#include "res_debug.h"
+#include "res_init.h"
#include "resolv_cache.h"
#include "resolv_private.h"
@@ -107,20 +110,13 @@
};
#define PTON_MAX 16
-#define MAXPACKET (8 * 1024)
-
-typedef union {
- HEADER hdr;
- uint8_t buf[MAXPACKET];
-} querybuf;
struct res_target {
struct res_target* next;
- const char* name; /* domain name */
- int qclass, qtype; /* class and type of query */
- uint8_t* answer; /* buffer to put answer */
- int anslen; /* size of answer buffer */
- int n; /* result length */
+ const char* name; // domain name
+ int qclass, qtype; // class and type of query
+ std::vector<uint8_t> answer = std::vector<uint8_t>(MAXPACKET, 0); // buffer to put answer
+ int n = 0; // result length
};
static int str2number(const char*);
@@ -138,15 +134,17 @@
static const struct afd* find_afd(int);
static int ip6_str2scopeid(const char*, struct sockaddr_in6*, uint32_t*);
-static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*,
- int* herrno);
+static struct addrinfo* getanswer(const std::vector<uint8_t>&, int, const char*, int,
+ const struct addrinfo*, int* herrno);
static int dns_getaddrinfo(const char* name, const addrinfo* pai,
const android_net_context* netcontext, addrinfo** rv,
NetworkDnsEventReported* event);
static void _sethtent(FILE**);
static void _endhtent(FILE**);
static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*);
-static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res);
+static struct addrinfo* getCustomHosts(const size_t netid, const char*, const struct addrinfo*);
+static bool files_getaddrinfo(const size_t netid, const char* name, const addrinfo* pai,
+ addrinfo** res);
static int _find_src_addr(const struct sockaddr*, struct sockaddr*, unsigned, uid_t);
static int res_queryN(const char* name, res_target* target, res_state res, int* herrno);
@@ -265,6 +263,7 @@
.dns_netid = NETID_UNSET,
.dns_mark = MARK_UNSET,
.uid = NET_CONTEXT_INVALID_UID,
+ .pid = NET_CONTEXT_INVALID_PID,
};
NetworkDnsEventReported event;
return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result,
@@ -469,7 +468,7 @@
// If the servname does not match socktype/protocol, return error code.
if ((error = get_portmatch(pai, servname))) return error;
- if (!files_getaddrinfo(hostname, pai, &result)) {
+ if (!files_getaddrinfo(netcontext->dns_netid, hostname, pai, &result)) {
error = dns_getaddrinfo(hostname, pai, netcontext, &result, event);
}
if (error) {
@@ -836,8 +835,8 @@
} \
} while (0)
-static struct addrinfo* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
- const struct addrinfo* pai, int* herrno) {
+static struct addrinfo* getanswer(const std::vector<uint8_t>& answer, int anslen, const char* qname,
+ int qtype, const struct addrinfo* pai, int* herrno) {
struct addrinfo sentinel = {};
struct addrinfo *cur;
struct addrinfo ai;
@@ -851,17 +850,17 @@
int type, ancount, qdcount;
int haveanswer, had_error;
char tbuf[MAXDNAME];
- int (*name_ok)(const char*);
char hostbuf[8 * 1024];
- assert(answer != NULL);
assert(qname != NULL);
assert(pai != NULL);
cur = &sentinel;
canonname = NULL;
- eom = answer->buf + anslen;
+ eom = answer.data() + anslen;
+
+ bool (*name_ok)(const char* dn);
switch (qtype) {
case T_A:
case T_AAAA:
@@ -874,18 +873,18 @@
/*
* find first satisfactory answer
*/
- hp = &answer->hdr;
+ hp = reinterpret_cast<const HEADER*>(answer.data());
ancount = ntohs(hp->ancount);
qdcount = ntohs(hp->qdcount);
bp = hostbuf;
ep = hostbuf + sizeof hostbuf;
- cp = answer->buf;
+ cp = answer.data();
BOUNDED_INCR(HFIXEDSZ);
if (qdcount != 1) {
*herrno = NO_RECOVERY;
return (NULL);
}
- n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+ n = dn_expand(answer.data(), eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
*herrno = NO_RECOVERY;
return (NULL);
@@ -909,7 +908,7 @@
haveanswer = 0;
had_error = 0;
while (ancount-- > 0 && cp < eom && !had_error) {
- n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+ n = dn_expand(answer.data(), eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
had_error++;
continue;
@@ -929,7 +928,7 @@
continue; /* XXX - had_error++ ? */
}
if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && type == T_CNAME) {
- n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+ n = dn_expand(answer.data(), eom, cp, tbuf, sizeof tbuf);
if ((n < 0) || !(*name_ok)(tbuf)) {
had_error++;
continue;
@@ -1395,16 +1394,11 @@
res_target q = {};
res_target q2 = {};
- auto buf = std::make_unique<querybuf>();
- auto buf2 = std::make_unique<querybuf>();
-
switch (pai->ai_family) {
case AF_UNSPEC: {
/* prefer IPv6 */
q.name = name;
q.qclass = C_IN;
- q.answer = buf->buf;
- q.anslen = sizeof(buf->buf);
int query_ipv6 = 1, query_ipv4 = 1;
if (pai->ai_flags & AI_ADDRCONFIG) {
query_ipv6 = have_ipv6(netcontext->app_mark, netcontext->uid);
@@ -1417,8 +1411,6 @@
q2.name = name;
q2.qclass = C_IN;
q2.qtype = T_A;
- q2.answer = buf2->buf;
- q2.anslen = sizeof(buf2->buf);
}
} else if (query_ipv4) {
q.qtype = T_A;
@@ -1431,32 +1423,21 @@
q.name = name;
q.qclass = C_IN;
q.qtype = T_A;
- q.answer = buf->buf;
- q.anslen = sizeof(buf->buf);
break;
case AF_INET6:
q.name = name;
q.qclass = C_IN;
q.qtype = T_AAAA;
- q.answer = buf->buf;
- q.anslen = sizeof(buf->buf);
break;
default:
return EAI_FAMILY;
}
- res_state res = res_get_state();
- if (!res) return EAI_MEMORY;
-
- /* this just sets our netid val in the thread private data so we don't have to
- * modify the api's all the way down to res_send.c's res_nsend. We could
- * fully populate the thread private data here, but if we get down there
- * and have a cache hit that would be wasted, so we do the rest there on miss
- */
- res_setnetcontext(res, netcontext, event);
+ ResState res;
+ res_init(&res, netcontext, event);
int he;
- if (res_searchN(name, &q, res, &he) < 0) {
+ if (res_searchN(name, &q, &res, &he) < 0) {
// Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.
// Note that res_searchN() doesn't set the pair NETDB_INTERNAL and errno.
// See also herrnoToAiErrno().
@@ -1465,13 +1446,13 @@
addrinfo sentinel = {};
addrinfo* cur = &sentinel;
- addrinfo* ai = getanswer(buf.get(), q.n, q.name, q.qtype, pai, &he);
+ addrinfo* ai = getanswer(q.answer, q.n, q.name, q.qtype, pai, &he);
if (ai) {
cur->ai_next = ai;
while (cur && cur->ai_next) cur = cur->ai_next;
}
if (q.next) {
- ai = getanswer(buf2.get(), q2.n, q2.name, q2.qtype, pai, &he);
+ ai = getanswer(q2.answer, q2.n, q2.name, q2.qtype, pai, &he);
if (ai) cur->ai_next = ai;
}
if (sentinel.ai_next == NULL) {
@@ -1551,22 +1532,43 @@
return res0;
}
-static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res) {
+static struct addrinfo* getCustomHosts(const size_t netid, const char* _Nonnull name,
+ const struct addrinfo* _Nonnull pai) {
+ struct addrinfo sentinel = {};
+ struct addrinfo *res0, *res;
+ res = &sentinel;
+ std::vector<std::string> hosts = getCustomizedTableByName(netid, name);
+ for (const std::string& host : hosts) {
+ int error = getaddrinfo_numeric(host.c_str(), nullptr, *pai, &res0);
+ if (!error && res0 != nullptr) {
+ res->ai_next = res0;
+ res = res0;
+ res0 = nullptr;
+ }
+ }
+ return sentinel.ai_next;
+}
+
+static bool files_getaddrinfo(const size_t netid, const char* name, const addrinfo* pai,
+ addrinfo** res) {
struct addrinfo sentinel = {};
struct addrinfo *p, *cur;
- FILE* hostf = NULL;
+ FILE* hostf = nullptr;
cur = &sentinel;
-
_sethtent(&hostf);
- while ((p = _gethtent(&hostf, name, pai)) != NULL) {
+ while ((p = _gethtent(&hostf, name, pai)) != nullptr) {
cur->ai_next = p;
while (cur && cur->ai_next) cur = cur->ai_next;
}
_endhtent(&hostf);
+ if ((p = getCustomHosts(netid, name, pai)) != nullptr) {
+ cur->ai_next = p;
+ }
+
*res = sentinel.ai_next;
- return sentinel.ai_next != NULL;
+ return sentinel.ai_next != nullptr;
}
/* resolver logic */
@@ -1583,7 +1585,6 @@
*/
static int res_queryN(const char* name, res_target* target, res_state res, int* herrno) {
uint8_t buf[MAXPACKET];
- HEADER* hp;
int n;
struct res_target* t;
int rcode;
@@ -1596,10 +1597,7 @@
ancount = 0;
for (t = target; t; t = t->next) {
- uint8_t* answer;
- int anslen;
-
- hp = (HEADER*) (void*) t->answer;
+ HEADER* hp = (HEADER*)(void*)t->answer.data();
bool retried = false;
again:
hp->rcode = NOERROR; /* default */
@@ -1607,12 +1605,12 @@
/* make it easier... */
int cl = t->qclass;
int type = t->qtype;
- answer = t->answer;
- anslen = t->anslen;
+ const int anslen = t->answer.size();
LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
- n = res_nmkquery(res, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+ n = res_nmkquery(QUERY, name, cl, type, /*data=*/nullptr, /*datalen=*/0, buf, sizeof(buf),
+ res->netcontext_flags);
if (n > 0 &&
(res->netcontext_flags &
(NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS)) &&
@@ -1624,7 +1622,7 @@
return n;
}
- n = res_nsend(res, buf, n, answer, anslen, &rcode, 0);
+ n = res_nsend(res, buf, n, t->answer.data(), anslen, &rcode, 0);
if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
// Record rcode from DNS response header only if no timeout.
// Keep rcode timeout for reporting later if any.
@@ -1693,7 +1691,7 @@
assert(name != NULL);
assert(target != NULL);
- hp = (HEADER*) (void*) target->answer; /*XXX*/
+ hp = (HEADER*)(void*)target->answer.data();
errno = 0;
*herrno = HOST_NOT_FOUND; /* default, if we never query */
@@ -1725,7 +1723,7 @@
* the domain stuff is tried. Will have a better
* fix after thread pools are used.
*/
- _resolv_populate_res_for_net(res);
+ resolv_populate_res_for_net(res);
for (const auto& domain : res->search_domains) {
ret = res_querydomainN(name, domain.c_str(), target, res, herrno);
diff --git a/gethnamaddr.cpp b/gethnamaddr.cpp
index 0d9683c..23f94bb 100644
--- a/gethnamaddr.cpp
+++ b/gethnamaddr.cpp
@@ -74,8 +74,10 @@
#include "hostent.h"
#include "netd_resolv/resolv.h"
+#include "res_comp.h"
+#include "res_debug.h" // p_class(), p_type()
+#include "res_init.h"
#include "resolv_cache.h"
-#include "resolv_private.h"
#include "stats.pb.h"
using android::net::NetworkDnsEventReported;
@@ -90,12 +92,6 @@
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
-#define maybe_ok(res, nm, ok) ((ok)(nm) != 0)
-#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
-#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok)
-
-#define MAXPACKET (8 * 1024)
-
constexpr int MAXADDRS = 35;
typedef union {
@@ -116,7 +112,7 @@
static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
const android_net_context* netcontext, getnamaddr* info,
NetworkDnsEventReported* event);
-static int dns_gethtbyname(const char* name, int af, getnamaddr* info);
+static int dns_gethtbyname(ResState* res, const char* name, int af, getnamaddr* info);
#define BOUNDED_INCR(x) \
do { \
@@ -143,7 +139,6 @@
char tbuf[MAXDNAME];
char* addr_ptrs[MAXADDRS];
const char* tname;
- int (*name_ok)(const char*);
std::vector<char*> aliases;
_DIAGASSERT(answer != NULL);
@@ -152,6 +147,8 @@
tname = qname;
hent->h_name = NULL;
eom = answer->buf + anslen;
+
+ bool (*name_ok)(const char* dn);
switch (qtype) {
case T_A:
case T_AAAA:
@@ -178,7 +175,7 @@
if (qdcount != 1) goto no_recovery;
n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
- if ((n < 0) || !maybe_ok(res, bp, name_ok)) goto no_recovery;
+ if ((n < 0) || !name_ok(bp)) goto no_recovery;
BOUNDED_INCR(n + QFIXEDSZ);
if (qtype == T_A || qtype == T_AAAA) {
@@ -199,7 +196,7 @@
had_error = 0;
while (ancount-- > 0 && cp < eom && !had_error) {
n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
- if ((n < 0) || !maybe_ok(res, bp, name_ok)) {
+ if ((n < 0) || !name_ok(bp)) {
had_error++;
continue;
}
@@ -220,7 +217,7 @@
}
if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
- if ((n < 0) || !maybe_ok(res, tbuf, name_ok)) {
+ if ((n < 0) || !name_ok(tbuf)) {
had_error++;
continue;
}
@@ -247,7 +244,7 @@
}
if (qtype == T_PTR && type == T_CNAME) {
n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
- if (n < 0 || !maybe_dnok(res, tbuf)) {
+ if (n < 0 || !res_dnok(tbuf)) {
had_error++;
continue;
}
@@ -280,7 +277,7 @@
continue; /* XXX - had_error++ ? */
}
n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
- if ((n < 0) || !maybe_hnok(res, bp)) {
+ if ((n < 0) || !res_hnok(bp)) {
had_error++;
break;
}
@@ -393,9 +390,8 @@
NetworkDnsEventReported* event) {
getnamaddr info;
- res_state res = res_get_state();
- if (res == nullptr) return EAI_MEMORY;
- res_setnetcontext(res, netcontext, event);
+ ResState res;
+ res_init(&res, netcontext, event);
size_t size;
switch (af) {
@@ -450,7 +446,7 @@
info.buf = buf;
info.buflen = buflen;
if (_hf_gethtbyname2(name, af, &info)) {
- int error = dns_gethtbyname(name, af, &info);
+ int error = dns_gethtbyname(&res, name, af, &info);
if (error != 0) return error;
}
*result = hp;
@@ -652,7 +648,7 @@
});
}
-static int dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
+static int dns_gethtbyname(ResState* res, const char* name, int addr_type, getnamaddr* info) {
int n, type;
info->hp->h_addrtype = addr_type;
@@ -670,9 +666,6 @@
}
auto buf = std::make_unique<querybuf>();
- res_state res = res_get_state();
- if (!res) return EAI_MEMORY;
-
int he;
n = res_nsearch(res, name, C_IN, type, buf->buf, (int)sizeof(buf->buf), &he);
if (n < 0) {
@@ -734,12 +727,10 @@
auto buf = std::make_unique<querybuf>();
- res_state res = res_get_state();
- if (!res) return EAI_MEMORY;
-
- res_setnetcontext(res, netcontext, event);
+ ResState res;
+ res_init(&res, netcontext, event);
int he;
- n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
+ n = res_nquery(&res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
if (n < 0) {
LOG(DEBUG) << __func__ << ": res_nquery failed (" << n << ")";
// Note that res_nquery() doesn't set the pair NETDB_INTERNAL and errno.
diff --git a/include/netd_resolv/resolv.h b/include/netd_resolv/resolv.h
index afd63f5..596a022 100644
--- a/include/netd_resolv/resolv.h
+++ b/include/netd_resolv/resolv.h
@@ -28,8 +28,7 @@
#pragma once
-#include "params.h"
-
+#include <arpa/nameser.h>
#include <netinet/in.h>
/*
@@ -43,6 +42,9 @@
*/
#define MARK_UNSET 0u
+#define NET_CONTEXT_INVALID_UID ((uid_t)-1)
+#define NET_CONTEXT_INVALID_PID ((pid_t)-1)
+
/*
* A struct to capture context relevant to network operations.
*
@@ -59,11 +61,12 @@
unsigned app_mark;
unsigned dns_netid;
unsigned dns_mark;
- uid_t uid;
+ uid_t uid = NET_CONTEXT_INVALID_UID;
unsigned flags;
+ // Variable to store the pid of the application sending DNS query.
+ pid_t pid = NET_CONTEXT_INVALID_PID;
};
-#define NET_CONTEXT_INVALID_UID ((uid_t) -1)
#define NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS 0x00000001
#define NET_CONTEXT_FLAG_USE_EDNS 0x00000002
#define NET_CONTEXT_FLAG_USE_DNS_OVER_TLS 0x00000004
@@ -74,7 +77,30 @@
typedef void (*get_network_context_callback)(unsigned netid, uid_t uid,
android_net_context* netcontext);
typedef void (*log_callback)(const char* msg);
-typedef int (*tagSocketCallback)(int sockFd, uint32_t tag, uid_t uid);
+typedef int (*tagSocketCallback)(int sockFd, uint32_t tag, uid_t uid, pid_t pid);
+
+// The DnsResolver module invokes this callback once before starting each DNS
+// lookup. The callback receives the android_net_context associated with the
+// request, and the (possibly unqualified) hostname requested by the app via
+// getaddrinfo() or gethostbyname().
+//
+// If the callback returns false, the DnsResolver will abort the request
+// returning EAI_SYSTEM. If the callback returns true, the query will proceed as
+// usual.
+//
+// If this callback is not present (i.e. set to nullptr), the effect is the same
+// of returning true.
+//
+// This callback *will* be invoked concurrently from multiple threads. It must
+// peform its own locking when accessing shared data structures. Furthermore,
+// the callback must not sleep nor perform RPC requests.
+//
+// Be mindful that hostnames could contain sensitive user data. Do not log them
+// and do not transmit them to third parties without explicit user
+// authorization.
+//
+typedef bool (*evaluate_domain_name_callback)(
+ const android_net_context &netcontext, const char *host);
/*
* Some functions needed by the resolver (e.g. checkCallingPermission()) live in
@@ -87,11 +113,21 @@
get_network_context_callback get_network_context;
log_callback log;
tagSocketCallback tagSocket;
+ evaluate_domain_name_callback evaluate_domain_name;
};
#define TAG_SYSTEM_DNS 0xFFFFFF82
+#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
+
LIBNETD_RESOLV_PUBLIC bool resolv_has_nameservers(unsigned netid);
// Set callbacks and bring DnsResolver up.
LIBNETD_RESOLV_PUBLIC bool resolv_init(const ResolverNetdCallbacks* callbacks);
+
+// Function that performs RDNS in local cache. The |domain_name_size| is the size of domain_name
+// buffer, which is recommended to NS_MAXDNAME. Function return false if hostname not found or
+// domain_name_size > NS_MAXDNAME.
+LIBNETD_RESOLV_PUBLIC bool resolv_gethostbyaddr_from_cache(unsigned netId, char domain_name[],
+ size_t domain_name_size,
+ const char* ip_address, int af);
diff --git a/include/netd_resolv/resolv_stub.h b/include/netd_resolv/resolv_stub.h
deleted file mode 100644
index 41f56fa..0000000
--- a/include/netd_resolv/resolv_stub.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef NETD_RESOLV_RESOLV_STUB_H
-#define NETD_RESOLV_RESOLV_STUB_H
-
-#include "resolv.h"
-#include "stats.h"
-
-// Struct containing function pointers for every function exported by libnetd_resolv.
-extern struct ResolvStub {
- bool (*resolv_has_nameservers)(unsigned netid);
-
- bool (*resolv_init)(const ResolverNetdCallbacks& callbacks);
-} RESOLV_STUB;
-
-int resolv_stub_init();
-
-#endif // NETD_RESOLV_RESOLV_STUB_H
diff --git a/include/netd_resolv/stats.h b/include/netd_resolv/stats.h
deleted file mode 100644
index 7bfe845..0000000
--- a/include/netd_resolv/stats.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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 NETD_RES_STATS_H
-#define NETD_RES_STATS_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <time.h>
-
-#include "params.h"
-
-#define RCODE_INTERNAL_ERROR 254
-#define RCODE_TIMEOUT 255
-
-struct res_sample {
- time_t at; // time in s at which the sample was recorded
- uint16_t rtt; // round-trip time in ms
- uint8_t rcode; // the DNS rcode or RCODE_XXX defined above
-};
-
-// Resolver reachability statistics and run-time parameters.
-struct res_stats {
- // Stats of the last <sample_count> queries.
- res_sample samples[MAXNSSAMPLES];
- // The number of samples stored.
- uint8_t sample_count;
- // The next sample to modify.
- uint8_t sample_next;
-};
-
-// Aggregates the reachability statistics for the given server based on on the stored samples.
-LIBNETD_RESOLV_PUBLIC void android_net_res_stats_aggregate(res_stats* stats, int* successes,
- int* errors, int* timeouts,
- int* internal_errors, int* rtt_avg,
- time_t* last_sample_time);
-
-LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_info_for_net(
- unsigned netid, int* nscount, sockaddr_storage servers[MAXNS], int* dcount,
- char domains[MAXDNSRCH][MAXDNSRCHPATH], res_params* params, res_stats stats[MAXNS],
- int* wait_for_pending_req_timeout_count);
-
-// Returns an array of bools indicating which servers are considered good
-LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_usable_servers(const res_params* params,
- res_stats stats[], int nscount,
- bool valid_servers[]);
-
-#endif // NETD_RES_STATS_H
diff --git a/libnetd_resolv.map.txt b/libnetd_resolv.map.txt
index be193db..06e67aa 100644
--- a/libnetd_resolv.map.txt
+++ b/libnetd_resolv.map.txt
@@ -22,6 +22,7 @@
global:
resolv_has_nameservers;
resolv_init;
+ resolv_gethostbyaddr_from_cache;
local:
*;
};
diff --git a/include/netd_resolv/params.h b/params.h
similarity index 89%
rename from include/netd_resolv/params.h
rename to params.h
index 93e6287..82cd058 100644
--- a/include/netd_resolv/params.h
+++ b/params.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef NETD_RESOLV_PARAMS_H
-#define NETD_RESOLV_PARAMS_H
+#pragma once
#include <stdint.h>
@@ -34,7 +33,3 @@
int base_timeout_msec; // base query retry timeout (if 0, use RES_TIMEOUT)
int retry_count; // number of retries
};
-
-#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
-
-#endif // NETD_RESOLV_PARAMS_H
diff --git a/res_cache.cpp b/res_cache.cpp
index 4b5ba28..5f87cca 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -58,10 +58,20 @@
#include <server_configurable_flags/get_flags.h>
+#include "DnsStats.h"
+#include "res_comp.h"
#include "res_debug.h"
#include "resolv_private.h"
+#include "util.h"
using android::base::StringAppendF;
+using android::net::DnsQueryEvent;
+using android::net::DnsStats;
+using android::net::PROTO_DOT;
+using android::net::PROTO_TCP;
+using android::net::PROTO_UDP;
+using android::netdutils::DumpWriter;
+using android::netdutils::IPSockAddr;
/* This code implements a small and *simple* DNS resolver cache.
*
@@ -140,6 +150,7 @@
* *****************************************
*/
const int CONFIG_MAX_ENTRIES = 64 * 2 * 5;
+constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
static time_t _time_now(void) {
struct timeval tv;
@@ -425,6 +436,7 @@
while (numBytes > 0 && p < end) {
hash = hash * FNV_MULT ^ *p++;
+ numBytes--;
}
packet->cursor = p;
return hash;
@@ -862,10 +874,53 @@
/* Maximum time for a thread to wait for an pending request */
constexpr int PENDING_REQUEST_TIMEOUT = 20;
-// lock protecting everything in the resolve_cache_info structs (next ptr, etc)
+// lock protecting everything in NetConfig.
static std::mutex cache_mutex;
static std::condition_variable cv;
+namespace {
+
+// Map format: ReturnCode:rate_denom
+// if the ReturnCode is not associated with any rate_denom, use default
+// Sampling rate varies by return code; events to log are chosen randomly, with a
+// probability proportional to the sampling rate.
+constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
+
+std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
+ using android::base::ParseInt;
+ using android::base::ParseUint;
+ using android::base::Split;
+ using server_configurable_flags::GetServerConfigurableFlag;
+ std::unordered_map<int, uint32_t> sampling_rate_map{};
+ std::vector<std::string> subsampling_vector =
+ Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
+ DEFAULT_SUBSAMPLING_MAP),
+ " ");
+ for (const auto& pair : subsampling_vector) {
+ std::vector<std::string> rate_denom = Split(pair, ":");
+ int return_code;
+ uint32_t denom;
+ if (rate_denom.size() != 2) {
+ LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
+ continue;
+ }
+ if (rate_denom[0] == "default") {
+ return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
+ } else if (!ParseInt(rate_denom[0], &return_code)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ if (!ParseUint(rate_denom[1], &denom)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ sampling_rate_map[return_code] = denom;
+ }
+ return sampling_rate_map;
+}
+
+} // namespace
+
// Note that Cache is not thread-safe per se, access to its members must be protected
// by an external mutex.
//
@@ -923,21 +978,29 @@
} pending_requests{};
};
-// TODO: allocate with new
-struct resolv_cache_info {
- unsigned netid;
- Cache* cache; // TODO: use unique_ptr or embed
- struct resolv_cache_info* next;
- int nscount;
+struct NetConfig {
+ explicit NetConfig(unsigned netId) : netid(netId) {
+ cache = std::make_unique<Cache>();
+ dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
+ }
+
+ const unsigned netid;
+ std::unique_ptr<Cache> cache;
+ int nscount = 0;
std::vector<std::string> nameservers;
- struct addrinfo* nsaddrinfo[MAXNS]; // TODO: Use struct sockaddr_storage.
- int revision_id; // # times the nameservers have been replaced
- res_params params;
- struct res_stats nsstats[MAXNS];
+ std::vector<IPSockAddr> nameserverSockAddrs;
+ int revision_id = 0; // # times the nameservers have been replaced
+ res_params params{};
+ res_stats nsstats[MAXNS]{};
std::vector<std::string> search_domains;
- int wait_for_pending_req_timeout_count;
+ int wait_for_pending_req_timeout_count = 0;
// Map format: ReturnCode:rate_denom
std::unordered_map<int, uint32_t> dns_event_subsampling_map;
+ DnsStats dnsStats;
+ // Customized hostname/address table will be stored in customizedTable.
+ // If resolverParams.hosts is empty, the existing customized table will be erased.
+ HostMapping customizedTable = {};
+ int tc_mode = aidl::android::net::IDnsResolver::TC_MODE_DEFAULT;
};
/* gets cache associated with a network, or NULL if none exists */
@@ -1115,8 +1178,8 @@
}
}
-// gets a resolv_cache_info associated with a network, or NULL if not found
-static resolv_cache_info* find_cache_info_locked(unsigned netid) REQUIRES(cache_mutex);
+// Get a NetConfig associated with a network, or nullptr if not found.
+static NetConfig* find_netconfig_locked(unsigned netid) REQUIRES(cache_mutex);
ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int querylen, void* answer,
int answersize, int* answerlen, uint32_t flags) {
@@ -1178,7 +1241,7 @@
return RESOLV_CACHE_NOTFOUND;
}
if (ret == false) {
- resolv_cache_info* info = find_cache_info_locked(netid);
+ NetConfig* info = find_netconfig_locked(netid);
if (info != NULL) {
info->wait_for_pending_req_timeout_count++;
}
@@ -1282,164 +1345,167 @@
return 0;
}
-// Head of the list of caches.
-static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex);
+bool resolv_gethostbyaddr_from_cache(unsigned netid, char domain_name[], size_t domain_name_size,
+ const char* ip_address, int af) {
+ if (domain_name_size > NS_MAXDNAME) {
+ LOG(WARNING) << __func__ << ": invalid domain_name_size " << domain_name_size;
+ return false;
+ } else if (ip_address == nullptr || ip_address[0] == '\0') {
+ LOG(WARNING) << __func__ << ": invalid ip_address";
+ return false;
+ } else if (af != AF_INET && af != AF_INET6) {
+ LOG(WARNING) << __func__ << ": unsupported AF";
+ return false;
+ }
-// insert resolv_cache_info into the list of resolv_cache_infos
-static void insert_cache_info_locked(resolv_cache_info* cache_info);
-// creates a resolv_cache_info
-static resolv_cache_info* create_cache_info();
-// Clears nameservers set for |cache_info| and clears the stats
-static void free_nameservers_locked(resolv_cache_info* cache_info);
+ Cache* cache = nullptr;
+ Entry* node = nullptr;
+
+ ns_rr rr;
+ ns_msg handle;
+ ns_rr rr_query;
+
+ struct sockaddr_in sa;
+ struct sockaddr_in6 sa6;
+ char* addr_buf = nullptr;
+
+ std::lock_guard guard(cache_mutex);
+
+ cache = find_named_cache_locked(netid);
+ if (cache == nullptr) {
+ return false;
+ }
+
+ for (node = cache->mru_list.mru_next; node != nullptr && node != &cache->mru_list;
+ node = node->mru_next) {
+ if (node->answer == nullptr) {
+ continue;
+ }
+
+ memset(&handle, 0, sizeof(handle));
+
+ if (ns_initparse(node->answer, node->answerlen, &handle) < 0) {
+ continue;
+ }
+
+ for (int n = 0; n < ns_msg_count(handle, ns_s_an); n++) {
+ memset(&rr, 0, sizeof(rr));
+
+ if (ns_parserr(&handle, ns_s_an, n, &rr)) {
+ continue;
+ }
+
+ if (ns_rr_type(rr) == ns_t_a && af == AF_INET) {
+ addr_buf = (char*)&(sa.sin_addr);
+ } else if (ns_rr_type(rr) == ns_t_aaaa && af == AF_INET6) {
+ addr_buf = (char*)&(sa6.sin6_addr);
+ } else {
+ continue;
+ }
+
+ if (inet_pton(af, ip_address, addr_buf) != 1) {
+ LOG(WARNING) << __func__ << ": inet_pton() fail";
+ return false;
+ }
+
+ if (memcmp(ns_rr_rdata(rr), addr_buf, ns_rr_rdlen(rr)) == 0) {
+ int query_count = ns_msg_count(handle, ns_s_qd);
+ for (int i = 0; i < query_count; i++) {
+ memset(&rr_query, 0, sizeof(rr_query));
+ if (ns_parserr(&handle, ns_s_qd, i, &rr_query)) {
+ continue;
+ }
+ strlcpy(domain_name, ns_rr_name(rr_query), domain_name_size);
+ if (domain_name[0] != '\0') {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static std::unordered_map<unsigned, std::unique_ptr<NetConfig>> sNetConfigMap
+ GUARDED_BY(cache_mutex);
+
+// Clears nameservers set for |netconfig| and clears the stats
+static void free_nameservers_locked(NetConfig* netconfig);
// Order-insensitive comparison for the two set of servers.
static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServers,
const std::vector<std::string>& newServers);
-// clears the stats samples contained withing the given cache_info
-static void res_cache_clear_stats_locked(resolv_cache_info* cache_info);
+// clears the stats samples contained withing the given netconfig.
+static void res_cache_clear_stats_locked(NetConfig* netconfig);
// public API for netd to query if name server is set on specific netid
bool resolv_has_nameservers(unsigned netid) {
std::lock_guard guard(cache_mutex);
- resolv_cache_info* info = find_cache_info_locked(netid);
+ NetConfig* info = find_netconfig_locked(netid);
return (info != nullptr) && (info->nscount > 0);
}
-namespace {
-
-// Map format: ReturnCode:rate_denom
-// if the ReturnCode is not associated with any rate_denom, use default
-// Sampling rate varies by return code; events to log are chosen randomly, with a
-// probability proportional to the sampling rate.
-constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
-
-std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
- using android::base::ParseInt;
- using android::base::ParseUint;
- using android::base::Split;
- using server_configurable_flags::GetServerConfigurableFlag;
- std::unordered_map<int, uint32_t> sampling_rate_map{};
- std::vector<std::string> subsampling_vector =
- Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
- DEFAULT_SUBSAMPLING_MAP),
- " ");
- for (const auto& pair : subsampling_vector) {
- std::vector<std::string> rate_denom = Split(pair, ":");
- int return_code;
- uint32_t denom;
- if (rate_denom.size() != 2) {
- LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
- continue;
- }
- if (rate_denom[0] == "default") {
- return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
- } else if (!ParseInt(rate_denom[0], &return_code)) {
- LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
- continue;
- }
- if (!ParseUint(rate_denom[1], &denom)) {
- LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
- continue;
- }
- sampling_rate_map[return_code] = denom;
- }
- return sampling_rate_map;
-}
-
-} // namespace
-
int resolv_create_cache_for_net(unsigned netid) {
std::lock_guard guard(cache_mutex);
- Cache* cache = find_named_cache_locked(netid);
- // Should not happen
- if (cache) {
+ if (sNetConfigMap.find(netid) != sNetConfigMap.end()) {
LOG(ERROR) << __func__ << ": Cache is already created, netId: " << netid;
return -EEXIST;
}
- resolv_cache_info* cache_info = create_cache_info();
- if (!cache_info) return -ENOMEM;
- cache_info->netid = netid;
- cache_info->cache = new Cache;
- cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
- insert_cache_info_locked(cache_info);
-
+ sNetConfigMap[netid] = std::make_unique<NetConfig>(netid);
return 0;
}
void resolv_delete_cache_for_net(unsigned netid) {
std::lock_guard guard(cache_mutex);
+ sNetConfigMap.erase(netid);
+}
- struct resolv_cache_info* prev_cache_info = &res_cache_list;
+int resolv_flush_cache_for_net(unsigned netid) {
+ std::lock_guard guard(cache_mutex);
- while (prev_cache_info->next) {
- struct resolv_cache_info* cache_info = prev_cache_info->next;
-
- if (cache_info->netid == netid) {
- prev_cache_info->next = cache_info->next;
- delete cache_info->cache;
- free_nameservers_locked(cache_info);
- free(cache_info);
- break;
- }
-
- prev_cache_info = prev_cache_info->next;
+ NetConfig* netconfig = find_netconfig_locked(netid);
+ if (netconfig == nullptr) {
+ return -ENONET;
}
+ netconfig->cache->flush();
+
+ // Also clear the NS statistics.
+ res_cache_clear_stats_locked(netconfig);
+ return 0;
}
std::vector<unsigned> resolv_list_caches() {
std::lock_guard guard(cache_mutex);
- struct resolv_cache_info* cache_info = res_cache_list.next;
std::vector<unsigned> result;
- while (cache_info) {
- result.push_back(cache_info->netid);
- cache_info = cache_info->next;
+ result.reserve(sNetConfigMap.size());
+ for (const auto& [netId, _] : sNetConfigMap) {
+ result.push_back(netId);
}
return result;
}
-static resolv_cache_info* create_cache_info() {
- return (struct resolv_cache_info*) calloc(sizeof(struct resolv_cache_info), 1);
-}
-
-// TODO: convert this to a simple and efficient C++ container.
-static void insert_cache_info_locked(struct resolv_cache_info* cache_info) {
- struct resolv_cache_info* last;
- for (last = &res_cache_list; last->next; last = last->next) {}
- last->next = cache_info;
-}
-
static Cache* find_named_cache_locked(unsigned netid) {
- resolv_cache_info* info = find_cache_info_locked(netid);
- if (info != nullptr) return info->cache;
+ NetConfig* info = find_netconfig_locked(netid);
+ if (info != nullptr) return info->cache.get();
return nullptr;
}
-static resolv_cache_info* find_cache_info_locked(unsigned netid) {
- struct resolv_cache_info* cache_info = res_cache_list.next;
-
- while (cache_info) {
- if (cache_info->netid == netid) {
- break;
- }
-
- cache_info = cache_info->next;
+static NetConfig* find_netconfig_locked(unsigned netid) {
+ if (auto it = sNetConfigMap.find(netid); it != sNetConfigMap.end()) {
+ return it->second.get();
}
- return cache_info;
+ return nullptr;
}
static void resolv_set_experiment_params(res_params* params) {
- using android::base::ParseInt;
- using server_configurable_flags::GetServerConfigurableFlag;
-
if (params->retry_count == 0) {
- params->retry_count = RES_DFLRETRY;
- ParseInt(GetServerConfigurableFlag("netd_native", "retry_count", ""), ¶ms->retry_count);
+ params->retry_count = getExperimentFlagInt("retry_count", RES_DFLRETRY);
}
if (params->base_timeout_msec == 0) {
- params->base_timeout_msec = RES_TIMEOUT;
- ParseInt(GetServerConfigurableFlag("netd_native", "retransmission_time_interval", ""),
- ¶ms->base_timeout_msec);
+ params->base_timeout_msec =
+ getExperimentFlagInt("retransmission_time_interval", RES_TIMEOUT);
}
}
@@ -1471,10 +1537,41 @@
return res;
}
+bool isValidServer(const std::string& server) {
+ const addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ addrinfo* result = nullptr;
+ if (int err = getaddrinfo_numeric(server.c_str(), "53", hints, &result); err != 0) {
+ LOG(WARNING) << __func__ << ": getaddrinfo_numeric(" << server
+ << ") = " << gai_strerror(err);
+ return false;
+ }
+ freeaddrinfo(result);
+ return true;
+}
+
} // namespace
-int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
- const std::vector<std::string>& domains, const res_params& params) {
+std::vector<std::string> getCustomizedTableByName(const size_t netid, const char* hostname) {
+ std::lock_guard guard(cache_mutex);
+ NetConfig* netconfig = find_netconfig_locked(netid);
+
+ std::vector<std::string> result;
+ if (netconfig != nullptr) {
+ const auto& hosts = netconfig->customizedTable.equal_range(hostname);
+ for (auto i = hosts.first; i != hosts.second; ++i) {
+ result.push_back(i->second);
+ }
+ }
+ return result;
+}
+
+int resolv_set_nameservers(
+ unsigned netid, const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const res_params& params,
+ const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions) {
std::vector<std::string> nameservers = filter_nameservers(servers);
const int numservers = static_cast<int>(nameservers.size());
@@ -1482,61 +1579,64 @@
// Parse the addresses before actually locking or changing any state, in case there is an error.
// As a side effect this also reduces the time the lock is kept.
- // TODO: find a better way to replace addrinfo*, something like std::vector<SafeAddrinfo>
- addrinfo* nsaddrinfo[MAXNS];
- for (int i = 0; i < numservers; i++) {
- // The addrinfo structures allocated here are freed in free_nameservers_locked().
- const addrinfo hints = {
- .ai_flags = AI_NUMERICHOST,
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_DGRAM,
- };
- const int rt = getaddrinfo_numeric(nameservers[i].c_str(), "53", hints, &nsaddrinfo[i]);
- if (rt != 0) {
- for (int j = 0; j < i; j++) {
- freeaddrinfo(nsaddrinfo[j]);
- }
- LOG(INFO) << __func__ << ": getaddrinfo_numeric(" << nameservers[i]
- << ") = " << gai_strerror(rt);
- return -EINVAL;
- }
+ std::vector<IPSockAddr> ipSockAddrs;
+ ipSockAddrs.reserve(nameservers.size());
+ for (const auto& server : nameservers) {
+ if (!isValidServer(server)) return -EINVAL;
+ ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53));
}
std::lock_guard guard(cache_mutex);
- resolv_cache_info* cache_info = find_cache_info_locked(netid);
+ NetConfig* netconfig = find_netconfig_locked(netid);
- if (cache_info == nullptr) return -ENONET;
+ if (netconfig == nullptr) return -ENONET;
- uint8_t old_max_samples = cache_info->params.max_samples;
- cache_info->params = params;
- resolv_set_experiment_params(&cache_info->params);
- if (!resolv_is_nameservers_equal(cache_info->nameservers, nameservers)) {
+ uint8_t old_max_samples = netconfig->params.max_samples;
+ netconfig->params = params;
+ resolv_set_experiment_params(&netconfig->params);
+ if (!resolv_is_nameservers_equal(netconfig->nameservers, nameservers)) {
// free current before adding new
- free_nameservers_locked(cache_info);
- cache_info->nameservers = std::move(nameservers);
+ free_nameservers_locked(netconfig);
+ netconfig->nameservers = std::move(nameservers);
for (int i = 0; i < numservers; i++) {
- cache_info->nsaddrinfo[i] = nsaddrinfo[i];
LOG(INFO) << __func__ << ": netid = " << netid
- << ", addr = " << cache_info->nameservers[i];
+ << ", addr = " << netconfig->nameservers[i];
}
- cache_info->nscount = numservers;
+ netconfig->nameserverSockAddrs = std::move(ipSockAddrs);
+ netconfig->nscount = numservers;
} else {
- if (cache_info->params.max_samples != old_max_samples) {
+ if (netconfig->params.max_samples != old_max_samples) {
// If the maximum number of samples changes, the overhead of keeping the most recent
// samples around is not considered worth the effort, so they are cleared instead.
// All other parameters do not affect shared state: Changing these parameters does
// not invalidate the samples, as they only affect aggregation and the conditions
// under which servers are considered usable.
- res_cache_clear_stats_locked(cache_info);
- }
- for (int j = 0; j < numservers; j++) {
- freeaddrinfo(nsaddrinfo[j]);
+ res_cache_clear_stats_locked(netconfig);
}
}
// Always update the search paths. Cache-flushing however is not necessary,
// since the stored cache entries do contain the domain, not just the host name.
- cache_info->search_domains = filter_domains(domains);
+ netconfig->search_domains = filter_domains(domains);
+
+ // Setup stats for cleartext dns servers.
+ if (!netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_TCP) ||
+ !netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_UDP)) {
+ LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats";
+ return -EINVAL;
+ }
+ netconfig->customizedTable.clear();
+ for (const auto& host : experimentalOptions.hosts) {
+ if (!host.hostName.empty() && !host.ipAddr.empty())
+ netconfig->customizedTable.emplace(host.hostName, host.ipAddr);
+ }
+
+ if (experimentalOptions.tcMode < aidl::android::net::IDnsResolver::TC_MODE_DEFAULT ||
+ experimentalOptions.tcMode >= aidl::android::net::IDnsResolver::TC_MODE_MAX) {
+ LOG(WARNING) << __func__ << ": netid = " << netid << ", invalid TC mode";
+ return -EINVAL;
+ }
+ netconfig->tc_mode = experimentalOptions.tcMode;
return 0;
}
@@ -1554,56 +1654,51 @@
return olds == news;
}
-static void free_nameservers_locked(resolv_cache_info* cache_info) {
- int i;
- for (i = 0; i < cache_info->nscount; i++) {
- cache_info->nameservers.clear();
- if (cache_info->nsaddrinfo[i] != nullptr) {
- freeaddrinfo(cache_info->nsaddrinfo[i]);
- cache_info->nsaddrinfo[i] = nullptr;
- }
- }
- cache_info->nscount = 0;
- res_cache_clear_stats_locked(cache_info);
+static void free_nameservers_locked(NetConfig* netconfig) {
+ netconfig->nscount = 0;
+ netconfig->nameservers.clear();
+ netconfig->nameserverSockAddrs.clear();
+ res_cache_clear_stats_locked(netconfig);
}
-void _resolv_populate_res_for_net(res_state statp) {
- if (statp == NULL) {
+void resolv_populate_res_for_net(ResState* statp) {
+ if (statp == nullptr) {
return;
}
LOG(INFO) << __func__ << ": netid=" << statp->netid;
std::lock_guard guard(cache_mutex);
- resolv_cache_info* info = find_cache_info_locked(statp->netid);
- if (info != NULL) {
- int nserv;
- struct addrinfo* ai;
- for (nserv = 0; nserv < MAXNS; nserv++) {
- ai = info->nsaddrinfo[nserv];
- if (ai == NULL) {
- break;
- }
+ NetConfig* info = find_netconfig_locked(statp->netid);
+ if (info == nullptr) return;
- if ((size_t)ai->ai_addrlen <= sizeof(statp->nsaddrs[0])) {
- memcpy(&statp->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
- } else {
- LOG(INFO) << __func__ << ": found too long addrlen";
- }
+ // TODO: Convert nsaddrs[] to c++ container and remove the size-checking.
+ const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size()));
+
+ for (int nserv = 0; nserv < serverNum; nserv++) {
+ sockaddr_storage ss = info->nameserverSockAddrs.at(nserv);
+
+ if (auto sockaddr_len = sockaddrSize(ss); sockaddr_len != 0) {
+ memcpy(&statp->nsaddrs[nserv], &ss, sockaddr_len);
+ } else {
+ LOG(WARNING) << __func__ << ": can't get sa_len from "
+ << info->nameserverSockAddrs.at(nserv);
}
- statp->nscount = nserv;
- statp->search_domains = info->search_domains;
}
+
+ statp->nscount = serverNum;
+ statp->search_domains = info->search_domains;
+ statp->tc_mode = info->tc_mode;
}
/* Resolver reachability statistics. */
-static void _res_cache_add_stats_sample_locked(res_stats* stats, const res_sample* sample,
- int max_samples) {
+static void res_cache_add_stats_sample_locked(res_stats* stats, const res_sample& sample,
+ int max_samples) {
// Note: This function expects max_samples > 0, otherwise a (harmless) modification of the
// allocated but supposedly unused memory for samples[0] will happen
LOG(INFO) << __func__ << ": adding sample to stats, next = " << unsigned(stats->sample_next)
<< ", count = " << unsigned(stats->sample_count);
- stats->samples[stats->sample_next] = *sample;
+ stats->samples[stats->sample_next] = sample;
if (stats->sample_count < max_samples) {
++stats->sample_count;
}
@@ -1612,17 +1707,17 @@
}
}
-static void res_cache_clear_stats_locked(resolv_cache_info* cache_info) {
+static void res_cache_clear_stats_locked(NetConfig* netconfig) {
for (int i = 0; i < MAXNS; ++i) {
- cache_info->nsstats[i].sample_count = 0;
- cache_info->nsstats[i].sample_next = 0;
+ netconfig->nsstats[i].sample_count = 0;
+ netconfig->nsstats[i].sample_next = 0;
}
// Increment the revision id to ensure that sample state is not written back if the
// servers change; in theory it would suffice to do so only if the servers or
// max_samples actually change, in practice the overhead of checking is higher than the
// cost, and overflows are unlikely.
- ++cache_info->revision_id;
+ ++netconfig->revision_id;
}
int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
@@ -1633,7 +1728,7 @@
int revision_id = -1;
std::lock_guard guard(cache_mutex);
- resolv_cache_info* info = find_cache_info_locked(netid);
+ NetConfig* info = find_netconfig_locked(netid);
if (info) {
if (info->nscount > MAXNS) {
LOG(INFO) << __func__ << ": nscount " << info->nscount << " > MAXNS " << MAXNS;
@@ -1641,31 +1736,18 @@
return -1;
}
int i;
- for (i = 0; i < info->nscount; i++) {
- // Verify that the following assumptions are held, failure indicates corruption:
- // - getaddrinfo() may never return a sockaddr > sockaddr_storage
- // - all addresses are valid
- // - there is only one address per addrinfo thanks to numeric resolution
- int addrlen = info->nsaddrinfo[i]->ai_addrlen;
- if (addrlen < (int) sizeof(struct sockaddr) || addrlen > (int) sizeof(servers[0])) {
- LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addrlen == " << addrlen;
- errno = EMSGSIZE;
- return -1;
- }
- if (info->nsaddrinfo[i]->ai_addr == NULL) {
- LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addr == NULL";
- errno = ENOENT;
- return -1;
- }
- if (info->nsaddrinfo[i]->ai_next != NULL) {
- LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_next != NULL";
- errno = ENOTUNIQ;
- return -1;
- }
- }
*nscount = info->nscount;
+
+ // It shouldn't happen, but just in case of buffer overflow.
+ if (info->nscount != static_cast<int>(info->nameserverSockAddrs.size())) {
+ LOG(INFO) << __func__ << ": nscount " << info->nscount
+ << " != " << info->nameserverSockAddrs.size();
+ errno = EFAULT;
+ return -1;
+ }
+
for (i = 0; i < info->nscount; i++) {
- memcpy(&servers[i], info->nsaddrinfo[i]->ai_addr, info->nsaddrinfo[i]->ai_addrlen);
+ servers[i] = info->nameserverSockAddrs.at(i);
stats[i] = info->nsstats[i];
}
@@ -1684,10 +1766,10 @@
std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
using android::base::StringPrintf;
std::lock_guard guard(cache_mutex);
- resolv_cache_info* cache_info = find_cache_info_locked(netid);
- if (cache_info == nullptr) return {};
+ NetConfig* netconfig = find_netconfig_locked(netid);
+ if (netconfig == nullptr) return {};
std::vector<std::string> result;
- for (const auto& pair : cache_info->dns_event_subsampling_map) {
+ for (const auto& pair : netconfig->dns_event_subsampling_map) {
result.push_back(StringPrintf("%s:%d",
(pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
? "default"
@@ -1703,9 +1785,9 @@
// Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded.
uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) {
std::lock_guard guard(cache_mutex);
- resolv_cache_info* cache_info = find_cache_info_locked(netid);
- if (cache_info == nullptr) return 0; // Don't log anything at all.
- const auto& subsampling_map = cache_info->dns_event_subsampling_map;
+ NetConfig* netconfig = find_netconfig_locked(netid);
+ if (netconfig == nullptr) return 0; // Don't log anything at all.
+ const auto& subsampling_map = netconfig->dns_event_subsampling_map;
auto search_returnCode = subsampling_map.find(return_code);
uint32_t denom;
if (search_returnCode != subsampling_map.end()) {
@@ -1719,7 +1801,7 @@
int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) {
std::lock_guard guard(cache_mutex);
- resolv_cache_info* info = find_cache_info_locked(netid);
+ NetConfig* info = find_netconfig_locked(netid);
if (info) {
memcpy(stats, info->nsstats, sizeof(info->nsstats));
*params = info->params;
@@ -1729,15 +1811,22 @@
return -1;
}
-void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
- const res_sample* sample, int max_samples) {
- if (max_samples <= 0) return;
+void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, const sockaddr* sa,
+ const res_sample& sample, int max_samples) {
+ if (max_samples <= 0 || sa == nullptr) return;
std::lock_guard guard(cache_mutex);
- resolv_cache_info* info = find_cache_info_locked(netid);
+ NetConfig* info = find_netconfig_locked(netid);
if (info && info->revision_id == revision_id) {
- _res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+ const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size()));
+ const IPSockAddr ipsa = IPSockAddr::toIPSockAddr(*sa);
+ for (int ns = 0; ns < serverNum; ns++) {
+ if (ipsa == info->nameserverSockAddrs.at(ns)) {
+ res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+ return;
+ }
+ }
}
}
@@ -1779,3 +1868,60 @@
*expiration = e->expires;
return 0;
}
+
+int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers) {
+ std::lock_guard guard(cache_mutex);
+ const auto info = find_netconfig_locked(netid);
+
+ if (info == nullptr) return -ENONET;
+
+ std::vector<IPSockAddr> serverSockAddrs;
+ serverSockAddrs.reserve(servers.size());
+ for (const auto& server : servers) {
+ serverSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 853));
+ }
+
+ if (!info->dnsStats.setServers(serverSockAddrs, android::net::PROTO_DOT)) {
+ LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& server,
+ const DnsQueryEvent* record) {
+ if (record == nullptr) return false;
+
+ std::lock_guard guard(cache_mutex);
+ if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+ return info->dnsStats.addStats(server, *record);
+ }
+ return false;
+}
+
+void resolv_stats_dump(DumpWriter& dw, unsigned netid) {
+ std::lock_guard guard(cache_mutex);
+ if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+ info->dnsStats.dump(dw);
+ }
+}
+
+void resolv_oem_options_dump(DumpWriter& dw, unsigned netid) {
+ std::lock_guard guard(cache_mutex);
+ if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+ // TODO: dump info->hosts
+ dw.println("TC mode: %s", tc_mode_to_str(info->tc_mode));
+ }
+}
+
+const char* tc_mode_to_str(const int mode) {
+ switch (mode) {
+ case aidl::android::net::IDnsResolver::TC_MODE_DEFAULT:
+ return "default";
+ case aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP:
+ return "UDP_TCP";
+ default:
+ return "unknown";
+ }
+}
diff --git a/res_comp.cpp b/res_comp.cpp
index c9889cb..9a191fa 100644
--- a/res_comp.cpp
+++ b/res_comp.cpp
@@ -70,14 +70,13 @@
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "res_comp.h"
+
#include <arpa/nameser.h>
-#include <ctype.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
-#include "resolv_private.h"
-
/*
* Expand compressed domain name 'src' to full domain name.
* 'msg' is a pointer to the begining of the message,
@@ -89,7 +88,7 @@
int n = ns_name_uncompress(msg, eom, src, dst, (size_t) dstsiz);
if (n > 0 && dst[0] == '.') dst[0] = '\0';
- return (n);
+ return n;
}
/*
@@ -141,7 +140,7 @@
#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c))
#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
-int res_hnok(const char* dn) {
+bool res_hnok(const char* dn) {
int pch = PERIOD, ch = *dn++;
while (ch != '\0') {
@@ -150,60 +149,25 @@
if (periodchar(ch)) {
;
} else if (periodchar(pch)) {
- if (!borderchar(ch)) return (0);
+ if (!borderchar(ch)) return false;
} else if (periodchar(nch) || nch == '\0') {
- if (!borderchar(ch)) return (0);
+ if (!borderchar(ch)) return false;
} else {
- if (!middlechar(ch)) return (0);
+ if (!middlechar(ch)) return false;
}
pch = ch, ch = nch;
}
- return (1);
-}
-
-/*
- * hostname-like (A, MX, WKS) owners can have "*" as their first label
- * but must otherwise be as a host name.
- */
-int res_ownok(const char* dn) {
- if (asterchar(dn[0])) {
- if (periodchar(dn[1])) return (res_hnok(dn + 2));
- if (dn[1] == '\0') return (1);
- }
- return (res_hnok(dn));
-}
-
-/*
- * SOA RNAMEs and RP RNAMEs can have any printable character in their first
- * label, but the rest of the name has to look like a host name.
- */
-int res_mailok(const char* dn) {
- int ch, escaped = 0;
-
- /* "." is a valid missing representation */
- if (*dn == '\0') return (1);
-
- /* otherwise <label>.<hostname> */
- while ((ch = *dn++) != '\0') {
- if (!domainchar(ch)) return (0);
- if (!escaped && periodchar(ch)) break;
- if (escaped)
- escaped = 0;
- else if (bslashchar(ch))
- escaped = 1;
- }
- if (periodchar(ch)) return (res_hnok(dn));
- return (0);
+ return true;
}
/*
* This function is quite liberal, since RFC 1034's character sets are only
* recommendations.
*/
-int res_dnok(const char* dn) {
+bool res_dnok(const char* dn) {
int ch;
while ((ch = *dn++) != '\0')
- if (!domainchar(ch)) return (0);
- return (1);
+ if (!domainchar(ch)) return false;
+ return true;
}
diff --git a/res_comp.h b/res_comp.h
new file mode 100644
index 0000000..6022838
--- /dev/null
+++ b/res_comp.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+int dn_expand(const uint8_t* msg, const uint8_t* eom, const uint8_t* src, char* dst, int dstsiz);
+int dn_comp(const char* src, uint8_t* dst, int dstsiz, uint8_t** dnptrs, uint8_t** lastdnptr);
+int dn_skipname(const uint8_t* ptr, const uint8_t* eom);
+
+bool res_hnok(const char* dn);
+bool res_dnok(const char* dn);
diff --git a/res_init.cpp b/res_init.cpp
index 12f4124..16fd98c 100644
--- a/res_init.cpp
+++ b/res_init.cpp
@@ -92,24 +92,27 @@
#include "netd_resolv/resolv.h"
#include "resolv_private.h"
-// Set up Resolver state default settings.
-// Note: this is called with statp zero-initialized
-void res_init(res_state statp) {
- statp->netid = NETID_UNSET;
+void res_init(ResState* statp, const struct android_net_context* _Nonnull netcontext,
+ android::net::NetworkDnsEventReported* _Nonnull event) {
+ memset(statp, 0, sizeof *statp);
+
+ statp->netid = netcontext->dns_netid;
+ statp->uid = netcontext->uid;
+ statp->pid = netcontext->pid;
statp->id = arc4random_uniform(65536);
- statp->_mark = MARK_UNSET;
+ statp->_mark = netcontext->dns_mark;
+ statp->netcontext_flags = netcontext->flags;
+ statp->event = event;
statp->ndots = 1;
statp->_vcsock = -1;
- statp->_flags = 0;
- statp->netcontext_flags = 0;
for (int ns = 0; ns < MAXNS; ns++) {
statp->nssocks[ns] = -1;
}
// The following dummy initialization is probably useless because
- // it's overwritten later by _resolv_populate_res_for_net().
+ // it's overwritten later by resolv_populate_res_for_net().
// TODO: check if it's safe to remove.
const sockaddr_union u{
.sin.sin_addr.s_addr = INADDR_ANY,
@@ -142,14 +145,3 @@
}
}
}
-
-void res_setnetcontext(res_state statp, const struct android_net_context* netcontext,
- android::net::NetworkDnsEventReported* _Nonnull event) {
- if (statp != nullptr) {
- statp->netid = netcontext->dns_netid;
- statp->uid = netcontext->uid;
- statp->_mark = netcontext->dns_mark;
- statp->netcontext_flags = netcontext->flags;
- statp->event = event;
- }
-}
diff --git a/res_init.h b/res_init.h
index af76414..4c759c4 100644
--- a/res_init.h
+++ b/res_init.h
@@ -15,5 +15,8 @@
*/
#pragma once
-struct __res_state;
-void res_init(__res_state* statp);
+#include "resolv_private.h"
+
+// TODO: make this a constructor for ResState
+void res_init(ResState* res, const struct android_net_context* netcontext,
+ android::net::NetworkDnsEventReported* event);
diff --git a/res_mkquery.cpp b/res_mkquery.cpp
index 5dde9be..b95d31f 100644
--- a/res_mkquery.cpp
+++ b/res_mkquery.cpp
@@ -80,8 +80,11 @@
#include <string.h>
#include <android-base/logging.h>
+#include <netd_resolv/resolv.h> // NET_CONTEXT_FLAG_USE_DNS_OVER_TLS
-#include "resolv_private.h"
+#include "res_comp.h"
+#include "res_debug.h"
+#include "resolv_private.h" // res_state
// Queries will be padded to a multiple of this length when EDNS0 is active.
constexpr uint16_t kEdns0Padding = 128;
@@ -93,19 +96,15 @@
"11", "12", "13", "ZONEINIT", "ZONEREF",
};
-/*
- * Form all types of queries.
- * Returns the size of the result or -1.
- */
-int res_nmkquery(res_state statp, int op, /* opcode of query */
- const char* dname, /* domain name */
- int cl, int type, /* class and type of query */
- const uint8_t* data, /* resource record data */
- int datalen, /* length of data */
- const uint8_t* /*newrr_in*/, /* new rr for modify or append */
- uint8_t* buf, /* buffer to put query */
- int buflen) /* size of buffer */
-{
+// Form all types of queries. Returns the size of the result or -1.
+int res_nmkquery(int op, // opcode of query
+ const char* dname, // domain name
+ int cl, int type, // class and type of query
+ const uint8_t* data, // resource record data
+ int datalen, // length of data
+ uint8_t* buf, // buffer to put query
+ int buflen, // size of buffer
+ int netcontext_flags) {
HEADER* hp;
uint8_t *cp, *ep;
int n;
@@ -123,7 +122,7 @@
hp->id = htons(arc4random_uniform(65536));
hp->opcode = op;
hp->rd = true;
- hp->ad = (statp->netcontext_flags & NET_CONTEXT_FLAG_USE_DNS_OVER_TLS) != 0U;
+ hp->ad = (netcontext_flags & NET_CONTEXT_FLAG_USE_DNS_OVER_TLS) != 0U;
hp->rcode = NOERROR;
cp = buf + HFIXEDSZ;
ep = buf + buflen;
diff --git a/res_query.cpp b/res_query.cpp
index f54ff3f..7702b44 100644
--- a/res_query.cpp
+++ b/res_query.cpp
@@ -89,12 +89,6 @@
#include "resolv_cache.h"
#include "resolv_private.h"
-#if PACKETSZ > 1024
-#define MAXPACKET PACKETSZ
-#else
-#define MAXPACKET 1024
-#endif
-
/*
* Formulate a normal query, send, and await answer.
* Returned answer is placed in supplied buffer "answer".
@@ -123,7 +117,8 @@
LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
- n = res_nmkquery(statp, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+ n = res_nmkquery(QUERY, name, cl, type, /*data=*/nullptr, 0, buf, sizeof(buf),
+ statp->netcontext_flags);
if (n > 0 &&
(statp->netcontext_flags &
(NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS)) &&
@@ -255,7 +250,7 @@
* be loaded once for the thread instead of each
* time a query is tried.
*/
- _resolv_populate_res_for_net(statp);
+ resolv_populate_res_for_net(statp);
for (const auto& domain : statp->search_domains) {
if (domain == "." || domain == "") ++root_on_list;
diff --git a/res_send.cpp b/res_send.cpp
index 6616ca3..66548bd 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -83,7 +83,6 @@
#include <arpa/inet.h>
#include <arpa/nameser.h>
-#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
@@ -104,11 +103,15 @@
#include "DnsTlsTransport.h"
#include "PrivateDnsConfiguration.h"
#include "netd_resolv/resolv.h"
-#include "netd_resolv/stats.h"
#include "private/android_filesystem_config.h"
+
+#include "res_comp.h"
#include "res_debug.h"
+#include "res_init.h"
#include "resolv_cache.h"
+#include "stats.h"
#include "stats.pb.h"
+#include "util.h"
// TODO: use the namespace something like android::netd_resolv for libnetd_resolv
using android::net::CacheStatus;
@@ -129,12 +132,12 @@
using android::net::PrivateDnsStatus;
using android::net::PROTO_TCP;
using android::net::PROTO_UDP;
+using android::netdutils::IPSockAddr;
using android::netdutils::Slice;
using android::netdutils::Stopwatch;
static DnsTlsDispatcher sDnsTlsDispatcher;
-static int get_salen(const struct sockaddr*);
static struct sockaddr* get_nsaddr(res_state, size_t);
static int send_vc(res_state, res_params* params, const uint8_t*, int, uint8_t*, int, int*, int,
time_t*, int*, int*);
@@ -224,15 +227,6 @@
return tsnow;
}
-static struct iovec evConsIovec(void* buf, size_t cnt) {
- struct iovec ret;
-
- memset(&ret, 0xf5, sizeof ret);
- ret.iov_base = buf;
- ret.iov_len = cnt;
- return ret;
-}
-
// END: Code copied from ISC eventlib
/* BIONIC-BEGIN: implement source port randomization */
@@ -405,24 +399,20 @@
int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int anssiz, int* rcode,
uint32_t flags) {
- int gotsomewhere, terrno, v_circuit, resplen, n;
- ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
+ LOG(DEBUG) << __func__;
+ // Should not happen
if (anssiz < HFIXEDSZ) {
// TODO: Remove errno once callers stop using it
errno = EINVAL;
return -EINVAL;
}
- LOG(DEBUG) << __func__;
res_pquery(buf, buflen);
- v_circuit = buflen > PACKETSZ;
- gotsomewhere = 0;
- terrno = ETIMEDOUT;
-
int anslen = 0;
Stopwatch cacheStopwatch;
- cache_status = resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
+ ResolvCacheStatus cache_status =
+ resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
const int32_t cacheLatencyUs = saturate_cast<int32_t>(cacheStopwatch.timeTakenUs());
if (cache_status == RESOLV_CACHE_FOUND) {
HEADER* hp = (HEADER*)(void*)ans;
@@ -430,11 +420,12 @@
DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
dnsQueryEvent->set_latency_micros(cacheLatencyUs);
dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
return anslen;
} else if (cache_status != RESOLV_CACHE_UNSUPPORTED) {
// had a cache miss for a known network, so populate the thread private
// data so the normal resolve path can do its thing
- _resolv_populate_res_for_net(statp);
+ resolv_populate_res_for_net(statp);
}
if (statp->nscount == 0) {
// We have no nameservers configured, so there's no point trying.
@@ -447,6 +438,25 @@
return -ESRCH;
}
+ // DoT
+ if (!(statp->netcontext_flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS)) {
+ bool fallback = false;
+ int resplen = res_tls_send(statp, Slice(const_cast<uint8_t*>(buf), buflen),
+ Slice(ans, anssiz), rcode, &fallback);
+ if (resplen > 0) {
+ LOG(DEBUG) << __func__ << ": got answer from DoT";
+ res_pquery(ans, resplen);
+ if (cache_status == RESOLV_CACHE_NOTFOUND) {
+ resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
+ }
+ return resplen;
+ }
+ if (!fallback) {
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ return -ETIMEDOUT;
+ }
+ }
+
res_stats stats[MAXNS];
res_params params;
int revision_id = resolv_cache_get_resolver_stats(statp->netid, ¶ms, stats);
@@ -467,129 +477,88 @@
res_set_usable_server(selectedServer, statp->nscount, usable_servers);
}
- /*
- * Send request, RETRY times, or until successful.
- */
+ // Send request, RETRY times, or until successful.
int retryTimes = (flags & ANDROID_RESOLV_NO_RETRY) ? 1 : params.retry_count;
+ int useTcp = buflen > PACKETSZ;
+ int gotsomewhere = 0;
+ int terrno = ETIMEDOUT;
for (int attempt = 0; attempt < retryTimes; ++attempt) {
-
- for (int ns = 0; ns < statp->nscount; ns++) {
+ for (int ns = 0; ns < statp->nscount; ++ns) {
if (!usable_servers[ns]) continue;
- int nsaplen;
- time_t now = 0;
- int delay = 0;
- *rcode = RCODE_INTERNAL_ERROR;
- const sockaddr* nsap = get_nsaddr(statp, ns);
- nsaplen = get_salen(nsap);
- same_ns:
- // TODO: Since we expect there is only one DNS server being queried here while this
- // function tries to query all of private DNS servers. Consider moving it to other
- // reasonable place. In addition, maybe add stats for private DNS.
- if (!(statp->netcontext_flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS)) {
- bool fallback = false;
- resplen = res_tls_send(statp, Slice(const_cast<uint8_t*>(buf), buflen),
- Slice(ans, anssiz), rcode, &fallback);
- if (resplen > 0) {
- LOG(DEBUG) << __func__ << ": got answer from DoT";
- res_pquery(ans, resplen);
- if (cache_status == RESOLV_CACHE_NOTFOUND) {
- resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
- }
- return resplen;
- }
- if (!fallback) {
- _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
- res_nclose(statp);
- return -terrno;
- }
- }
+ *rcode = RCODE_INTERNAL_ERROR;
+
+ // Get server addr
+ const sockaddr* nsap = get_nsaddr(statp, ns);
+ const int nsaplen = sockaddrSize(nsap);
static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
char abuf[NI_MAXHOST];
- DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
- dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
-
if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0)
LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1
<< ") address = " << abuf;
+ ::android::net::Protocol query_proto = useTcp ? PROTO_TCP : PROTO_UDP;
+ time_t now = 0;
+ int delay = 0;
+ bool fallbackTCP = false;
+ const bool shouldRecordStats = (attempt == 0);
+ int resplen;
Stopwatch queryStopwatch;
- if (v_circuit) {
- /* Use VC; at most one attempt per server. */
- bool shouldRecordStats = (attempt == 0);
+ if (useTcp) {
+ // TCP; at most one attempt per server.
attempt = retryTimes;
+ resplen = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now,
+ rcode, &delay);
- n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode,
- &delay);
-
- dnsQueryEvent->set_latency_micros(
- saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
- dnsQueryEvent->set_dns_server_index(ns);
- dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
- dnsQueryEvent->set_retry_times(attempt);
- dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
- dnsQueryEvent->set_protocol(PROTO_TCP);
- dnsQueryEvent->set_type(getQueryType(buf, buflen));
-
- /*
- * Only record stats the first time we try a query. This ensures that
- * queries that deterministically fail (e.g., a name that always returns
- * SERVFAIL or times out) do not unduly affect the stats.
- */
- if (shouldRecordStats) {
- res_sample sample;
- _res_stats_set_sample(&sample, now, *rcode, delay);
- _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
- params.max_samples);
+ if (buflen <= PACKETSZ && resplen <= 0 &&
+ statp->tc_mode == aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP) {
+ // reset to UDP for next query on next DNS server if resolver is currently doing
+ // TCP fallback retry and current server does not support TCP connectin
+ useTcp = false;
}
-
- LOG(INFO) << __func__ << ": used send_vc " << n;
-
- if (n < 0) {
- _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
- res_nclose(statp);
- return -terrno;
- };
- if (n == 0) goto next_ns;
- resplen = n;
+ LOG(INFO) << __func__ << ": used send_vc " << resplen;
} else {
- /* Use datagrams. */
- LOG(INFO) << __func__ << ": using send_dg";
-
- n = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit,
- &gotsomewhere, &now, rcode, &delay);
-
- dnsQueryEvent->set_latency_micros(
- saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
- dnsQueryEvent->set_dns_server_index(ns);
- dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
- dnsQueryEvent->set_retry_times(attempt);
- dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
- dnsQueryEvent->set_protocol(PROTO_UDP);
- dnsQueryEvent->set_type(getQueryType(buf, buflen));
-
- /* Only record stats the first time we try a query. See above. */
- if (attempt == 0) {
- res_sample sample;
- _res_stats_set_sample(&sample, now, *rcode, delay);
- _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
- params.max_samples);
- }
-
- LOG(INFO) << __func__ << ": used send_dg " << n;
-
- if (n < 0) {
- _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
- res_nclose(statp);
- return -terrno;
- };
- if (n == 0) goto next_ns;
- if (v_circuit) goto same_ns;
- resplen = n;
+ // UDP
+ resplen = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &useTcp,
+ &gotsomewhere, &now, rcode, &delay);
+ fallbackTCP = useTcp ? true : false;
+ LOG(INFO) << __func__ << ": used send_dg " << resplen;
}
+ DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+ dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(ns);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+ dnsQueryEvent->set_retry_times(attempt);
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+ dnsQueryEvent->set_protocol(query_proto);
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
+ // Only record stats the first time we try a query. This ensures that
+ // queries that deterministically fail (e.g., a name that always returns
+ // SERVFAIL or times out) do not unduly affect the stats.
+ if (shouldRecordStats) {
+ res_sample sample;
+ _res_stats_set_sample(&sample, now, *rcode, delay);
+ resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, nsap, sample,
+ params.max_samples);
+ resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(*nsap), dnsQueryEvent);
+ }
+
+ if (resplen == 0) continue;
+ if (fallbackTCP) {
+ ns--;
+ continue;
+ }
+ if (resplen < 0) {
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+ res_nclose(statp);
+ return -terrno;
+ };
+
LOG(DEBUG) << __func__ << ": got answer:";
res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
@@ -598,38 +567,21 @@
}
res_nclose(statp);
return (resplen);
- next_ns:;
} // for each ns
} // for each retry
res_nclose(statp);
- if (!v_circuit) {
- if (!gotsomewhere) {
- // TODO: Remove errno once callers stop using it
- errno = ECONNREFUSED; /* no nameservers found */
- terrno = ECONNREFUSED;
- } else {
- // TODO: Remove errno once callers stop using it
- errno = ETIMEDOUT; /* no answer obtained */
- terrno = ETIMEDOUT;
- }
- } else {
- errno = terrno;
- }
+ terrno = useTcp ? terrno : gotsomewhere ? ETIMEDOUT : ECONNREFUSED;
+ // TODO: Remove errno once callers stop using it
+ errno = useTcp ? terrno
+ : gotsomewhere ? ETIMEDOUT /* no answer obtained */
+ : ECONNREFUSED /* no nameservers found */;
+
_resolv_cache_query_failed(statp->netid, buf, buflen, flags);
return -terrno;
}
/* Private */
-static int get_salen(const struct sockaddr* sa) {
- if (sa->sa_family == AF_INET)
- return (sizeof(struct sockaddr_in));
- else if (sa->sa_family == AF_INET6)
- return (sizeof(struct sockaddr_in6));
- else
- return (0); /* unknown, die on connect */
-}
-
static struct sockaddr* get_nsaddr(res_state statp, size_t n) {
return (struct sockaddr*)(void*)&statp->nsaddrs[n];
}
@@ -665,13 +617,12 @@
struct sockaddr* nsap;
int nsaplen;
int truncating, connreset, n;
- struct iovec iov[2];
uint8_t* cp;
LOG(INFO) << __func__ << ": using send_vc";
nsap = get_nsaddr(statp, (size_t) ns);
- nsaplen = get_salen(nsap);
+ nsaplen = sockaddrSize(nsap);
connreset = 0;
same_ns:
@@ -711,7 +662,7 @@
return -1;
}
}
- resolv_tag_socket(statp->_vcsock, statp->uid);
+ resolv_tag_socket(statp->_vcsock, statp->uid, statp->pid);
if (statp->_mark != MARK_UNSET) {
if (setsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &statp->_mark,
sizeof(statp->_mark)) < 0) {
@@ -750,8 +701,10 @@
* Send length & message
*/
uint16_t len = htons(static_cast<uint16_t>(buflen));
- iov[0] = evConsIovec(&len, INT16SZ);
- iov[1] = evConsIovec((void*) buf, (size_t) buflen);
+ const iovec iov[] = {
+ {.iov_base = &len, .iov_len = INT16SZ},
+ {.iov_base = const_cast<uint8_t*>(buf), .iov_len = static_cast<size_t>(buflen)},
+ };
if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
*terrno = errno;
PLOG(DEBUG) << __func__ << ": write failed: ";
@@ -859,8 +812,8 @@
}
/* return -1 on error (errno set), 0 on success */
-static int connect_with_timeout(int sock, const struct sockaddr* nsap, socklen_t salen,
- const struct timespec timeout) {
+static int connect_with_timeout(int sock, const sockaddr* nsap, socklen_t salen,
+ const timespec timeout) {
int res, origflags;
origflags = fcntl(sock, F_GETFL, 0);
@@ -872,8 +825,8 @@
goto done;
}
if (res != 0) {
- struct timespec now = evNowTime();
- struct timespec finish = evAddTime(now, timeout);
+ timespec now = evNowTime();
+ timespec finish = evAddTime(now, timeout);
LOG(INFO) << __func__ << ": " << sock << " send_vc";
res = retrying_poll(sock, POLLIN | POLLOUT, &finish);
if (res <= 0) {
@@ -937,7 +890,7 @@
int resplen, n, s;
nsap = get_nsaddr(statp, (size_t) ns);
- nsaplen = get_salen(nsap);
+ nsaplen = sockaddrSize(nsap);
if (statp->nssocks[ns] == -1) {
statp->nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (statp->nssocks[ns] < 0) {
@@ -954,7 +907,7 @@
}
}
- resolv_tag_socket(statp->nssocks[ns], statp->uid);
+ resolv_tag_socket(statp->nssocks[ns], statp->uid, statp->pid);
if (statp->_mark != MARK_UNSET) {
if (setsockopt(statp->nssocks[ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
sizeof(statp->_mark)) < 0) {
@@ -1098,6 +1051,7 @@
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
constexpr int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+ const int err = errno;
if (!WOULD_LOG(DEBUG)) return;
@@ -1107,6 +1061,7 @@
strncpy(sbuf, "?", sizeof(sbuf) - 1);
sbuf[sizeof(sbuf) - 1] = '\0';
}
+ errno = err;
PLOG(DEBUG) << __func__ << ": " << str << " ([" << hbuf << "]." << sbuf << "): ";
}
@@ -1141,8 +1096,9 @@
return PrivateDnsModes::PDM_OPPORTUNISTIC;
case PrivateDnsMode::STRICT:
return PrivateDnsModes::PDM_STRICT;
+ default:
+ return PrivateDnsModes::PDM_UNKNOWN;
}
- return PrivateDnsModes::PDM_UNKNOWN;
}
static int res_tls_send(res_state statp, const Slice query, const Slice answer, int* rcode,
@@ -1237,9 +1193,9 @@
uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
NetworkDnsEventReported* event) {
assert(event != nullptr);
- res_state res = res_get_state();
- res_setnetcontext(res, netContext, event);
- _resolv_populate_res_for_net(res);
+ ResState res;
+ res_init(&res, netContext, event);
+ resolv_populate_res_for_net(&res);
*rcode = NOERROR;
- return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags);
+ return res_nsend(&res, msg, msgLen, ans, ansLen, rcode, flags);
}
diff --git a/res_state.cpp b/res_state.cpp
deleted file mode 100644
index 7388082..0000000
--- a/res_state.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define LOG_TAG "resolv"
-
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <netdb.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h> /* for gettid() */
-
-#include <android-base/logging.h>
-
-#include "res_init.h"
-#include "resolv_cache.h"
-#include "resolv_private.h"
-
-typedef struct {
- // TODO: Have one __res_state per network so we don't have to repopulate frequently.
- struct __res_state _nres[1];
-} _res_thread;
-
-static _res_thread* res_thread_alloc(void) {
- _res_thread* rt = (_res_thread*) calloc(1, sizeof(*rt));
- return rt;
-}
-
-static void res_thread_free(void* _rt) {
- _res_thread* rt = (_res_thread*) _rt;
-
- LOG(VERBOSE) << __func__ << ": rt=" << rt << " for thread=" << gettid();
-
- res_nclose(rt->_nres);
- free(rt);
-}
-
-static pthread_key_t _res_key;
-
-__attribute__((constructor)) static void __res_key_init() {
- pthread_key_create(&_res_key, res_thread_free);
-}
-
-static _res_thread* res_thread_get(void) {
- _res_thread* rt = (_res_thread*) pthread_getspecific(_res_key);
- if (rt != NULL) {
- return rt;
- }
-
- /* It is the first time this function is called in this thread,
- * we need to create a new thread-specific DNS resolver state. */
- rt = res_thread_alloc();
- if (rt == NULL) {
- return NULL;
- }
- pthread_setspecific(_res_key, rt);
-
- LOG(VERBOSE) << __func__ << ": tid=" << gettid() << ", rt=" << rt;
- res_init(rt->_nres);
- return rt;
-}
-
-struct __res_state _nres;
-
-res_state res_get_state(void) {
- _res_thread* rt = res_thread_get();
-
- return rt ? rt->_nres : NULL;
-}
diff --git a/res_stats.cpp b/res_stats.cpp
index 5faf7ca..f3b79bb 100644
--- a/res_stats.cpp
+++ b/res_stats.cpp
@@ -21,8 +21,7 @@
#include <android-base/logging.h>
-#include "netd_resolv/stats.h"
-
+#include "stats.h"
// Calculate the round-trip-time from start time t0 and end time t1.
int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0) {
@@ -119,11 +118,11 @@
android_net_res_stats_aggregate(stats, &successes, &errors, &timeouts, &internal_errors,
&rtt_avg, &last_sample_time);
if (successes >= 0 && errors >= 0 && timeouts >= 0) {
- int total = successes + errors + timeouts;
+ int total = successes + errors + timeouts + internal_errors;
LOG(INFO) << __func__ << ": NS stats: S " << successes << " + E " << errors << " + T "
<< timeouts << " + I " << internal_errors << " = " << total
<< ", rtt = " << rtt_avg << ", min_samples = " << unsigned(params->min_samples);
- if (total >= params->min_samples && (errors > 0 || timeouts > 0)) {
+ if (total >= params->min_samples) {
int success_rate = successes * 100 / total;
LOG(INFO) << __func__ << ": success rate " << success_rate;
if (success_rate < params->success_threshold) {
diff --git a/resolv_cache.h b/resolv_cache.h
index 9a9787e..d34ae95 100644
--- a/resolv_cache.h
+++ b/resolv_cache.h
@@ -28,22 +28,27 @@
#pragma once
-#include "netd_resolv/resolv.h"
-
-#include <stddef.h>
-
#include <unordered_map>
#include <vector>
-struct __res_state;
-struct resolv_cache;
+#include <aidl/android/net/IDnsResolver.h>
+#include <aidl/android/net/ResolverExperimentalOptionsParcel.h>
-constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
+#include <netdutils/DumpWriter.h>
+#include <netdutils/InternetAddresses.h>
+#include <stats.pb.h>
-/* sets the name server addresses to the provided res_state structure. The
- * name servers are retrieved from the cache which is associated
- * with the network to which the res_state structure is associated */
-void _resolv_populate_res_for_net(struct __res_state* statp);
+#include "ResolverStats.h"
+#include "params.h"
+
+// Sets the name server addresses to the provided ResState.
+// The name servers are retrieved from the cache which is associated
+// with the network to which ResState is associated.
+struct ResState;
+
+typedef std::multimap<std::string /* hostname */, std::string /* IPv4/IPv6 address */> HostMapping;
+
+void resolv_populate_res_for_net(ResState* statp);
std::vector<unsigned> resolv_list_caches();
@@ -69,9 +74,16 @@
/* Notify the cache a request failed */
void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags);
+// Get a customized table for a given network.
+std::vector<std::string> getCustomizedTableByName(const size_t netid, const char* hostname);
+
// Sets name servers for a given network.
-int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
- const std::vector<std::string>& domains, const res_params& params);
+// TODO: Pass all of ResolverParamsParcel and remove the res_params argument.
+int resolv_set_nameservers(
+ unsigned netid, const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const res_params& params,
+ const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions = {
+ {} /* hosts */, aidl::android::net::IDnsResolver::TC_MODE_DEFAULT});
// Creates the cache associated with the given network.
int resolv_create_cache_for_net(unsigned netid);
@@ -79,6 +91,9 @@
// Deletes the cache associated with the given network.
void resolv_delete_cache_for_net(unsigned netid);
+// Flushes the cache associated with the given network.
+int resolv_flush_cache_for_net(unsigned netid);
+
// For test only.
// Return true if the cache is existent in the given network, false otherwise.
bool has_named_cache(unsigned netid);
@@ -87,3 +102,16 @@
// Get the expiration time of a cache entry. Return 0 on success; otherwise, an negative error is
// returned if the expiration time can't be acquired.
int resolv_cache_get_expiration(unsigned netid, const std::vector<char>& query, time_t* expiration);
+
+// Set private DNS servers to DnsStats for a given network.
+int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers);
+
+// Add a statistics record to DnsStats for a given network.
+bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& server,
+ const android::net::DnsQueryEvent* record);
+
+void resolv_stats_dump(android::netdutils::DumpWriter& dw, unsigned netid);
+
+void resolv_oem_options_dump(android::netdutils::DumpWriter& dw, unsigned netid);
+
+const char* tc_mode_to_str(const int mode);
diff --git a/resolv_cache_unit_test.cpp b/resolv_cache_unit_test.cpp
index f62f317..03f8806 100644
--- a/resolv_cache_unit_test.cpp
+++ b/resolv_cache_unit_test.cpp
@@ -25,24 +25,26 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android/multinetwork.h>
+#include <arpa/inet.h>
#include <cutils/properties.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
-#include "netd_resolv/stats.h"
+#include "res_init.h"
#include "resolv_cache.h"
#include "resolv_private.h"
+#include "stats.h"
#include "tests/dns_responder/dns_responder.h"
using namespace std::chrono_literals;
constexpr int TEST_NETID = 30;
constexpr int TEST_NETID_2 = 31;
+constexpr int DNS_PORT = 53;
// Constant values sync'd from res_cache.cpp
constexpr int DNS_HEADER_SIZE = 12;
constexpr int MAX_ENTRIES = 64 * 2 * 5;
-constexpr int MAXPACKET = 8 * 1024;
namespace {
@@ -64,9 +66,10 @@
};
std::vector<char> makeQuery(int op, const char* qname, int qclass, int qtype) {
- res_state res = res_get_state();
uint8_t buf[MAXPACKET] = {};
- const int len = res_nmkquery(res, op, qname, qclass, qtype, NULL, 0, NULL, buf, sizeof(buf));
+ const int len = res_nmkquery(op, qname, qclass, qtype, /*data=*/nullptr, /*datalen=*/0, buf,
+ sizeof(buf),
+ /*netcontext_flags=*/0);
return std::vector<char>(buf, buf + len);
}
@@ -83,7 +86,7 @@
.rclass = question.qclass,
.ttl = ttl,
};
- test::DNSResponder::fillAnswerRdata(rdata_str, record);
+ test::DNSResponder::fillRdata(rdata_str, record);
header.answers.push_back(std::move(record));
}
@@ -97,13 +100,6 @@
return std::time(nullptr);
}
-std::string addrToString(const sockaddr_storage* addr) {
- char out[INET6_ADDRSTRLEN] = {0};
- getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
- NI_NUMERICHOST);
- return std::string(out);
-}
-
// Comparison for res_stats. Simply check the count in the cache test.
bool operator==(const res_stats& a, const res_stats& b) {
return std::tie(a.sample_count, a.sample_next) == std::tie(b.sample_count, b.sample_next);
@@ -194,6 +190,13 @@
return resolv_set_nameservers(netId, setup.servers, setup.domains, setup.params);
}
+ void cacheAddStats(uint32_t netId, int revision_id, const sockaddr* sa,
+ const res_sample& sample, int max_samples) {
+ resolv_cache_add_resolver_stats_sample(netId, revision_id, sa, sample, max_samples);
+ }
+
+ int cacheFlush(uint32_t netId) { return resolv_flush_cache_for_net(netId); }
+
void expectCacheStats(const std::string& msg, uint32_t netId, const CacheStats& expected) {
int nscount = -1;
sockaddr_storage servers[MAXNS];
@@ -221,6 +224,11 @@
EXPECT_TRUE(params == expected.setup.params) << msg;
// res_stats checking.
+ if (expected.stats.size() == 0) {
+ for (int ns = 0; ns < nscount; ns++) {
+ EXPECT_EQ(0U, stats[ns].sample_count) << msg;
+ }
+ }
for (size_t i = 0; i < expected.stats.size(); i++) {
EXPECT_TRUE(stats[i] == expected.stats[i]) << msg;
}
@@ -714,6 +722,117 @@
expectCacheStats("GetStats", TEST_NETID, cacheStats);
}
+TEST_F(ResolvCacheTest, FlushCache) {
+ EXPECT_EQ(0, cacheCreate(TEST_NETID));
+ const SetupParams setup = {
+ .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
+ .domains = {"domain1.com", "domain2.com"},
+ .params = kParams,
+ };
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, setup));
+ EXPECT_TRUE(resolv_has_nameservers(TEST_NETID));
+
+ res_sample sample = {.at = time(NULL), .rtt = 100, .rcode = ns_r_noerror};
+ sockaddr_in sin = {.sin_family = AF_INET, .sin_port = htons(DNS_PORT)};
+ ASSERT_TRUE(inet_pton(AF_INET, setup.servers[0].c_str(), &sin.sin_addr));
+ cacheAddStats(TEST_NETID, 1 /*revision_id*/, (const sockaddr*)&sin, sample,
+ setup.params.max_samples);
+
+ const CacheStats cacheStats = {
+ .setup = setup,
+ .stats = {{{sample}, 1 /*sample_count*/, 1 /*sample_next*/}},
+ .pendingReqTimeoutCount = 0,
+ };
+ expectCacheStats("FlushCache: a record in cache stats", TEST_NETID, cacheStats);
+
+ EXPECT_EQ(0, cacheFlush(TEST_NETID));
+ const CacheStats cacheStats_empty = {
+ .setup = setup,
+ .stats = {},
+ .pendingReqTimeoutCount = 0,
+ };
+ expectCacheStats("FlushCache: no record in cache stats", TEST_NETID, cacheStats_empty);
+}
+
+TEST_F(ResolvCacheTest, GetHostByAddrFromCache_InvalidArgs) {
+ char domain_name[NS_MAXDNAME] = {};
+ const char query_v4[] = "1.2.3.5";
+
+ // invalid buffer size
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME + 1, nullptr,
+ AF_INET));
+ EXPECT_STREQ("", domain_name);
+
+ // invalid query
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, nullptr,
+ AF_INET));
+ EXPECT_STREQ("", domain_name);
+
+ // unsupported AF
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+ AF_UNSPEC));
+ EXPECT_STREQ("", domain_name);
+}
+
+TEST_F(ResolvCacheTest, GetHostByAddrFromCache) {
+ char domain_name[NS_MAXDNAME] = {};
+ const char query_v4[] = "1.2.3.5";
+ const char query_v6[] = "2001:db8::102:304";
+ const char query_v6_unabbreviated[] = "2001:0db8:0000:0000:0000:0000:0102:0304";
+ const char query_v6_mixed[] = "2001:db8::1.2.3.4";
+ const char answer[] = "existent.in.cache";
+
+ // cache does not exist
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+ AF_INET));
+ EXPECT_STREQ("", domain_name);
+
+ // cache is empty
+ EXPECT_EQ(0, cacheCreate(TEST_NETID));
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+ AF_INET));
+ EXPECT_STREQ("", domain_name);
+
+ // no v4 match in cache
+ CacheEntry ce = makeCacheEntry(QUERY, "any.data", ns_c_in, ns_t_a, "1.2.3.4");
+ EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+ AF_INET));
+ EXPECT_STREQ("", domain_name);
+
+ // v4 match
+ ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_a, query_v4);
+ EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+ EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+ AF_INET));
+ EXPECT_STREQ(answer, domain_name);
+
+ // no v6 match in cache
+ memset(domain_name, 0, NS_MAXDNAME);
+ EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
+ AF_INET6));
+ EXPECT_STREQ("", domain_name);
+
+ // v6 match
+ ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_aaaa, query_v6);
+ EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+ EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
+ AF_INET6));
+ EXPECT_STREQ(answer, domain_name);
+
+ // v6 match with unabbreviated address format
+ memset(domain_name, 0, NS_MAXDNAME);
+ EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
+ query_v6_unabbreviated, AF_INET6));
+ EXPECT_STREQ(answer, domain_name);
+
+ // v6 with mixed address format
+ memset(domain_name, 0, NS_MAXDNAME);
+ EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
+ query_v6_mixed, AF_INET6));
+ EXPECT_STREQ(answer, domain_name);
+}
+
namespace {
constexpr int EAI_OK = 0;
@@ -783,7 +902,7 @@
}
}
-// TODO: Tests for struct resolv_cache_info, including:
+// TODO: Tests for NetConfig, including:
// - res_params
// -- resolv_cache_get_resolver_stats()
// - res_stats
diff --git a/resolv_callback_unit_test.cpp b/resolv_callback_unit_test.cpp
new file mode 100644
index 0000000..6de8f45
--- /dev/null
+++ b/resolv_callback_unit_test.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "resolv_callback_unit_test"
+
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+
+#include "DnsResolver.h"
+#include "getaddrinfo.h"
+#include "resolv_cache.h"
+#include "tests/resolv_test_utils.h"
+
+namespace android::net {
+
+using android::net::NetworkDnsEventReported;
+using android::netdutils::ScopedAddrinfo;
+
+// Use maximum reserved appId for applications to avoid conflict with existing uids.
+const int TEST_UID = 99999;
+// Use testUid to make sure TagSocketCallback is called.
+static int testUid = 0;
+
+bool isApiLevelSupported(size_t requiredVersion) {
+ if (android::base::GetUintProperty<uint64_t>("ro.build.version.sdk", 0) >= requiredVersion)
+ return true;
+ else
+ return false;
+}
+
+#define SKIP_IF_APILEVEL_LESS_THAN(version) \
+ do { \
+ if (!isApiLevelSupported(version)) { \
+ GTEST_LOG_(INFO) << "Skip. Required API version: " << version << "\n"; \
+ return; \
+ } \
+ } while (0)
+
+void getNetworkContextCallback(uint32_t, uint32_t, android_net_context*) {
+ // No-op
+}
+
+bool checkCallingPermissionCallback(const char*) {
+ // No-op
+ return true;
+}
+
+void logCallback(const char*) {
+ // No-op
+}
+
+int tagSocketCallback(int, uint32_t, uid_t uid, pid_t) {
+ testUid = uid;
+ return true;
+}
+
+bool evaluateDomainNameCallback(const android_net_context&, const char*) {
+ // No-op
+ return true;
+}
+
+void initDnsResolverCallbacks() {
+ ResolverNetdCallbacks callbacks = {
+ .check_calling_permission = &checkCallingPermissionCallback,
+ .get_network_context = &getNetworkContextCallback,
+ .log = &logCallback,
+ .tagSocket = &tagSocketCallback,
+ .evaluate_domain_name = &evaluateDomainNameCallback,
+ };
+ // It returns fail since socket 'dnsproxyd' has been occupied.
+ // But the callback funtions is configured successfully and can
+ // be tested when running unit test cases.
+ resolv_init(&callbacks);
+}
+
+void resetDnsResolverCallbacks() {
+ ResolverNetdCallbacks callbacks = {
+ .check_calling_permission = nullptr,
+ .get_network_context = nullptr,
+ .log = nullptr,
+ .tagSocket = nullptr,
+ .evaluate_domain_name = nullptr,
+ };
+ resolv_init(&callbacks);
+}
+
+void resetCallbackParams() {
+ testUid = 0;
+}
+
+class CallbackTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ initDnsResolverCallbacks();
+ // Create cache for test
+ android::net::gDnsResolv->resolverCtrl.createNetworkCache(TEST_NETID);
+ }
+
+ void TearDown() override {
+ // Reset related parameters and callback functions.
+ resetCallbackParams();
+ resetDnsResolverCallbacks();
+ // Delete cache for test
+ android::net::gDnsResolv->resolverCtrl.destroyNetworkCache(TEST_NETID);
+ }
+
+ int SetResolvers() {
+ const std::vector<std::string> servers = {test::kDefaultListenAddr};
+ const std::vector<std::string> domains = {"example.com"};
+ const res_params params = {
+ .sample_validity = 300,
+ .success_threshold = 25,
+ .min_samples = 8,
+ .max_samples = 8,
+ .base_timeout_msec = 1000,
+ .retry_count = 2,
+ };
+ return resolv_set_nameservers(TEST_NETID, servers, domains, params);
+ }
+
+ const android_net_context mNetcontext = {
+ .app_netid = TEST_NETID,
+ .app_mark = MARK_UNSET,
+ .dns_netid = TEST_NETID,
+ .dns_mark = MARK_UNSET,
+ .uid = TEST_UID,
+ };
+};
+
+TEST_F(CallbackTest, tagSocketCallback) {
+ // tagSocketCallback is used when ro.build.version.sdk >=30.
+ // If ro.build.version.sdk < 30, don't run this test case.
+ SKIP_IF_APILEVEL_LESS_THAN(30);
+ test::DNSResponder dns;
+ dns.addMapping(kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4);
+ ASSERT_TRUE(dns.startServer());
+ EXPECT_EQ(SetResolvers(), 0);
+
+ addrinfo* result = nullptr;
+ const addrinfo hints = {.ai_family = AF_INET};
+ NetworkDnsEventReported event;
+ // tagSocketCallback will be called.
+ const int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &result, &event);
+ ScopedAddrinfo result_cleanup(result);
+ EXPECT_EQ(testUid, TEST_UID);
+ EXPECT_EQ(rv, 0);
+}
+} // end of namespace android::net
diff --git a/resolv_private.h b/resolv_private.h
index 4c881c2..a062c1a 100644
--- a/resolv_private.h
+++ b/resolv_private.h
@@ -46,25 +46,18 @@
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * @(#)resolv.h 8.1 (Berkeley) 6/2/93
- * Id: resolv.h,v 1.7.2.11.4.2 2004/06/25 00:41:05 marka Exp
- */
-
-#ifndef NETD_RESOLV_PRIVATE_H
-#define NETD_RESOLV_PRIVATE_H
+#pragma once
#include <android-base/logging.h>
#include <net/if.h>
-#include <resolv.h>
#include <time.h>
#include <string>
#include <vector>
#include "DnsResolver.h"
-#include "netd_resolv/params.h"
#include "netd_resolv/resolv.h"
-#include "netd_resolv/stats.h"
+#include "params.h"
+#include "stats.h"
#include "stats.pb.h"
// Linux defines MAXHOSTNAMELEN as 64, while the domain name limit in
@@ -86,10 +79,12 @@
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
+constexpr int MAXPACKET = 8 * 1024;
-struct __res_state {
+struct ResState {
unsigned netid; // NetId: cache key and socket mark
uid_t uid; // uid of the app that sent the DNS lookup
+ pid_t pid; // pid of the app that sent the DNS lookup
int nscount; // number of name srvers
uint16_t id; // current message id
std::vector<std::string> search_domains; // domains to search
@@ -101,9 +96,11 @@
uint32_t _flags; // See RES_F_* defines below
android::net::NetworkDnsEventReported* event;
uint32_t netcontext_flags;
+ int tc_mode;
};
-typedef struct __res_state* res_state;
+// TODO: remove these legacy aliases
+typedef ResState* res_state;
/* Retrieve a local copy of the stats for the given netid. The buffer must have space for
* MAXNS __resolver_stats. Returns the revision id of the resolvers used.
@@ -113,8 +110,8 @@
/* Add a sample to the shared struct for the given netid and server, provided that the
* revision_id of the stored servers has not changed.
*/
-void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
- const res_sample* sample, int max_samples);
+void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, const sockaddr* sa,
+ const res_sample& sample, int max_samples);
// Calculate the round-trip-time from start time t0 and end time t1.
int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0);
@@ -140,33 +137,18 @@
extern const char* const _res_opcodes[];
-/* Things involving an internal (static) resolver context. */
-struct __res_state* res_get_state(void);
-
-int res_hnok(const char*);
-int res_ownok(const char*);
-int res_mailok(const char*);
-int res_dnok(const char*);
-int dn_skipname(const uint8_t*, const uint8_t*);
-void putlong(uint32_t, uint8_t*);
-void putshort(uint16_t, uint8_t*);
-
int res_nameinquery(const char*, int, int, const uint8_t*, const uint8_t*);
int res_queriesmatch(const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*);
int res_nquery(res_state, const char*, int, int, uint8_t*, int, int*);
int res_nsearch(res_state, const char*, int, int, uint8_t*, int, int*);
int res_nquerydomain(res_state, const char*, const char*, int, int, uint8_t*, int, int*);
-int res_nmkquery(res_state, int, const char*, int, int, const uint8_t*, int, const uint8_t*,
- uint8_t*, int);
+int res_nmkquery(int op, const char* qname, int cl, int type, const uint8_t* data, int datalen,
+ uint8_t* buf, int buflen, int netcontext_flags);
int res_nsend(res_state, const uint8_t*, int, uint8_t*, int, int*, uint32_t);
void res_nclose(res_state);
int res_nopt(res_state, int, uint8_t*, int, int);
-struct android_net_context;
-void res_setnetcontext(res_state, const struct android_net_context*,
- android::net::NetworkDnsEventReported* event);
-
int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
addrinfo** result);
@@ -188,9 +170,9 @@
android::net::IpVersion ipFamilyToIPVersion(int ipFamily);
-inline void resolv_tag_socket(int sock, uid_t uid) {
+inline void resolv_tag_socket(int sock, uid_t uid, pid_t pid) {
if (android::net::gResNetdCallbacks.tagSocket != nullptr) {
- if (int err = android::net::gResNetdCallbacks.tagSocket(sock, TAG_SYSTEM_DNS, uid)) {
+ if (int err = android::net::gResNetdCallbacks.tagSocket(sock, TAG_SYSTEM_DNS, uid, pid)) {
LOG(WARNING) << "Failed to tag socket: " << strerror(-err);
}
}
@@ -200,4 +182,9 @@
}
}
-#endif // NETD_RESOLV_PRIVATE_H
+inline std::string addrToString(const sockaddr_storage* addr) {
+ char out[INET6_ADDRSTRLEN] = {0};
+ getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
+ NI_NUMERICHOST);
+ return std::string(out);
+}
diff --git a/resolv_tls_unit_test.cpp b/resolv_tls_unit_test.cpp
index 5d3ca26..ed9c218 100644
--- a/resolv_tls_unit_test.cpp
+++ b/resolv_tls_unit_test.cpp
@@ -148,6 +148,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(QUERY, r.response);
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// Fake Socket that echoes the observed query ID as the response body.
@@ -188,6 +189,7 @@
// after each use.
EXPECT_EQ(0, (r.response[2] << 8) | r.response[3]);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// These queries might be handled in serial or parallel as they race the
@@ -207,6 +209,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(QUERY, r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// A server that waits until sDelay queries are queued before responding.
@@ -272,6 +275,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(QUERY, r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
TEST_F(TransportTest, ParallelColliding_Max) {
@@ -291,6 +295,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(QUERY, r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
TEST_F(TransportTest, ParallelUnique) {
@@ -310,6 +315,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(queries[i], r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
TEST_F(TransportTest, ParallelUnique_Max) {
@@ -331,6 +337,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(queries[i], r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
TEST_F(TransportTest, IdExhaustion) {
@@ -358,6 +365,7 @@
EXPECT_EQ(std::future_status::timeout,
result.wait_for(std::chrono::duration<int>::zero()));
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// Responses can come back from the server in any order. This should have no
@@ -379,6 +387,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(queries[i], r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
TEST_F(TransportTest, ReverseOrder_Max) {
@@ -398,6 +407,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(queries[i], r.response);
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// Returning null from the factory indicates a connection failure.
@@ -420,6 +430,7 @@
EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
EXPECT_TRUE(r.response.empty());
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// Simulate a socket that connects but then immediately receives a server
@@ -445,6 +456,9 @@
EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
EXPECT_TRUE(r.response.empty());
+
+ // Reconnections are triggered since DnsTlsQueryMap is not empty.
+ EXPECT_EQ(transport.getConnectCounter(), DnsTlsQueryMap::kMaxTries);
}
// Simulate a server that occasionally closes the connection and silently
@@ -537,6 +551,9 @@
EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
EXPECT_TRUE(r.response.empty());
}
+
+ // Reconnections are triggered since DnsTlsQueryMap is not empty.
+ EXPECT_EQ(transport.getConnectCounter(), DnsTlsQueryMap::kMaxTries);
}
TEST_F(TransportTest, PartialDrop) {
@@ -561,6 +578,33 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
EXPECT_EQ(queries[i], r.response);
}
+
+ // TODO: transport.getConnectCounter() seems not stable in this test. Find how to check the
+ // connect attempts for this test.
+}
+
+TEST_F(TransportTest, ConnectCounter) {
+ FakeSocketLimited::sLimit = 2; // Close the socket after 2 queries.
+ FakeSocketLimited::sMaxSize = SIZE; // No query drops.
+ FakeSocketFactory<FakeSocketLimited> factory;
+ DnsTlsTransport transport(SERVER1, MARK, &factory);
+
+ // Connecting on demand.
+ EXPECT_EQ(transport.getConnectCounter(), 0);
+
+ const int num_queries = 10;
+ std::vector<std::future<DnsTlsTransport::Result>> results;
+ results.reserve(num_queries);
+ for (int i = 0; i < num_queries; i++) {
+ // Reconnections take place every two queries.
+ results.push_back(transport.query(makeSlice(QUERY)));
+ }
+ for (int i = 0; i < num_queries; i++) {
+ auto r = results[i].get();
+ EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+ }
+
+ EXPECT_EQ(transport.getConnectCounter(), num_queries / FakeSocketLimited::sLimit);
}
// Simulate a malfunctioning server that injects extra miscellaneous
@@ -604,6 +648,7 @@
EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
// Don't check the response because this server is malfunctioning.
}
+ EXPECT_EQ(transport.getConnectCounter(), 1);
}
// Dispatcher tests
@@ -612,28 +657,38 @@
TEST_F(DispatcherTest, Query) {
bytevec ans(4096);
int resplen = 0;
+ bool connectTriggered = false;
auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
DnsTlsDispatcher dispatcher(std::move(factory));
- auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
- makeSlice(ans), &resplen);
+ auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+ &connectTriggered);
EXPECT_EQ(DnsTlsTransport::Response::success, r);
EXPECT_EQ(int(QUERY.size()), resplen);
+ EXPECT_TRUE(connectTriggered);
ans.resize(resplen);
EXPECT_EQ(QUERY, ans);
+
+ // Expect to reuse the connection.
+ r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+ &connectTriggered);
+ EXPECT_EQ(DnsTlsTransport::Response::success, r);
+ EXPECT_FALSE(connectTriggered);
}
TEST_F(DispatcherTest, AnswerTooLarge) {
bytevec ans(SIZE - 1); // Too small to hold the answer
int resplen = 0;
+ bool connectTriggered = false;
auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
DnsTlsDispatcher dispatcher(std::move(factory));
- auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
- makeSlice(ans), &resplen);
+ auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+ &connectTriggered);
EXPECT_EQ(DnsTlsTransport::Response::limit_error, r);
+ EXPECT_TRUE(connectTriggered);
}
template<class T>
@@ -678,10 +733,11 @@
auto q = make_query(i, SIZE);
bytevec ans(4096);
int resplen = 0;
+ bool connectTriggered = false;
unsigned mark = key.first;
const DnsTlsServer& server = key.second;
- auto r = dispatcher->query(server, mark, makeSlice(q),
- makeSlice(ans), &resplen);
+ auto r = dispatcher->query(server, mark, makeSlice(q), makeSlice(ans), &resplen,
+ &connectTriggered);
EXPECT_EQ(DnsTlsTransport::Response::success, r);
EXPECT_EQ(int(q.size()), resplen);
ans.resize(resplen);
diff --git a/resolv_unit_test.cpp b/resolv_unit_test.cpp
index 3295cac..31d7bf1 100644
--- a/resolv_unit_test.cpp
+++ b/resolv_unit_test.cpp
@@ -22,6 +22,7 @@
#include <gtest/gtest.h>
#include <netdb.h>
#include <netdutils/InternetAddresses.h>
+#include <resolv_stats_test_utils.h>
#include "dns_responder.h"
#include "getaddrinfo.h"
@@ -42,6 +43,10 @@
// The buffer size of resolv_gethostbyname().
constexpr unsigned int MAXPACKET = 8 * 1024;
+// Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates
+// that any type or protocol can be returned by getaddrinfo().
+constexpr unsigned int ANY = 0;
+
class TestBase : public ::testing::Test {
protected:
struct DnsMessage {
@@ -67,7 +72,7 @@
.rclass = rclass,
.ttl = ttl,
};
- EXPECT_TRUE(test::DNSResponder::fillAnswerRdata(rdata, record));
+ EXPECT_TRUE(test::DNSResponder::fillRdata(rdata, record));
return record;
}
@@ -387,6 +392,62 @@
TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname_NoData) {
constexpr char v4_host_name[] = "v4only.example.com.";
+ // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+ // So don't need to config those values: event_type, return_code, latency_micros,
+ // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+ constexpr char event_ipv6[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ },
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ },
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ },
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ }
+ ]
+ }
+ })Event";
+
test::DNSResponder dns;
dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
ASSERT_TRUE(dns.startServer());
@@ -397,6 +458,7 @@
const addrinfo hints = {.ai_family = AF_INET6};
NetworkDnsEventReported event;
int rv = resolv_getaddrinfo("v4only", nullptr, &hints, &mNetcontext, &result, &event);
+ EXPECT_THAT(event, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event_ipv6)));
ScopedAddrinfo result_cleanup(result);
EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
EXPECT_EQ(nullptr, result);
@@ -407,7 +469,66 @@
constexpr char host_name[] = "sawadee.example.com.";
constexpr char v4addr[] = "1.2.3.4";
constexpr char v6addr[] = "::1.2.3.4";
+ // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+ // So don't need to config those values: event_type, return_code, latency_micros,
+ // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+ constexpr char event_ipv4[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 0,
+ type: 1,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ },
+ {
+ rcode: 0,
+ type: 1,
+ cache_hit: 2,
+ ip_version: 0,
+ protocol: 0,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ }
+ ]
+ }
+ })Event";
+ constexpr char event_ipv6[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ },
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 2,
+ ip_version: 0,
+ protocol: 0,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ }
+ ]
+ }
+ })Event";
test::DNSResponder dns;
dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
@@ -417,9 +538,10 @@
static const struct TestConfig {
int ai_family;
const std::string expected_addr;
+ const std::string expected_event;
} testConfigs[]{
- {AF_INET, v4addr},
- {AF_INET6, v6addr},
+ {AF_INET, v4addr, event_ipv4},
+ {AF_INET6, v6addr, event_ipv6},
};
for (const auto& config : testConfigs) {
@@ -430,6 +552,8 @@
const addrinfo hints = {.ai_family = config.ai_family};
NetworkDnsEventReported event;
int rv = resolv_getaddrinfo("sawadee", nullptr, &hints, &mNetcontext, &result, &event);
+ EXPECT_THAT(event,
+ NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
ScopedAddrinfo result_cleanup(result);
EXPECT_EQ(0, rv);
EXPECT_TRUE(result != nullptr);
@@ -690,7 +814,48 @@
constexpr char host_name[] = "jiababuei.example.com.";
constexpr char v4addr[] = "1.2.3.4";
constexpr char v6addr[] = "::1.2.3.4";
+ // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+ // So don't need to config those values: event_type, return_code, latency_micros,
+ // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+ constexpr char event_ipv4[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 0,
+ type: 1,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ }
+ ]
+ }
+ })Event";
+ constexpr char event_ipv6[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 0,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 0,
+ dns_server_index: 0,
+ connected: 0,
+ latency_micros: 0,
+ }
+ ]
+ }
+ })Event";
test::DNSResponder dns;
dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
@@ -700,9 +865,10 @@
static const struct TestConfig {
int ai_family;
const std::string expected_addr;
+ const std::string expected_event;
} testConfigs[]{
- {AF_INET, v4addr},
- {AF_INET6, v6addr},
+ {AF_INET, v4addr, event_ipv4},
+ {AF_INET6, v6addr, event_ipv6},
};
for (const auto& config : testConfigs) {
@@ -715,6 +881,8 @@
NetworkDnsEventReported event;
int rv = resolv_gethostbyname("jiababuei", config.ai_family, &hbuf, tmpbuf, sizeof(tmpbuf),
&mNetcontext, &hp, &event);
+ EXPECT_THAT(event,
+ NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
EXPECT_EQ(0, rv);
EXPECT_TRUE(hp != nullptr);
EXPECT_EQ(1U, GetNumQueries(dns, host_name));
diff --git a/stats.h b/stats.h
new file mode 100644
index 0000000..cc7de9f
--- /dev/null
+++ b/stats.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include "params.h"
+
+#define RCODE_INTERNAL_ERROR 254
+#define RCODE_TIMEOUT 255
+
+struct res_sample {
+ time_t at; // time in s at which the sample was recorded
+ uint16_t rtt; // round-trip time in ms
+ uint8_t rcode; // the DNS rcode or RCODE_XXX defined above
+};
+
+// Resolver reachability statistics and run-time parameters.
+struct res_stats {
+ // Stats of the last <sample_count> queries.
+ res_sample samples[MAXNSSAMPLES];
+ // The number of samples stored.
+ uint8_t sample_count;
+ // The next sample to modify.
+ uint8_t sample_next;
+};
+
+// Aggregates the reachability statistics for the given server based on on the stored samples.
+void android_net_res_stats_aggregate(res_stats* stats, int* successes, int* errors, int* timeouts,
+ int* internal_errors, int* rtt_avg, time_t* last_sample_time);
+
+int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
+ sockaddr_storage servers[MAXNS], int* dcount,
+ char domains[MAXDNSRCH][MAXDNSRCHPATH],
+ res_params* params, res_stats stats[MAXNS],
+ int* wait_for_pending_req_timeout_count);
+
+// Returns an array of bools indicating which servers are considered good
+int android_net_res_stats_get_usable_servers(const res_params* params, res_stats stats[],
+ int nscount, bool valid_servers[]);
diff --git a/tests/Android.bp b/tests/Android.bp
index f59c371..fa2ea31 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -6,7 +6,6 @@
],
header_libs: [
"libnetd_resolv_headers",
- "libnetd_resolv_internal_headers",
],
shared_libs: [
"libbase",
@@ -15,13 +14,60 @@
static_libs: [
"libnetd_test_dnsresponder",
"libnetdutils",
- "dnsresolver_aidl_interface-V2-cpp",
+ ],
+}
+
+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", "mts"],
+ require_root: true,
+ defaults: ["netd_defaults"],
+ data: ["testdata/*.pbtxt"],
+ srcs: [
+ "resolv_gold_test.cpp",
+ ],
+ header_libs: [
+ "libnetd_client_headers",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ "libprotobuf-cpp-full",
+ "libssl",
+ ],
+ static_libs: [
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "golddata_proto",
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libnetd_resolv",
+ "libnetd_test_dnsresponder_ndk",
+ "libnetd_test_resolv_utils",
+ "libnetdutils",
+ "netd_aidl_interface-ndk_platform",
+ "netd_event_listener_interface-ndk_platform",
+ "server_configurable_flags",
+ "stats_proto",
+ ],
+ compile_multilib: "first",
+}
+
+cc_test {
name: "resolv_stress_test",
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
// This won't work with test_config
// require_root: true,
test_config: "resolv_stress_test_config.xml",
@@ -31,26 +77,104 @@
],
header_libs: [
"libnetd_resolv_headers",
- "libnetd_resolv_internal_headers",
],
shared_libs: [
"libbase",
- "libbinder",
+ "libbinder_ndk",
"libnetd_client",
"libutils",
],
static_libs: [
+ "dnsresolver_aidl_interface-ndk_platform",
"libgmock",
- "libnetd_test_dnsresponder",
+ "libnetd_test_dnsresponder_ndk",
"libnetd_test_resolv_utils",
"libnetdutils",
- "netd_aidl_interface-V2-cpp",
- "netd_event_listener_interface-cpp",
- "dnsresolver_aidl_interface-V2-cpp",
+ "netd_event_listener_interface-ndk_platform",
+ "netd_aidl_interface-ndk_platform",
+ ],
+ compile_multilib: "first",
+}
+
+cc_test {
+ name: "resolv_integration_test",
+ test_suites: ["device-tests", "mts"],
+ require_root: true,
+ defaults: ["netd_defaults"],
+ tidy: false, // cuts test build time by > 1m30s
+ srcs: [
+ "dns_responder/dns_responder.cpp",
+ "dnsresolver_binder_test.cpp",
+ "resolv_integration_test.cpp",
+ ],
+ header_libs: [
+ "libnetd_resolv_headers",
+ ],
+ // TODO: make static link libc++ work.
+ //stl: "libc++_static",
+ shared_libs: [
+ "libbinder_ndk",
+ "libnetd_client",
+ ],
+ static_libs: [
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "libbase",
+ "libbpf_android",
+ "libcrypto_static",
+ "libgmock",
+ "liblog",
+ "libnetd_test_dnsresponder_ndk",
+ "libnetd_test_metrics_listener",
+ "libnetd_test_resolv_utils",
+ "libnetd_test_tun_interface",
+ "libnetd_test_utils",
+ "libnetdutils",
+ "libssl",
+ "libutils",
+ "netd_aidl_interface-ndk_platform",
+ "netd_event_listener_interface-ndk_platform",
],
compile_multilib: "both",
- sanitize: {
- address: true,
- recover: ["all"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
},
}
+
+cc_test_library {
+ name: "resolv_stats_test_utils",
+ srcs: [
+ "resolv_stats_test_utils.cpp"
+ ],
+ defaults: ["netd_defaults"],
+ export_include_dirs: ["."],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libnetdutils",
+ "libprotobuf-cpp-lite",
+ "stats_proto",
+ ],
+}
+
+cc_test {
+ name: "resolv_stats_test_utils_test",
+ srcs: [
+ "resolv_stats_test_utils_test.cpp",
+ ],
+ defaults: ["netd_defaults"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "resolv_stats_test_utils",
+ "stats_proto",
+ ],
+}
diff --git a/tests/dns_metrics_listener/Android.bp b/tests/dns_metrics_listener/Android.bp
index f19ca88..8a957d6 100644
--- a/tests/dns_metrics_listener/Android.bp
+++ b/tests/dns_metrics_listener/Android.bp
@@ -7,11 +7,11 @@
"test_metrics.cpp",
],
shared_libs: [
- "libbase",
- "libbinder",
- "libutils",
+ "libbinder_ndk",
],
static_libs: [
- "netd_event_listener_interface-V1-cpp",
+ "libbase",
+ "libutils",
+ "netd_event_listener_interface-ndk_platform",
],
}
diff --git a/tests/dns_metrics_listener/base_metrics_listener.cpp b/tests/dns_metrics_listener/base_metrics_listener.cpp
index e6dbb32..c46f093 100644
--- a/tests/dns_metrics_listener/base_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/base_metrics_listener.cpp
@@ -20,51 +20,51 @@
namespace net {
namespace metrics {
-android::binder::Status BaseMetricsListener::onDnsEvent(
+::ndk::ScopedAStatus BaseMetricsListener::onDnsEvent(
int32_t /*netId*/, int32_t /*eventType*/, int32_t /*returnCode*/, int32_t /*latencyMs*/,
- const std::string& /*hostname*/, const ::std::vector<std::string>& /*ipAddresses*/,
+ const std::string& /*hostname*/, const std::vector<std::string>& /*ipAddresses*/,
int32_t /*ipAddressesCount*/, int32_t /*uid*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
-android::binder::Status BaseMetricsListener::onPrivateDnsValidationEvent(
- int32_t /*netId*/, const ::android::String16& /*ipAddress*/,
- const ::android::String16& /*hostname*/, bool /*validated*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onPrivateDnsValidationEvent(
+ int32_t /*netId*/, const std::string& /*ipAddress*/, const std::string& /*hostname*/,
+ bool /*validated*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
-android::binder::Status BaseMetricsListener::onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
- int32_t /*latencyMs*/,
- const ::android::String16& /*ipAddr*/,
- int32_t /*port*/, int32_t /*uid*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
+ int32_t /*latencyMs*/,
+ const std::string& /*ipAddr*/,
+ int32_t /*port*/, int32_t /*uid*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
-android::binder::Status BaseMetricsListener::onWakeupEvent(
- const ::android::String16& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
- int32_t /*ipNextHeader*/, const ::std::vector<uint8_t>& /*dstHw*/,
- const ::android::String16& /*srcIp*/, const ::android::String16& /*dstIp*/,
- int32_t /*srcPort*/, int32_t /*dstPort*/, int64_t /*timestampNs*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onWakeupEvent(
+ const std::string& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
+ int32_t /*ipNextHeader*/, const std::vector<int8_t>& /*dstHw*/,
+ const std::string& /*srcIp*/, const std::string& /*dstIp*/, int32_t /*srcPort*/,
+ int32_t /*dstPort*/, int64_t /*timestampNs*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
-android::binder::Status BaseMetricsListener::onTcpSocketStatsEvent(
- const ::std::vector<int32_t>& /*networkIds*/, const ::std::vector<int32_t>& /*sentPackets*/,
- const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
- const ::std::vector<int32_t>& /*sentAckDiffMs*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onTcpSocketStatsEvent(
+ const std::vector<int32_t>& /*networkIds*/, const std::vector<int32_t>& /*sentPackets*/,
+ const std::vector<int32_t>& /*lostPackets*/, const std::vector<int32_t>& /*rttUs*/,
+ const std::vector<int32_t>& /*sentAckDiffMs*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
-android::binder::Status BaseMetricsListener::onNat64PrefixEvent(
- int32_t /*netId*/, bool /*added*/, const ::std::string& /*prefixString*/,
- int32_t /*prefixLength*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
+ const std::string& /*prefixString*/,
+ int32_t /*prefixLength*/) {
// default no-op
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
} // namespace metrics
diff --git a/tests/dns_metrics_listener/base_metrics_listener.h b/tests/dns_metrics_listener/base_metrics_listener.h
index 9ba8423..d50997d 100644
--- a/tests/dns_metrics_listener/base_metrics_listener.h
+++ b/tests/dns_metrics_listener/base_metrics_listener.h
@@ -19,43 +19,44 @@
#include <string>
#include <vector>
-#include "android/net/metrics/BnNetdEventListener.h"
+#include <aidl/android/net/metrics/BnNetdEventListener.h>
namespace android {
namespace net {
namespace metrics {
-class BaseMetricsListener : public BnNetdEventListener {
+class BaseMetricsListener : public aidl::android::net::metrics::BnNetdEventListener {
public:
BaseMetricsListener() = default;
~BaseMetricsListener() = default;
- virtual android::binder::Status onDnsEvent(int32_t /*netId*/, int32_t /*eventType*/,
- int32_t /*returnCode*/, int32_t /*latencyMs*/,
- const std::string& /*hostname*/,
- const ::std::vector<std::string>& /*ipAddresses*/,
- int32_t /*ipAddressesCount*/,
- int32_t /*uid*/) override;
- virtual android::binder::Status onPrivateDnsValidationEvent(
- int32_t /*netId*/, const ::android::String16& /*ipAddress*/,
- const ::android::String16& /*hostname*/, bool /*validated*/) override;
- virtual android::binder::Status onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
- int32_t /*latencyMs*/,
- const ::android::String16& /*ipAddr*/,
- int32_t /*port*/, int32_t /*uid*/) override;
- virtual android::binder::Status onWakeupEvent(
- const ::android::String16& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
- int32_t /*ipNextHeader*/, const ::std::vector<uint8_t>& /*dstHw*/,
- const ::android::String16& /*srcIp*/, const ::android::String16& /*dstIp*/,
- int32_t /*srcPort*/, int32_t /*dstPort*/, int64_t /*timestampNs*/) override;
- virtual android::binder::Status onTcpSocketStatsEvent(
- const ::std::vector<int32_t>& /*networkIds*/,
- const ::std::vector<int32_t>& /*sentPackets*/,
- const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
- const ::std::vector<int32_t>& /*sentAckDiffMs*/) override;
- virtual android::binder::Status onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
- const ::std::string& /*prefixString*/,
- int32_t /*prefixLength*/) override;
+ virtual ::ndk::ScopedAStatus onDnsEvent(int32_t /*netId*/, int32_t /*eventType*/,
+ int32_t /*returnCode*/, int32_t /*latencyMs*/,
+ const std::string& /*hostname*/,
+ const std::vector<std::string>& /*ipAddresses*/,
+ int32_t /*ipAddressesCount*/, int32_t /*uid*/) override;
+ virtual ::ndk::ScopedAStatus onPrivateDnsValidationEvent(int32_t /*netId*/,
+ const std::string& /*ipAddress*/,
+ const std::string& /*hostname*/,
+ bool /*validated*/) override;
+ virtual ::ndk::ScopedAStatus onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
+ int32_t /*latencyMs*/,
+ const std::string& /*ipAddr*/, int32_t /*port*/,
+ int32_t /*uid*/) override;
+ virtual ::ndk::ScopedAStatus onWakeupEvent(const std::string& /*prefix*/, int32_t /*uid*/,
+ int32_t /*ethertype*/, int32_t /*ipNextHeader*/,
+ const std::vector<int8_t>& /*dstHw*/,
+ const std::string& /*srcIp*/,
+ const std::string& /*dstIp*/, int32_t /*srcPort*/,
+ int32_t /*dstPort*/,
+ int64_t /*timestampNs*/) override;
+ virtual ::ndk::ScopedAStatus onTcpSocketStatsEvent(
+ const std::vector<int32_t>& /*networkIds*/, const std::vector<int32_t>& /*sentPackets*/,
+ const std::vector<int32_t>& /*lostPackets*/, const std::vector<int32_t>& /*rttUs*/,
+ const std::vector<int32_t>& /*sentAckDiffMs*/) override;
+ virtual ::ndk::ScopedAStatus onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
+ const std::string& /*prefixString*/,
+ int32_t /*prefixLength*/) override;
};
} // namespace metrics
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.cpp b/tests/dns_metrics_listener/dns_metrics_listener.cpp
index acfb416..18c98d2 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/dns_metrics_listener.cpp
@@ -29,26 +29,24 @@
constexpr milliseconds kRetryIntervalMs{20};
constexpr milliseconds kEventTimeoutMs{5000};
-android::binder::Status DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
- const std::string& prefixString,
- int32_t /*prefixLength*/) {
+::ndk::ScopedAStatus DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
+ const std::string& prefixString,
+ int32_t /*prefixLength*/) {
std::lock_guard lock(mMutex);
if (netId == mNetId) mNat64Prefix = added ? prefixString : "";
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
}
-android::binder::Status DnsMetricsListener::onPrivateDnsValidationEvent(
- int32_t netId, const ::android::String16& ipAddress,
- const ::android::String16& /*hostname*/, bool validated) {
+::ndk::ScopedAStatus DnsMetricsListener::onPrivateDnsValidationEvent(
+ int32_t netId, const std::string& ipAddress, const std::string& /*hostname*/,
+ bool validated) {
{
std::lock_guard lock(mMutex);
- std::string serverAddr(String8(ipAddress.string()));
-
// keep updating the server to have latest validation status.
- mValidationRecords.insert_or_assign({netId, serverAddr}, validated);
+ mValidationRecords.insert_or_assign({netId, ipAddress}, validated);
}
mCv.notify_one();
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
}
bool DnsMetricsListener::waitForNat64Prefix(ExpectNat64PrefixStatus status,
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.h b/tests/dns_metrics_listener/dns_metrics_listener.h
index d933b13..d6268e2 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.h
+++ b/tests/dns_metrics_listener/dns_metrics_listener.h
@@ -42,14 +42,13 @@
DnsMetricsListener(int32_t netId) : mNetId(netId){};
// Override DNS metrics event(s).
- android::binder::Status onNat64PrefixEvent(int32_t netId, bool added,
- const std::string& prefixString,
- int32_t /*prefixLength*/) override;
+ ::ndk::ScopedAStatus onNat64PrefixEvent(int32_t netId, bool added,
+ const std::string& prefixString,
+ int32_t /*prefixLength*/) override;
- android::binder::Status onPrivateDnsValidationEvent(int32_t netId,
- const ::android::String16& ipAddress,
- const ::android::String16& /*hostname*/,
- bool validated) override;
+ ::ndk::ScopedAStatus onPrivateDnsValidationEvent(int32_t netId, const std::string& ipAddress,
+ const std::string& /*hostname*/,
+ bool validated) override;
// Wait for expected NAT64 prefix status until timeout.
bool waitForNat64Prefix(ExpectNat64PrefixStatus status,
diff --git a/tests/dns_metrics_listener/test_metrics.cpp b/tests/dns_metrics_listener/test_metrics.cpp
index 094c3a8..036abe8 100644
--- a/tests/dns_metrics_listener/test_metrics.cpp
+++ b/tests/dns_metrics_listener/test_metrics.cpp
@@ -33,11 +33,11 @@
}
// Derived class TestOnDnsEvent.
-android::binder::Status TestOnDnsEvent::onDnsEvent(int32_t netId, int32_t eventType,
- int32_t returnCode, int32_t /*latencyMs*/,
- const std::string& hostname,
- const ::std::vector<std::string>& ipAddresses,
- int32_t ipAddressesCount, int32_t /*uid*/) {
+::ndk::ScopedAStatus TestOnDnsEvent::onDnsEvent(int32_t netId, int32_t eventType,
+ int32_t returnCode, int32_t /*latencyMs*/,
+ const std::string& hostname,
+ const ::std::vector<std::string>& ipAddresses,
+ int32_t ipAddressesCount, int32_t /*uid*/) {
// A bitwise-OR combination of all expected test cases.
// Currently, the maximum number of test case is 32 because a 32-bits bitwise-OR combination
// is used for checking and recording verified test cases.
@@ -77,7 +77,7 @@
notify();
}
- return android::binder::Status::ok();
+ return ::ndk::ScopedAStatus::ok();
};
} // namespace metrics
diff --git a/tests/dns_metrics_listener/test_metrics.h b/tests/dns_metrics_listener/test_metrics.h
index d098aca..03c9842 100644
--- a/tests/dns_metrics_listener/test_metrics.h
+++ b/tests/dns_metrics_listener/test_metrics.h
@@ -83,10 +83,10 @@
bool isVerified() override { return (getVerified() & EventFlag::onDnsEvent) != 0; }
// Override for testing verification.
- android::binder::Status onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
- int32_t /*latencyMs*/, const std::string& hostname,
- const std::vector<std::string>& ipAddresses,
- int32_t ipAddressesCount, int32_t /*uid*/) override;
+ ::ndk::ScopedAStatus onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
+ int32_t /*latencyMs*/, const std::string& hostname,
+ const std::vector<std::string>& ipAddresses,
+ int32_t ipAddressesCount, int32_t /*uid*/) override;
private:
const std::vector<TestResult>& mResults; // Expected results for test verification.
diff --git a/tests/dns_responder/Android.bp b/tests/dns_responder/Android.bp
index 0f07088..c1e785f 100644
--- a/tests/dns_responder/Android.bp
+++ b/tests/dns_responder/Android.bp
@@ -1,14 +1,15 @@
+// TODO: Remove libnetd_test_dnsresponder after eliminating all users.
cc_library_static {
name: "libnetd_test_dnsresponder",
defaults: ["netd_defaults"],
shared_libs: [
- "dnsresolver_aidl_interface-cpp",
+ "dnsresolver_aidl_interface-unstable-cpp",
"libbase",
"libbinder",
"libnetd_client",
"libnetdutils",
"libssl",
- "netd_aidl_interface-V2-cpp",
+ "netd_aidl_interface-cpp",
],
srcs: [
"dns_responder.cpp",
@@ -17,3 +18,35 @@
],
export_include_dirs: ["."],
}
+
+cc_library {
+ name: "libnetd_test_dnsresponder_ndk",
+ defaults: ["netd_defaults"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libnetd_client",
+ ],
+ header_libs: [
+ "libnetd_resolv_headers",
+ "libnetd_resolv_internal_headers",
+ ],
+ export_header_lib_headers: [
+ "libnetd_resolv_internal_headers",
+ ],
+ static_libs: [
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "libbase",
+ "libcrypto_static",
+ "liblog",
+ "libnetdutils",
+ "libssl",
+ "netd_event_listener_interface-ndk_platform",
+ "netd_aidl_interface-ndk_platform",
+ ],
+ srcs: [
+ "dns_responder.cpp",
+ "dns_responder_client_ndk.cpp",
+ "dns_tls_frontend.cpp",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/tests/dns_responder/dns_responder.cpp b/tests/dns_responder/dns_responder.cpp
index 072c2dd..62b783c 100644
--- a/tests/dns_responder/dns_responder.cpp
+++ b/tests/dns_responder/dns_responder.cpp
@@ -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 {
@@ -139,6 +141,17 @@
return it->second;
}
+const char* dnsproto2str(int protocol) {
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return "TCP";
+ case IPPROTO_UDP:
+ return "UDP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
const char* DNSName::read(const char* buffer, const char* buffer_end) {
const char* cur = buffer;
bool last = false;
@@ -402,6 +415,15 @@
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,58 +468,79 @@
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_.try_emplace({name, type}, header);
+ dnsheader_mappings_[{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";
}
- 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";
}
- 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);
+ }
+}
+
+// Set response probability on all supported protocols.
void DNSResponder::setResponseProbability(double response_probability) {
- response_probability_ = response_probability;
+ setResponseProbability(response_probability, IPPROTO_TCP);
+ setResponseProbability(response_probability, IPPROTO_UDP);
+}
+
+// Set response probability on specific protocol. It's caller's duty to ensure that the |protocol|
+// can be supported by DNSResponder.
+void DNSResponder::setResponseProbability(double response_probability, int protocol) {
+ switch (protocol) {
+ case IPPROTO_TCP:
+ response_probability_tcp_ = response_probability;
+ break;
+ case IPPROTO_UDP:
+ response_probability_udp_ = response_probability;
+ break;
+ default:
+ LOG(FATAL) << "Unsupported protocol " << protocol; // abort() by log level FATAL
+ }
+}
+
+double DNSResponder::getResponseProbability(int protocol) const {
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return response_probability_tcp_;
+ case IPPROTO_UDP:
+ return response_probability_udp_;
+ default:
+ LOG(FATAL) << "Unsupported protocol " << protocol; // abort() by log level FATAL
+ // unreachable
+ return -1;
+ }
}
void DNSResponder::setEdns(Edns edns) {
@@ -505,7 +548,7 @@
}
bool DNSResponder::running() const {
- return socket_.get() != -1;
+ return (udp_socket_.ok()) && (tcp_socket_.ok());
}
bool DNSResponder::startServer() {
@@ -514,59 +557,51 @@
return false;
}
- // Set up UDP socket.
- addrinfo ai_hints{
- .ai_flags = AI_PASSIVE,
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_DGRAM,
- };
- addrinfo* ai_res = nullptr;
- int rv = getaddrinfo(listen_address_.c_str(), listen_service_.c_str(), &ai_hints, &ai_res);
- ScopedAddrinfo ai_res_cleanup(ai_res);
- if (rv) {
- LOG(ERROR) << "getaddrinfo(" << listen_address_ << ", " << listen_service_
- << ") failed: " << gai_strerror(rv);
+ // Create UDP, TCP socket
+ if (udp_socket_ = createListeningSocket(SOCK_DGRAM); udp_socket_.get() < 0) {
+ PLOG(ERROR) << "failed to create UDP socket";
return false;
}
- for (const addrinfo* ai = ai_res; ai; ai = ai->ai_next) {
- socket_.reset(socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol));
- if (socket_.get() < 0) {
- PLOG(INFO) << "ignore creating socket " << socket_.get() << " failed";
- continue;
- }
- enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
- enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
- std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
- if (bind(socket_.get(), ai->ai_addr, ai->ai_addrlen)) {
- LOG(INFO) << "failed to bind UDP " << host_str << ":" << listen_service_;
- continue;
- }
- LOG(INFO) << "bound to UDP " << host_str << ":" << listen_service_;
- break;
+
+ if (tcp_socket_ = createListeningSocket(SOCK_STREAM); tcp_socket_.get() < 0) {
+ PLOG(ERROR) << "failed to create TCP socket";
+ return false;
+ }
+
+ if (listen(tcp_socket_.get(), 1) < 0) {
+ PLOG(ERROR) << "failed to listen TCP socket";
+ return false;
}
// Set up eventfd socket.
event_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
if (event_fd_.get() == -1) {
- PLOG(ERROR) << "failed to create eventfd " << event_fd_.get();
+ PLOG(ERROR) << "failed to create eventfd";
return false;
}
// Set up epoll socket.
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd_.get() < 0) {
- PLOG(ERROR) << "epoll_create1() failed on fd " << epoll_fd_.get();
+ PLOG(ERROR) << "epoll_create1() failed on fd";
return false;
}
- LOG(INFO) << "adding socket " << socket_.get() << " to epoll";
- if (!addFd(socket_.get(), EPOLLIN)) {
- LOG(ERROR) << "failed to add the socket " << socket_.get() << " to epoll";
+ LOG(INFO) << "adding UDP socket to epoll";
+ if (!addFd(udp_socket_.get(), EPOLLIN)) {
+ LOG(ERROR) << "failed to add the UDP socket to epoll";
return false;
}
- LOG(INFO) << "adding eventfd " << event_fd_.get() << " to epoll";
+
+ LOG(INFO) << "adding TCP socket to epoll";
+ if (!addFd(tcp_socket_.get(), EPOLLIN)) {
+ LOG(ERROR) << "failed to add the TCP socket to epoll";
+ return false;
+ }
+
+ LOG(INFO) << "adding eventfd to epoll";
if (!addFd(event_fd_.get(), EPOLLIN)) {
- LOG(ERROR) << "failed to add the eventfd " << event_fd_.get() << " to epoll";
+ LOG(ERROR) << "failed to add the eventfd to epoll";
return false;
}
@@ -590,12 +625,14 @@
}
handler_thread_.join();
epoll_fd_.reset();
- socket_.reset();
+ event_fd_.reset();
+ udp_socket_.reset();
+ tcp_socket_.reset();
LOG(INFO) << "server stopped successfully";
return true;
}
-std::vector<std::pair<std::string, ns_type>> DNSResponder::queries() const {
+std::vector<DNSResponder::QueryInfo> DNSResponder::queries() const {
std::lock_guard lock(queries_mutex_);
return queries_;
}
@@ -603,8 +640,10 @@
std::string DNSResponder::dumpQueries() const {
std::lock_guard lock(queries_mutex_);
std::string out;
+
for (const auto& q : queries_) {
- out += "{\"" + q.first + "\", " + std::to_string(q.second) + "} ";
+ out += "{\"" + q.name + "\", " + std::to_string(q.type) + "\", " +
+ dnsproto2str(q.protocol) + "} ";
}
return out;
}
@@ -614,6 +653,15 @@
queries_.clear();
}
+bool DNSResponder::hasOptPseudoRR(DNSHeader* header) const {
+ if (header->additionals.empty()) return false;
+
+ // OPT RR may be placed anywhere within the additional section. See RFC 6891 section 6.1.1.
+ auto found = std::find_if(header->additionals.begin(), header->additionals.end(),
+ [](const auto& a) { return a.rtype == ns_type::ns_t_opt; });
+ return found != header->additionals.end();
+}
+
void DNSResponder::requestHandler() {
epoll_event evs[EPOLL_MAX_EVENTS];
while (true) {
@@ -629,8 +677,10 @@
if (fd == event_fd_.get() && (events & (EPOLLIN | EPOLLERR))) {
handleEventFd();
return;
- } else if (fd == socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
- handleQuery();
+ } else if (fd == udp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
+ handleQuery(IPPROTO_UDP);
+ } else if (fd == tcp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
+ handleQuery(IPPROTO_TCP);
} else {
LOG(WARNING) << "unexpected epoll events " << events << " on fd " << fd;
}
@@ -638,9 +688,9 @@
}
}
-bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* response,
+bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, int protocol, char* response,
size_t* response_len) const {
- LOG(DEBUG) << "request: '" << str2hex(buffer, len) << "'";
+ LOG(DEBUG) << "request: '" << str2hex(buffer, len) << "', on " << dnsproto2str(protocol);
const char* buffer_end = buffer + len;
DNSHeader header;
const char* cur = header.read(buffer, buffer_end);
@@ -684,13 +734,12 @@
{
std::lock_guard lock(queries_mutex_);
for (const DNSQuestion& question : header.questions) {
- queries_.push_back(make_pair(question.qname.name, ns_type(question.qtype)));
+ queries_.push_back({question.qname.name, ns_type(question.qtype), protocol});
}
}
-
// Ignore requests with the preset probability.
auto constexpr bound = std::numeric_limits<unsigned>::max();
- if (arc4random_uniform(bound) > bound * response_probability_) {
+ if (arc4random_uniform(bound) > bound * getResponseProbability(protocol)) {
if (error_rcode_ < 0) {
LOG(ERROR) << "Returning no response";
return false;
@@ -703,13 +752,7 @@
// 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);
- }
+ return makeResponse(&header, protocol, response, response_len);
}
bool DNSResponder::addAnswerRecords(const DNSQuestion& question,
@@ -742,7 +785,7 @@
.rclass = ns_class::ns_c_in,
.ttl = kAnswerRecordTtlSec, // seconds
};
- if (!fillAnswerRdata(it->second, record)) return false;
+ if (!fillRdata(it->second, record)) return false;
answers->push_back(std::move(record));
if (rtype != ns_type::ns_t_cname) break;
rname = it->second;
@@ -758,7 +801,7 @@
return true;
}
-bool DNSResponder::fillAnswerRdata(const std::string& rdatastr, DNSRecord& record) {
+bool DNSResponder::fillRdata(const std::string& rdatastr, DNSRecord& record) {
if (record.rtype == ns_type::ns_t_a) {
record.rdata.resize(4);
if (inet_pton(AF_INET, rdatastr.c_str(), record.rdata.data()) != 1) {
@@ -771,7 +814,8 @@
LOG(ERROR) << "inet_pton(AF_INET6, " << rdatastr << ") failed";
return false;
}
- } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname)) {
+ } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname) ||
+ (record.rtype == ns_type::ns_t_ns)) {
constexpr char delimiter = '.';
std::string name = rdatastr;
std::vector<char> rdata;
@@ -808,6 +852,16 @@
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();
@@ -815,13 +869,69 @@
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 writePacket(header, response, response_len);
+}
+
+bool DNSResponder::makeTruncatedResponse(DNSHeader* header, char* response,
+ size_t* response_len) const {
+ // Build a minimal response for non-EDNS response over UDP. Truncate all stub RRs in answer,
+ // authority and additional section. EDNS response truncation has not supported here yet
+ // because the EDNS response must have an OPT record. See RFC 6891 section 7.
+ header->answers.clear();
+ header->authorities.clear();
+ header->additionals.clear();
+ header->qr = true;
+ header->tr = true;
+ return writePacket(header, response, response_len);
+}
+
+bool DNSResponder::makeResponse(DNSHeader* header, int protocol, char* response,
+ size_t* response_len) const {
+ char buffer[4096];
+ size_t buffer_len = sizeof(buffer);
+ bool ret;
+
+ switch (mapping_type_) {
+ case MappingType::DNS_HEADER:
+ ret = makeResponseFromDnsHeader(header, buffer, &buffer_len);
+ break;
+ case MappingType::BINARY_PACKET:
+ ret = makeResponseFromBinaryPacket(header, buffer, &buffer_len);
+ break;
+ case MappingType::ADDRESS_OR_HOSTNAME:
+ default:
+ ret = makeResponseFromAddressOrHostname(header, buffer, &buffer_len);
+ }
+
+ if (!ret) return false;
+
+ // Return truncated response if the built non-EDNS response size which is larger than 512 bytes
+ // will be responded over UDP. The truncated response implementation here just simply set up
+ // the TC bit and truncate all stub RRs in answer, authority and additional section. It is
+ // because the resolver will retry DNS query over TCP and use the full TCP response. See also
+ // RFC 1035 section 4.2.1 for UDP response truncation and RFC 6891 section 4.3 for EDNS larger
+ // response size capability.
+ // TODO: Perhaps keep the stub RRs as possible.
+ // TODO: Perhaps truncate the EDNS based response over UDP. See also RFC 6891 section 4.3,
+ // section 6.2.5 and section 7.
+ if (protocol == IPPROTO_UDP && buffer_len > kMaximumUdpSize &&
+ !hasOptPseudoRR(header) /* non-EDNS */) {
+ LOG(INFO) << "Return truncated response because original response length " << buffer_len
+ << " is larger than " << kMaximumUdpSize << " bytes.";
+ return makeTruncatedResponse(header, response, response_len);
+ }
+
+ if (buffer_len > *response_len) {
+ LOG(ERROR) << "buffer overflow on line " << __LINE__;
+ return false;
+ }
+ memcpy(response, buffer, buffer_len);
+ *response_len = buffer_len;
return true;
}
-bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* response_len) const {
+bool DNSResponder::makeResponseFromAddressOrHostname(DNSHeader* header, char* response,
+ size_t* response_len) const {
for (const DNSQuestion& question : header->questions) {
if (question.qclass != ns_class::ns_c_in && question.qclass != ns_class::ns_c_any) {
LOG(INFO) << "unsupported question class " << question.qclass;
@@ -832,14 +942,8 @@
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,
@@ -880,18 +984,50 @@
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.
+ // Note that do nothing as makeResponseFromAddressOrHostname() 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);
+}
- char* response_cur = header->write(response, response + *response_len);
- if (response_cur == nullptr) {
- return false;
+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;
+ } 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 makeResponseFromAddressOrHostname() 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);
}
- *response_len = response_cur - response;
- return true;
}
void DNSResponder::setDeferredResp(bool deferred_resp) {
@@ -913,38 +1049,99 @@
return true;
}
-void DNSResponder::handleQuery() {
+void DNSResponder::handleQuery(int protocol) {
char buffer[4096];
sockaddr_storage sa;
socklen_t sa_len = sizeof(sa);
- ssize_t len;
- do {
- len = recvfrom(socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa, &sa_len);
- } while (len < 0 && (errno == EAGAIN || errno == EINTR));
- if (len <= 0) {
- PLOG(INFO) << "recvfrom() failed, len=" << len;
- return;
+ ssize_t len = 0;
+ android::base::unique_fd tcpFd;
+ switch (protocol) {
+ case IPPROTO_UDP:
+ do {
+ len = recvfrom(udp_socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa,
+ &sa_len);
+ } while (len < 0 && (errno == EAGAIN || errno == EINTR));
+ if (len <= 0) {
+ PLOG(ERROR) << "recvfrom() failed, len=" << len;
+ return;
+ }
+ break;
+ case IPPROTO_TCP:
+ tcpFd.reset(accept4(tcp_socket_.get(), reinterpret_cast<sockaddr*>(&sa), &sa_len,
+ SOCK_CLOEXEC));
+ if (tcpFd.get() < 0) {
+ PLOG(ERROR) << "failed to accept client socket";
+ return;
+ }
+ // Get the message length from two byte length field.
+ // See also RFC 1035, section 4.2.2 and RFC 7766, section 8
+ uint8_t queryMessageLengthField[2];
+ if (read(tcpFd.get(), &queryMessageLengthField, 2) != 2) {
+ PLOG(ERROR) << "Not enough length field bytes";
+ return;
+ }
+
+ const uint16_t qlen = (queryMessageLengthField[0] << 8) | queryMessageLengthField[1];
+ while (len < qlen) {
+ ssize_t ret = read(tcpFd.get(), buffer + len, qlen - len);
+ if (ret <= 0) {
+ PLOG(ERROR) << "Error while reading query";
+ return;
+ }
+ len += ret;
+ }
+ break;
}
- LOG(DEBUG) << "read " << len << " bytes";
+ LOG(DEBUG) << "read " << len << " bytes on " << dnsproto2str(protocol);
std::lock_guard lock(cv_mutex_);
char response[4096];
size_t response_len = sizeof(response);
- if (handleDNSRequest(buffer, len, response, &response_len) && response_len > 0) {
+ // TODO: check whether sending malformed packets to DnsResponder
+ if (handleDNSRequest(buffer, len, protocol, response, &response_len) && response_len > 0) {
// place wait_for after handleDNSRequest() so we can check the number of queries in
// test case before it got responded.
std::unique_lock guard(cv_mutex_for_deferred_resp_);
cv_for_deferred_resp_.wait(
guard, [this]() REQUIRES(cv_mutex_for_deferred_resp_) { return !deferred_resp_; });
+ len = 0;
- len = sendto(socket_.get(), response, response_len, 0,
- reinterpret_cast<const sockaddr*>(&sa), sa_len);
- std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len);
+ switch (protocol) {
+ case IPPROTO_UDP:
+ len = sendto(udp_socket_.get(), response, response_len, 0,
+ reinterpret_cast<const sockaddr*>(&sa), sa_len);
+ if (len < 0) {
+ PLOG(ERROR) << "Failed to send response";
+ }
+ break;
+ case IPPROTO_TCP:
+ // Get the message length from two byte length field.
+ // See also RFC 1035, section 4.2.2 and RFC 7766, section 8
+ uint8_t responseMessageLengthField[2];
+ responseMessageLengthField[0] = response_len >> 8;
+ responseMessageLengthField[1] = response_len;
+ if (write(tcpFd.get(), responseMessageLengthField, 2) != 2) {
+ PLOG(ERROR) << "Failed to write response length field";
+ break;
+ }
+ if (write(tcpFd.get(), response, response_len) !=
+ static_cast<ssize_t>(response_len)) {
+ PLOG(ERROR) << "Failed to write response";
+ break;
+ }
+ len = response_len;
+ break;
+ }
+ const std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len);
if (len > 0) {
LOG(DEBUG) << "sent " << len << " bytes to " << host_str;
} else {
- PLOG(INFO) << "sendto() failed for " << host_str;
+ const char* method_str = (protocol == IPPROTO_TCP) ? "write()" : "sendto()";
+ LOG(ERROR) << method_str << " failed for " << host_str;
}
// Test that the response is actually a correct DNS message.
+ // TODO: Perhaps make DNS message validation to support name compression. Or it throws
+ // a warning for a valid DNS message with name compression while the binary packet mapping
+ // is used.
const char* response_end = response + len;
DNSHeader header;
const char* cur = header.read(response, response_end);
@@ -972,4 +1169,42 @@
}
}
+android::base::unique_fd DNSResponder::createListeningSocket(int socket_type) {
+ addrinfo ai_hints{
+ .ai_flags = AI_PASSIVE,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = socket_type,
+ };
+ addrinfo* ai_res = nullptr;
+ const int rv =
+ getaddrinfo(listen_address_.c_str(), listen_service_.c_str(), &ai_hints, &ai_res);
+ ScopedAddrinfo ai_res_cleanup(ai_res);
+ if (rv) {
+ LOG(ERROR) << "getaddrinfo(" << listen_address_ << ", " << listen_service_
+ << ") failed: " << gai_strerror(rv);
+ return {};
+ }
+ for (const addrinfo* ai = ai_res; ai; ai = ai->ai_next) {
+ android::base::unique_fd fd(
+ socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol));
+ if (fd.get() < 0) {
+ PLOG(ERROR) << "ignore creating socket failed";
+ continue;
+ }
+ enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
+ enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
+ const std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
+ const char* socket_str = (socket_type == SOCK_STREAM) ? "TCP" : "UDP";
+
+ if (bind(fd.get(), ai->ai_addr, ai->ai_addrlen)) {
+ PLOG(ERROR) << "failed to bind " << socket_str << " " << host_str << ":"
+ << listen_service_;
+ continue;
+ }
+ LOG(INFO) << "bound to " << socket_str << " " << host_str << ":" << listen_service_;
+ return fd;
+ }
+ return {};
+}
+
} // namespace test
diff --git a/tests/dns_responder/dns_responder.h b/tests/dns_responder/dns_responder.h
index ed4b089..7382364 100644
--- a/tests/dns_responder/dns_responder.h
+++ b/tests/dns_responder/dns_responder.h
@@ -34,6 +34,10 @@
// 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 {
@@ -94,6 +98,7 @@
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:
@@ -122,20 +127,26 @@
*/
class DNSResponder {
public:
- enum class Edns : uint8_t {
+ 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(), 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 {
+ 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,
@@ -152,26 +163,33 @@
~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 setResponseProbability(double response_probability, int protocol);
void setEdns(Edns edns);
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<std::pair<std::string, ns_type>> queries() const;
+ 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 fillAnswerRdata(const std::string& rdatastr, DNSRecord& record);
+ 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.
@@ -194,32 +212,61 @@
}
};
+ // 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, char* response,
+ 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 makeErrorResponse and makeResponse{, FromDnsHeader} to use C++ containers
+
+ // 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 response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}.
- bool makeResponse(DNSHeader* header, 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();
+ void handleQuery(int protocol);
// Trigger the handler thread to terminate.
bool sendToEventFd();
@@ -227,16 +274,25 @@
// Used in the handler thread for the termination signal.
void handleEventFd();
- // Address and service to listen on, currently limited to UDP.
+ // 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_;
// 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;
+ // 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;
+
// Maximum number of fds for epoll.
const int EPOLL_MAX_EVENTS = 2;
@@ -251,16 +307,20 @@
// 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<std::pair<std::string, ns_type>> queries_ GUARDED_BY(queries_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 socket_;
+ 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.
diff --git a/tests/dns_responder/dns_responder_client.cpp b/tests/dns_responder/dns_responder_client.cpp
index 473bfb5..2b24811 100644
--- a/tests/dns_responder/dns_responder_client.cpp
+++ b/tests/dns_responder/dns_responder_client.cpp
@@ -35,38 +35,17 @@
using android::net::INetd;
using android::net::ResolverParamsParcel;
-static const char kCaCert[] = R"(
------BEGIN CERTIFICATE-----
-MIIC4TCCAcmgAwIBAgIUQUHZnWhL6M4qcS+I0lLkMyqf3VMwDQYJKoZIhvcNAQEL
-BQAwADAeFw0xOTA2MTAwODM3MzlaFw0yOTA2MDcwODM3MzlaMAAwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCapRbBg6dRT4id4DxmlyktomE8gpm4W+VA
-ZOivhKat4CvGfVjVIAUYxV7LOGREkkT8Qhn5/gU0lShsnURzEDWY+IjMDDw+kRAm
-iFAlMRnCobTp/tenseNRB2tDuUhkRbzaT6qaidPbKy099p909gxf4YqsgY2NfsY2
-JkideqIkVq2hvLehsu3BgiK06TGUgxOHfj74vx7iGyujq1v38J1hlox5vj/svJF6
-jVdDw8p2UkJbO2N9u3al0nNSMG+MCgd3cvKUySTnjedYXsYB0WyH/JZn//KDq6o+
-as6eQVHuH1fu+8XPzBNATlkHzy+YAs7T+UWbkoa1F8wIElVQg66lAgMBAAGjUzBR
-MB0GA1UdDgQWBBShu/e54D3VdqdLOWo9Ou5hbjaIojAfBgNVHSMEGDAWgBShu/e5
-4D3VdqdLOWo9Ou5hbjaIojAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
-A4IBAQBFkEGqqzzdQlhP5m1kzh+SiUCwekzSump0CSk0JAXAdeLNbWs3H+pE1/hM
-Fx7oFonoX5O6pi68JfcIP0u7wNuZkKqubUl4gG6aHDkAad2oeTov0Be7EKt8Ekwf
-tmFWVQQrF9otlG3Stn4vmE5zVNGQXDgRsNKPekSo0XJz757q5WgblauB71Rekvio
-TCUXXt3jf3SuovcUFjgBkaohikBRbLiPWZrW4y0XUsgBKI6sLtiSZOPiNevY2xAR
-y7mCSmi4wP7vtUQ5G8znkAMKoo0FzyfjSogGQeREUM8Oe9Mmh/D39sq/D4TsiAxt
-Pwl59DlzlHHJhmOL+SCGciBX4X7p
------END CERTIFICATE-----
-)";
-
-void DnsResponderClient::SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+void DnsResponderClient::SetupMappings(unsigned numHosts, const std::vector<std::string>& domains,
std::vector<Mapping>* mappings) {
- mappings->resize(num_hosts * domains.size());
- auto mappings_it = mappings->begin();
- for (unsigned i = 0; i < num_hosts; ++i) {
+ mappings->resize(numHosts * domains.size());
+ auto mappingsIt = mappings->begin();
+ for (unsigned i = 0; i < numHosts; ++i) {
for (const auto& domain : domains) {
- mappings_it->host = StringPrintf("host%u", i);
- mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(), domain.c_str());
- mappings_it->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
- mappings_it->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
- ++mappings_it;
+ mappingsIt->host = StringPrintf("host%u", i);
+ mappingsIt->entry = StringPrintf("%s.%s.", mappingsIt->host.c_str(), domain.c_str());
+ mappingsIt->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
+ mappingsIt->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
+ ++mappingsIt;
}
}
}
@@ -103,7 +82,6 @@
paramsParcel.tlsServers = tlsServers;
paramsParcel.tlsFingerprints = {};
paramsParcel.caCertificate = caCert;
- paramsParcel.tlsConnectTimeoutMs = 1000;
return paramsParcel;
}
@@ -129,17 +107,29 @@
return rv.isOk();
}
-void DnsResponderClient::SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+bool DnsResponderClient::SetResolversFromParcel(const ResolverParamsParcel& resolverParams) {
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ if (!rv.isOk()) LOG(ERROR) << "SetResolversFromParcel() -> " << rv.toString8();
+ return rv.isOk();
+}
+
+ResolverParamsParcel DnsResponderClient::GetDefaultResolverParamsParcel() {
+ return makeResolverParamsParcel(TEST_NETID, kDefaultParams, kDefaultServers,
+ kDefaultSearchDomains, {} /* tlsHostname */, kDefaultServers,
+ kCaCert);
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
std::vector<std::unique_ptr<test::DNSResponder>>* dns,
std::vector<std::string>* servers) {
- const char* listen_srv = "53";
- dns->resize(num_servers);
- servers->resize(num_servers);
- for (unsigned i = 0; i < num_servers; ++i) {
+ const char* listenSrv = "53";
+ dns->resize(numServers);
+ servers->resize(numServers);
+ for (unsigned i = 0; i < numServers; ++i) {
auto& server = (*servers)[i];
auto& d = (*dns)[i];
server = StringPrintf("127.0.0.%u", i + 100);
- d = std::make_unique<test::DNSResponder>(server, listen_srv, ns_rcode::ns_r_servfail);
+ d = std::make_unique<test::DNSResponder>(server, listenSrv, ns_rcode::ns_r_servfail);
for (const auto& mapping : mappings) {
d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
diff --git a/tests/dns_responder/dns_responder_client.h b/tests/dns_responder/dns_responder_client.h
index 0b2966b..93ced99 100644
--- a/tests/dns_responder/dns_responder_client.h
+++ b/tests/dns_responder/dns_responder_client.h
@@ -25,6 +25,7 @@
#include "android/net/IDnsResolver.h"
#include "android/net/INetd.h"
#include "dns_responder.h"
+#include "dns_tls_certificate.h"
inline const std::vector<std::string> kDefaultServers = {"127.0.0.3"};
inline const std::vector<std::string> kDefaultSearchDomains = {"example.com"};
@@ -36,6 +37,8 @@
2, // retry count
};
+// TODO: Replace binder service related code to ndk version after finishing all tests migration
+// from libbinder to libbinder_ndk.
class DnsResponderClient {
public:
struct Mapping {
@@ -50,14 +53,12 @@
static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
std::vector<Mapping>* mappings);
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
bool SetResolversForNetwork(const std::vector<std::string>& servers = kDefaultServers,
const std::vector<std::string>& domains = kDefaultSearchDomains,
const std::vector<int>& params = kDefaultParams);
- bool SetResolversForNetwork(const std::vector<std::string>& servers,
- const std::vector<std::string>& searchDomains,
- const std::string& params);
-
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
bool SetResolversWithTls(const std::vector<std::string>& servers,
const std::vector<std::string>& searchDomains,
const std::vector<int>& params, const std::string& name) {
@@ -66,12 +67,18 @@
return SetResolversWithTls(servers, searchDomains, params, servers, name);
}
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
bool SetResolversWithTls(const std::vector<std::string>& servers,
const std::vector<std::string>& searchDomains,
const std::vector<int>& params,
const std::vector<std::string>& tlsServers, const std::string& name);
- static void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+ bool SetResolversFromParcel(const android::net::ResolverParamsParcel& resolverParams);
+
+ // Return a default resolver configuration for opportunistic mode.
+ static android::net::ResolverParamsParcel GetDefaultResolverParamsParcel();
+
+ static void SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
std::vector<std::unique_ptr<test::DNSResponder>>* dns,
std::vector<std::string>* servers);
diff --git a/tests/dns_responder/dns_responder_client_ndk.cpp b/tests/dns_responder/dns_responder_client_ndk.cpp
new file mode 100644
index 0000000..12152cc
--- /dev/null
+++ b/tests/dns_responder/dns_responder_client_ndk.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dns_responder_client"
+
+#include "dns_responder_client_ndk.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <android/binder_manager.h>
+#include "NetdClient.h"
+
+// TODO: make this dynamic and stop depending on implementation details.
+#define TEST_OEM_NETWORK "oem29"
+#define TEST_NETID 30
+
+// TODO: move this somewhere shared.
+static const char* ANDROID_DNS_MODE = "ANDROID_DNS_MODE";
+
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::INetd;
+using aidl::android::net::ResolverParamsParcel;
+using android::base::StringPrintf;
+using android::net::ResolverStats;
+
+void DnsResponderClient::SetupMappings(unsigned numHosts, const std::vector<std::string>& domains,
+ std::vector<Mapping>* mappings) {
+ mappings->resize(numHosts * domains.size());
+ auto mappingsIt = mappings->begin();
+ for (unsigned i = 0; i < numHosts; ++i) {
+ for (const auto& domain : domains) {
+ mappingsIt->host = StringPrintf("host%u", i);
+ mappingsIt->entry = StringPrintf("%s.%s.", mappingsIt->host.c_str(), domain.c_str());
+ mappingsIt->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
+ mappingsIt->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
+ ++mappingsIt;
+ }
+ }
+}
+
+// TODO: Use SetResolverConfiguration() with ResolverParamsParcel struct directly.
+// DEPRECATED: Use SetResolverConfiguration() in new code
+ResolverParamsParcel DnsResponderClient::makeResolverParamsParcel(
+ int netId, const std::vector<int>& params, const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const std::string& tlsHostname,
+ const std::vector<std::string>& tlsServers, const std::string& caCert) {
+ ResolverParamsParcel paramsParcel;
+
+ paramsParcel.netId = netId;
+ paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
+ paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
+ paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
+ paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
+ paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
+ } else {
+ paramsParcel.baseTimeoutMsec = 0;
+ }
+ if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
+ paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
+ } else {
+ paramsParcel.retryCount = 0;
+ }
+ paramsParcel.servers = servers;
+ paramsParcel.domains = domains;
+ paramsParcel.tlsName = tlsHostname;
+ paramsParcel.tlsServers = tlsServers;
+ paramsParcel.tlsFingerprints = {};
+ paramsParcel.caCertificate = caCert;
+
+ // Note, do not remove this otherwise the ResolverTest#ConnectTlsServerTimeout won't pass in M4
+ // module.
+ // TODO: remove after 2020-01 rolls out.
+ paramsParcel.tlsConnectTimeoutMs = 1000;
+
+ return paramsParcel;
+}
+
+bool DnsResponderClient::GetResolverInfo(aidl::android::net::IDnsResolver* dnsResolverService,
+ unsigned netId, std::vector<std::string>* servers,
+ std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers, res_params* params,
+ std::vector<ResolverStats>* stats,
+ int* waitForPendingReqTimeoutCount) {
+ using aidl::android::net::IDnsResolver;
+ std::vector<int32_t> params32;
+ std::vector<int32_t> stats32;
+ std::vector<int32_t> waitForPendingReqTimeoutCount32{0};
+ auto rv = dnsResolverService->getResolverInfo(netId, servers, domains, tlsServers, ¶ms32,
+ &stats32, &waitForPendingReqTimeoutCount32);
+
+ if (!rv.isOk() || params32.size() != static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT)) {
+ return false;
+ }
+ *params = res_params{
+ .sample_validity =
+ static_cast<uint16_t>(params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
+ .success_threshold =
+ static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
+ .min_samples =
+ static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]),
+ .max_samples =
+ static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]),
+ .base_timeout_msec = params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+ .retry_count = params32[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT],
+ };
+ *waitForPendingReqTimeoutCount = waitForPendingReqTimeoutCount32[0];
+ return ResolverStats::decodeAll(stats32, stats);
+}
+
+bool DnsResponderClient::isRemoteVersionSupported(
+ aidl::android::net::IDnsResolver* dnsResolverService, int requiredVersion) {
+ int remoteVersion = 0;
+ if (!dnsResolverService->getInterfaceVersion(&remoteVersion).isOk()) {
+ LOG(FATAL) << "Can't get 'dnsresolver' remote version";
+ }
+ if (remoteVersion < requiredVersion) {
+ LOG(WARNING) << StringPrintf("Remote version: %d < Required version: %d", remoteVersion,
+ requiredVersion);
+ return false;
+ }
+ return true;
+}
+
+bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains,
+ const std::vector<int>& params) {
+ const auto& resolverParams =
+ makeResolverParamsParcel(TEST_NETID, params, servers, domains, "", {}, "");
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains,
+ const std::vector<int>& params,
+ const std::vector<std::string>& tlsServers,
+ const std::string& name) {
+ const auto& resolverParams = makeResolverParamsParcel(TEST_NETID, params, servers, domains,
+ name, tlsServers, kCaCert);
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ if (!rv.isOk()) LOG(ERROR) << "SetResolversWithTls() -> " << rv.getMessage();
+ return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversFromParcel(const ResolverParamsParcel& resolverParams) {
+ const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+ if (!rv.isOk()) LOG(ERROR) << "SetResolversFromParcel() -> " << rv.getMessage();
+ return rv.isOk();
+}
+
+ResolverParamsParcel DnsResponderClient::GetDefaultResolverParamsParcel() {
+ return makeResolverParamsParcel(TEST_NETID, kDefaultParams, kDefaultServers,
+ kDefaultSearchDomains, {} /* tlsHostname */, kDefaultServers,
+ kCaCert);
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
+ std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+ std::vector<std::string>* servers) {
+ const char* listenSrv = "53";
+ dns->resize(numServers);
+ servers->resize(numServers);
+ for (unsigned i = 0; i < numServers; ++i) {
+ auto& server = (*servers)[i];
+ auto& d = (*dns)[i];
+ server = StringPrintf("127.0.0.%u", i + 100);
+ d = std::make_unique<test::DNSResponder>(server, listenSrv, ns_rcode::ns_r_servfail);
+ for (const auto& mapping : mappings) {
+ d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
+ d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
+ }
+ d->startServer();
+ }
+}
+
+int DnsResponderClient::SetupOemNetwork() {
+ mNetdSrv->networkDestroy(TEST_NETID);
+ mDnsResolvSrv->destroyNetworkCache(TEST_NETID);
+ auto ret = mNetdSrv->networkCreatePhysical(TEST_NETID, INetd::PERMISSION_NONE);
+ if (!ret.isOk()) {
+ fprintf(stderr, "Creating physical network %d failed, %s\n", TEST_NETID, ret.getMessage());
+ return -1;
+ }
+ ret = mDnsResolvSrv->createNetworkCache(TEST_NETID);
+ if (!ret.isOk()) {
+ fprintf(stderr, "Creating network cache %d failed, %s\n", TEST_NETID, ret.getMessage());
+ return -1;
+ }
+ setNetworkForProcess(TEST_NETID);
+ if ((unsigned)TEST_NETID != getNetworkForProcess()) {
+ return -1;
+ }
+ return TEST_NETID;
+}
+
+void DnsResponderClient::TearDownOemNetwork(int oemNetId) {
+ if (oemNetId != -1) {
+ mNetdSrv->networkDestroy(oemNetId);
+ mDnsResolvSrv->destroyNetworkCache(oemNetId);
+ }
+}
+
+void DnsResponderClient::SetUp() {
+ // binder setup
+ ndk::SpAIBinder netdBinder = ndk::SpAIBinder(AServiceManager_getService("netd"));
+ mNetdSrv = INetd::fromBinder(netdBinder);
+ if (mNetdSrv.get() == nullptr) {
+ LOG(FATAL) << "Can't connect to service 'netd'. Missing root privileges? uid=" << getuid();
+ }
+
+ ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver"));
+ mDnsResolvSrv = IDnsResolver::fromBinder(resolvBinder);
+ if (mDnsResolvSrv.get() == nullptr) {
+ LOG(FATAL) << "Can't connect to service 'dnsresolver'. Missing root privileges? uid="
+ << getuid();
+ }
+
+ // Ensure resolutions go via proxy.
+ setenv(ANDROID_DNS_MODE, "", 1);
+ mOemNetId = SetupOemNetwork();
+}
+
+void DnsResponderClient::TearDown() {
+ TearDownOemNetwork(mOemNetId);
+}
diff --git a/tests/dns_responder/dns_responder_client_ndk.h b/tests/dns_responder/dns_responder_client_ndk.h
new file mode 100644
index 0000000..092d414
--- /dev/null
+++ b/tests/dns_responder/dns_responder_client_ndk.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <aidl/android/net/IDnsResolver.h>
+#include <aidl/android/net/INetd.h>
+#include "ResolverStats.h" // TODO: stop depending on this internal header
+#include "dns_responder.h"
+#include "dns_tls_certificate.h"
+#include "params.h"
+
+inline const std::vector<std::string> kDefaultServers = {"127.0.0.3"};
+inline const std::vector<std::string> kDefaultSearchDomains = {"example.com"};
+inline const std::vector<int> kDefaultParams = {
+ 300, // sample validity in seconds
+ 25, // success threshod in percent
+ 8, 8, // {MIN,MAX}_SAMPLES
+ 1000, // BASE_TIMEOUT_MSEC
+ 2, // retry count
+};
+
+#define SKIP_IF_REMOTE_VERSION_LESS_THAN(service, version) \
+ do { \
+ if (!DnsResponderClient::isRemoteVersionSupported(service, version)) { \
+ std::cerr << " Skip test. Remote version is too old, required version: " << version \
+ << std::endl; \
+ return; \
+ } \
+ } while (0)
+
+// TODO: Remove dns_responder_client_ndk.{h,cpp} after replacing the binder usage of
+// dns_responder_client.*
+class DnsResponderClient {
+ public:
+ struct Mapping {
+ std::string host;
+ std::string entry;
+ std::string ip4;
+ std::string ip6;
+ };
+
+ virtual ~DnsResponderClient() = default;
+
+ static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+ std::vector<Mapping>* mappings);
+
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
+ bool SetResolversForNetwork(const std::vector<std::string>& servers = kDefaultServers,
+ const std::vector<std::string>& domains = kDefaultSearchDomains,
+ const std::vector<int>& params = kDefaultParams);
+
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
+ bool SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& searchDomains,
+ const std::vector<int>& params, const std::string& name) {
+ // Pass servers as both network-assigned and TLS servers. Tests can
+ // determine on which server and by which protocol queries arrived.
+ return SetResolversWithTls(servers, searchDomains, params, servers, name);
+ }
+
+ // This function is deprecated. Please use SetResolversFromParcel() instead.
+ bool SetResolversWithTls(const std::vector<std::string>& servers,
+ const std::vector<std::string>& searchDomains,
+ const std::vector<int>& params,
+ const std::vector<std::string>& tlsServers, const std::string& name);
+
+ bool SetResolversFromParcel(const aidl::android::net::ResolverParamsParcel& resolverParams);
+
+ static bool isRemoteVersionSupported(aidl::android::net::IDnsResolver* dnsResolverService,
+ int enabledVersion);
+
+ static bool GetResolverInfo(aidl::android::net::IDnsResolver* dnsResolverService,
+ unsigned netId, std::vector<std::string>* servers,
+ std::vector<std::string>* domains,
+ std::vector<std::string>* tlsServers, res_params* params,
+ std::vector<android::net::ResolverStats>* stats,
+ int* waitForPendingReqTimeoutCount);
+
+ // Return a default resolver configuration for opportunistic mode.
+ static aidl::android::net::ResolverParamsParcel GetDefaultResolverParamsParcel();
+
+ static void SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
+ std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+ std::vector<std::string>* servers);
+
+ static aidl::android::net::ResolverParamsParcel makeResolverParamsParcel(
+ int netId, const std::vector<int>& params, const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains, const std::string& tlsHostname,
+ const std::vector<std::string>& tlsServers, const std::string& caCert = "");
+
+ int SetupOemNetwork();
+
+ void TearDownOemNetwork(int oemNetId);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ aidl::android::net::IDnsResolver* resolvService() const { return mDnsResolvSrv.get(); }
+ aidl::android::net::INetd* netdService() const { return mNetdSrv.get(); }
+
+ private:
+ std::shared_ptr<aidl::android::net::INetd> mNetdSrv;
+ std::shared_ptr<aidl::android::net::IDnsResolver> mDnsResolvSrv;
+ int mOemNetId = -1;
+};
diff --git a/tests/dns_responder/dns_tls_certificate.h b/tests/dns_responder/dns_tls_certificate.h
new file mode 100644
index 0000000..79a4b0f
--- /dev/null
+++ b/tests/dns_responder/dns_tls_certificate.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+// Currently the hostname of TLS server must match the CN filed on the server's certificate.
+// Inject a test CA whose hostname is "example.com" for DNS-OVER-TLS tests.
+static constexpr char kDefaultPrivateDnsHostName[] = "example.com";
+static constexpr char kDefaultIncorrectPrivateDnsHostName[] = "www.example.com";
+
+/*
+ * test cert, key, and rootca files can be generated using openssl with
+ * the following steps:
+ *
+ * 1. Create CA certificate:
+ * $ openssl genrsa 2048 > ca_key.pem
+ * $ openssl req -new -sha256 -x509 -nodes -days 3650 -key ca_key.pem -out ca_certificate.pem -subj
+ * '/C=/ST=/L=/CN=/emailAddress='
+ *
+ * 2. Create private key:
+ * $ openssl req -sha256 -newkey rsa:2048 -days 3650 -nodes -keyout server_key.pem -out
+ * server_req.pem -subj '/C=/ST=/L=/CN=example.com/emailAddress='
+ * $ openssl rsa -in server_key.pem -out server_key.pem
+ *
+ * 3. Create server certificate:
+ * $ openssl x509 -sha256 -req -in server_req.pem -days 3650 -CA
+ * ca_certificate.pem -CAkey ca_key.pem -set_serial 01 -out server_certificate.pem
+ *
+ * 4. Verify the certificate:
+ * $ openssl verify -CAfile ca_certificate.pem server_certificate.pem
+ */
+// ca_certificate.pem
+static constexpr char kCaCert[] = R"(
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgIUQUHZnWhL6M4qcS+I0lLkMyqf3VMwDQYJKoZIhvcNAQEL
+BQAwADAeFw0xOTA2MTAwODM3MzlaFw0yOTA2MDcwODM3MzlaMAAwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCapRbBg6dRT4id4DxmlyktomE8gpm4W+VA
+ZOivhKat4CvGfVjVIAUYxV7LOGREkkT8Qhn5/gU0lShsnURzEDWY+IjMDDw+kRAm
+iFAlMRnCobTp/tenseNRB2tDuUhkRbzaT6qaidPbKy099p909gxf4YqsgY2NfsY2
+JkideqIkVq2hvLehsu3BgiK06TGUgxOHfj74vx7iGyujq1v38J1hlox5vj/svJF6
+jVdDw8p2UkJbO2N9u3al0nNSMG+MCgd3cvKUySTnjedYXsYB0WyH/JZn//KDq6o+
+as6eQVHuH1fu+8XPzBNATlkHzy+YAs7T+UWbkoa1F8wIElVQg66lAgMBAAGjUzBR
+MB0GA1UdDgQWBBShu/e54D3VdqdLOWo9Ou5hbjaIojAfBgNVHSMEGDAWgBShu/e5
+4D3VdqdLOWo9Ou5hbjaIojAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQBFkEGqqzzdQlhP5m1kzh+SiUCwekzSump0CSk0JAXAdeLNbWs3H+pE1/hM
+Fx7oFonoX5O6pi68JfcIP0u7wNuZkKqubUl4gG6aHDkAad2oeTov0Be7EKt8Ekwf
+tmFWVQQrF9otlG3Stn4vmE5zVNGQXDgRsNKPekSo0XJz757q5WgblauB71Rekvio
+TCUXXt3jf3SuovcUFjgBkaohikBRbLiPWZrW4y0XUsgBKI6sLtiSZOPiNevY2xAR
+y7mCSmi4wP7vtUQ5G8znkAMKoo0FzyfjSogGQeREUM8Oe9Mmh/D39sq/D4TsiAxt
+Pwl59DlzlHHJhmOL+SCGciBX4X7p
+-----END CERTIFICATE-----
+)";
+
+// server_key.pem
+static constexpr char kPrivatekey[] = R"(
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4FX+j8yyohxS8
+OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIqnEDbxvcWBrv7
+NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7gu1gn+CnaDYu
+pAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8jhBqhYbQK7ZFL
+mIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOEbAI1QF+tEn1b
+naLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABAoIBACDLLF9wumviLYH6
+9g3IoZMEFpGgo+dEbAEnxnQA+9DDCNy1yGCaJ+8n2ZhwJboLkXAFwWXh07HGq3mQ
+AMo2I7ZPzzkWxVJqaubwCo1s2TUgOb71TDLgZLdJxwnmVRHfS650L3/7gC9yZxON
+RSiWTLVSb5gziLMJ1PD8E/nvwAxaJDlT6vzqwRbnHBkQoumTmds2ecLJd2/6pfl4
+bMhtIKA3ULqnJlqlRt6ds/pWU9ttmXEX52uaGhzaF7PRomOW5pKR6CyBzNCn/RNF
+ZPIINW1TVWss9NMZsJLdIzs7Oon5gQYil9rU2uiA5ZUanYDIL9DOMrfAM3hfUuFq
+ZOhfBAECgYEA36CT81EkdDE7pum/kIuCG3wDEls+xNbWmF76IJAxnolJzKvJsdJA
+0za/l1Qe3/bRYHZWKc7by45LFYefOsS29jqBnBBMLurI7OLtcXqkTSSm11AfsDDI
+gw4bKs81TYdHhnbIDGeApfSWOGXgDM/j4N3stuvY1lOIocXqKMomZVMCgYEA1ZHD
+jtxeAmCqzJHIJ4DOY0Y2RR3Bq3ue/mc8gmV9wDyJMMvBpvOoRkUjwbKZToi4Px30
+5fn6SCRtOKfEL0b9LV7JFsMr84Zoj8kjtnE0BdNfQqdE/uWltpATl6YUPlzqZTGs
+HBGVpsNCzYkjFu9m/zIiryCHY8Uut3VEZmNJjTsCgYEAgADBTzAuBpg7xeHcdhd0
+xNiqRXKXLkKvGQ6ca9E9pbp91LqsO63Wz09yQWO0PIxh8q4pycqPQyfS0KMNwKzi
+8XQxxiwJ/30Cv51xPlht/X4yReKmEMsLqwCDCnEK2LLLfSs2fOst11B2QBgINC03
+CfrdySKcvqmX9sl7rBdx/OMCgYB9t2o4RDwKhkDEXuRFbKsRARmdIeEJQqHa+4ZA
+8+FMMdZIJQj/b9qUUsqzkKBx/EUI0meAoN/Va6vnd8oiUlViSbNxdL4AghQ2353o
+HUcUTtJ6d+BDc4dSqgj+ccLk2ukXXGAFvcwr+DDwsFM5gv9MJYUJNcq8ziurzpnO
+848uVQKBgEmyAa2jt1qNpAvxU0MakJIuKhQl2b6/54EKi9WKqIMs1+rKk6O/Ck3n
++tEWqHhZ4uCRmvTgpOM821l4fTHsoJ8IGWV0mwfk95pEL+g/eBLExR4etMqaW9uz
+x8vnVTKNzZsAVgRcemcLqzuyuMg+/ZnH+YNMzMl0Nbkt+kE3FhfM
+-----END RSA PRIVATE KEY-----
+)";
+
+// server_certificate.pem
+static constexpr char kCertificate[] = R"(
+-----BEGIN CERTIFICATE-----
+MIICijCCAXICAQEwDQYJKoZIhvcNAQELBQAwADAeFw0xOTA2MTAwODM3MzlaFw0y
+OTA2MDcwODM3MzlaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4
+FX+j8yyohxS8OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIq
+nEDbxvcWBrv7NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7
+gu1gn+CnaDYupAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8j
+hBqhYbQK7ZFLmIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOE
+bAI1QF+tEn1bnaLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQAuI2NjdWiD2lwmRraW6C7VBF+Sf+9QlzTVzSjuDbPkkYIo
+YWpeYsEeFO5NlxxXl77iu4MqznSAOK8GCiNDCCulDNWRhd5lcO1dVHLcIFYKZ+xv
+6IuH3vh60qJ2hoZbalwflnMQklqh3745ZyOH79dzKTFvlWyNJ2hQgP9bZ2g8F4od
+dS7aOwvx3DCv46b7vBJMKd53ZCdHubfFebDcGxc60DUR0fLSI/o1MJgriODZ1SX7
+sxwzrxvbJW0T+gJOL0C0lE6D84F9oL2u3ef17fC5u1bRd/77pzjTM+dQe7sZspCz
+iboTujdUqo+NSdWgwPUTGTYQg/1i9Qe0vjc0YplY
+-----END CERTIFICATE-----
+)";
\ No newline at end of file
diff --git a/tests/dns_responder/dns_tls_frontend.cpp b/tests/dns_responder/dns_tls_frontend.cpp
index 6399842..dfd4ec4 100644
--- a/tests/dns_responder/dns_tls_frontend.cpp
+++ b/tests/dns_responder/dns_tls_frontend.cpp
@@ -32,82 +32,12 @@
#include <android-base/logging.h>
#include <netdutils/InternetAddresses.h>
#include <netdutils/SocketOption.h>
+#include "dns_tls_certificate.h"
using android::netdutils::enableSockopt;
using android::netdutils::ScopedAddrinfo;
namespace {
-/*
- * test cert, key, and rootca files can be generated using openssl with
- * the following commands:
- *
- * Create CA certificate:
- * $ openssl genrsa 2048 > ca-key.pem
- * $ openssl req -new -sha256 -x509 -nodes -days 3650 -key ca_key.pem -out ca_certificate.pem -subj
- * '/C=/ST=/L=/CN=/emailAddress='
- *
- * Create server certificate:
- * $ openssl req -sha256 -newkey rsa:2048 -days 3650 -nodes -keyout serve_key.pem -out
- * server_req.pem -subj '/C=/ST=/L=/CN=example.com/emailAddress='
- * $ openssl rsa -in server_key.pem
- * -out server_key.pem $ openssl x509 -sha256 -req -in server_req.pem -days 3650 -CA
- * ca_certificate.pem -CAkey ca_key.pem -set_serial 01 -out server_certificate.pem
- *
- * Verify the certificate:
- * $ openssl verify -CAfile ca_certificate.pem server_certificate.pem
- */
-// server_certificate.pem
-static const char kCertificate[] = R"(
-const std::string kCertificate =
------BEGIN CERTIFICATE-----
-MIICijCCAXICAQEwDQYJKoZIhvcNAQELBQAwADAeFw0xOTA2MTAwODM3MzlaFw0y
-OTA2MDcwODM3MzlaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4
-FX+j8yyohxS8OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIq
-nEDbxvcWBrv7NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7
-gu1gn+CnaDYupAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8j
-hBqhYbQK7ZFLmIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOE
-bAI1QF+tEn1bnaLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABMA0GCSqG
-SIb3DQEBCwUAA4IBAQAuI2NjdWiD2lwmRraW6C7VBF+Sf+9QlzTVzSjuDbPkkYIo
-YWpeYsEeFO5NlxxXl77iu4MqznSAOK8GCiNDCCulDNWRhd5lcO1dVHLcIFYKZ+xv
-6IuH3vh60qJ2hoZbalwflnMQklqh3745ZyOH79dzKTFvlWyNJ2hQgP9bZ2g8F4od
-dS7aOwvx3DCv46b7vBJMKd53ZCdHubfFebDcGxc60DUR0fLSI/o1MJgriODZ1SX7
-sxwzrxvbJW0T+gJOL0C0lE6D84F9oL2u3ef17fC5u1bRd/77pzjTM+dQe7sZspCz
-iboTujdUqo+NSdWgwPUTGTYQg/1i9Qe0vjc0YplY
------END CERTIFICATE-----
-)";
-
-// server_key.pem
-static const char kPrivatekey[] = R"(
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4FX+j8yyohxS8
-OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIqnEDbxvcWBrv7
-NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7gu1gn+CnaDYu
-pAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8jhBqhYbQK7ZFL
-mIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOEbAI1QF+tEn1b
-naLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABAoIBACDLLF9wumviLYH6
-9g3IoZMEFpGgo+dEbAEnxnQA+9DDCNy1yGCaJ+8n2ZhwJboLkXAFwWXh07HGq3mQ
-AMo2I7ZPzzkWxVJqaubwCo1s2TUgOb71TDLgZLdJxwnmVRHfS650L3/7gC9yZxON
-RSiWTLVSb5gziLMJ1PD8E/nvwAxaJDlT6vzqwRbnHBkQoumTmds2ecLJd2/6pfl4
-bMhtIKA3ULqnJlqlRt6ds/pWU9ttmXEX52uaGhzaF7PRomOW5pKR6CyBzNCn/RNF
-ZPIINW1TVWss9NMZsJLdIzs7Oon5gQYil9rU2uiA5ZUanYDIL9DOMrfAM3hfUuFq
-ZOhfBAECgYEA36CT81EkdDE7pum/kIuCG3wDEls+xNbWmF76IJAxnolJzKvJsdJA
-0za/l1Qe3/bRYHZWKc7by45LFYefOsS29jqBnBBMLurI7OLtcXqkTSSm11AfsDDI
-gw4bKs81TYdHhnbIDGeApfSWOGXgDM/j4N3stuvY1lOIocXqKMomZVMCgYEA1ZHD
-jtxeAmCqzJHIJ4DOY0Y2RR3Bq3ue/mc8gmV9wDyJMMvBpvOoRkUjwbKZToi4Px30
-5fn6SCRtOKfEL0b9LV7JFsMr84Zoj8kjtnE0BdNfQqdE/uWltpATl6YUPlzqZTGs
-HBGVpsNCzYkjFu9m/zIiryCHY8Uut3VEZmNJjTsCgYEAgADBTzAuBpg7xeHcdhd0
-xNiqRXKXLkKvGQ6ca9E9pbp91LqsO63Wz09yQWO0PIxh8q4pycqPQyfS0KMNwKzi
-8XQxxiwJ/30Cv51xPlht/X4yReKmEMsLqwCDCnEK2LLLfSs2fOst11B2QBgINC03
-CfrdySKcvqmX9sl7rBdx/OMCgYB9t2o4RDwKhkDEXuRFbKsRARmdIeEJQqHa+4ZA
-8+FMMdZIJQj/b9qUUsqzkKBx/EUI0meAoN/Va6vnd8oiUlViSbNxdL4AghQ2353o
-HUcUTtJ6d+BDc4dSqgj+ccLk2ukXXGAFvcwr+DDwsFM5gv9MJYUJNcq8ziurzpnO
-848uVQKBgEmyAa2jt1qNpAvxU0MakJIuKhQl2b6/54EKi9WKqIMs1+rKk6O/Ck3n
-+tEWqHhZ4uCRmvTgpOM821l4fTHsoJ8IGWV0mwfk95pEL+g/eBLExR4etMqaW9uz
-x8vnVTKNzZsAVgRcemcLqzuyuMg+/ZnH+YNMzMl0Nbkt+kE3FhfM
------END RSA PRIVATE KEY-----
-)";
-
static bssl::UniquePtr<X509> stringToX509Certs(const char* certs) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(certs, strlen(certs)));
return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
@@ -364,11 +294,14 @@
return true;
}
-bool DnsTlsFrontend::waitForQueries(int number, int timeoutMs) const {
+// TODO: use a condition variable instead of polling
+// TODO: also clear queries_ to eliminate potential race conditions
+bool DnsTlsFrontend::waitForQueries(int expected_count) const {
constexpr int intervalMs = 20;
+ constexpr int timeoutMs = 5000;
int limit = timeoutMs / intervalMs;
for (int count = 0; count <= limit; ++count) {
- bool done = queries_ >= number;
+ bool done = queries_ >= expected_count;
// Always sleep at least one more interval after we are done, to wait for
// any immediate post-query actions that the client may take (such as
// marking this server as reachable during validation).
diff --git a/tests/dns_responder/dns_tls_frontend.h b/tests/dns_responder/dns_tls_frontend.h
index b4455de..1b34607 100644
--- a/tests/dns_responder/dns_tls_frontend.h
+++ b/tests/dns_responder/dns_tls_frontend.h
@@ -39,8 +39,10 @@
*/
class DnsTlsFrontend {
public:
- DnsTlsFrontend(const std::string& listen_address, const std::string& listen_service,
- const std::string& backend_address, const std::string& backend_service)
+ DnsTlsFrontend(const std::string& listen_address = kDefaultListenAddr,
+ const std::string& listen_service = kDefaultListenService,
+ const std::string& backend_address = kDefaultBackendAddr,
+ const std::string& backend_service = kDefaultBackendService)
: listen_address_(listen_address),
listen_service_(listen_service),
backend_address_(backend_address),
@@ -51,12 +53,19 @@
bool running() const { return socket_ != -1; }
bool startServer();
bool stopServer();
+
int queries() const { return queries_; }
void clearQueries() { queries_ = 0; }
- bool waitForQueries(int number, int timeoutMs) const;
+ bool waitForQueries(int expected_count) const;
+
void set_chain_length(int length) { chain_length_ = length; }
void setHangOnHandshakeForTesting(bool hangOnHandshake) { hangOnHandshake_ = hangOnHandshake; }
+ static constexpr char kDefaultListenAddr[] = "127.0.0.3";
+ static constexpr char kDefaultListenService[] = "853";
+ static constexpr char kDefaultBackendAddr[] = "127.0.0.3";
+ static constexpr char kDefaultBackendService[] = "53";
+
private:
void requestHandler();
bool handleOneRequest(SSL* ssl);
diff --git a/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
similarity index 71%
rename from dnsresolver_binder_test.cpp
rename to tests/dnsresolver_binder_test.cpp
index 97c136f..1c6527f 100644
--- a/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -23,33 +23,27 @@
#include <iostream>
#include <vector>
+#include <aidl/android/net/IDnsResolver.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android/net/IDnsResolver.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#include <netdutils/Stopwatch.h>
-#include "tests/dns_metrics_listener/base_metrics_listener.h"
-#include "tests/dns_metrics_listener/test_metrics.h"
+#include "dns_metrics_listener/base_metrics_listener.h"
+#include "dns_metrics_listener/test_metrics.h"
#include "ResolverStats.h"
#include "dns_responder.h"
-#include "dns_responder_client.h"
+#include "dns_responder_client_ndk.h"
-namespace binder = android::binder;
-
-using android::IBinder;
-using android::IServiceManager;
-using android::ProcessState;
-using android::sp;
-using android::String16;
-using android::String8;
-using android::net::IDnsResolver;
-using android::net::ResolverParamsParcel;
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::ResolverParamsParcel;
+using aidl::android::net::metrics::INetdEventListener;
+using android::base::StringPrintf;
using android::net::ResolverStats;
-using android::net::metrics::INetdEventListener;
using android::net::metrics::TestOnDnsEvent;
using android::netdutils::Stopwatch;
@@ -60,11 +54,9 @@
class DnsResolverBinderTest : public ::testing::Test {
public:
DnsResolverBinderTest() {
- sp<IServiceManager> sm = android::defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("dnsresolver"));
- if (binder != nullptr) {
- mDnsResolver = android::interface_cast<IDnsResolver>(binder);
- }
+ ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver"));
+
+ mDnsResolver = IDnsResolver::fromBinder(resolvBinder);
// This could happen when the test isn't running as root, or if netd isn't running.
assert(nullptr != mDnsResolver.get());
// Create cache for test
@@ -77,7 +69,7 @@
}
protected:
- sp<IDnsResolver> mDnsResolver;
+ std::shared_ptr<aidl::android::net::IDnsResolver> mDnsResolver;
};
class TimedOperation : public Stopwatch {
@@ -91,43 +83,6 @@
std::string mName;
};
-namespace {
-
-// TODO: Convert tests to ResolverParamsParcel and delete this stub.
-ResolverParamsParcel makeResolverParamsParcel(int netId, const std::vector<int>& params,
- const std::vector<std::string>& servers,
- const std::vector<std::string>& domains,
- const std::string& tlsHostname,
- const std::vector<std::string>& tlsServers) {
- using android::net::IDnsResolver;
- ResolverParamsParcel paramsParcel;
-
- paramsParcel.netId = netId;
- paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
- paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
- paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
- paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
- if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
- paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
- } else {
- paramsParcel.baseTimeoutMsec = 0;
- }
- if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
- paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
- } else {
- paramsParcel.retryCount = 0;
- }
- paramsParcel.servers = servers;
- paramsParcel.domains = domains;
- paramsParcel.tlsName = tlsHostname;
- paramsParcel.tlsServers = tlsServers;
- paramsParcel.tlsFingerprints = {};
-
- return paramsParcel;
-}
-
-} // namespace
-
TEST_F(DnsResolverBinderTest, IsAlive) {
TimedOperation t("isAlive RPC");
bool isAlive = false;
@@ -136,26 +91,23 @@
}
TEST_F(DnsResolverBinderTest, RegisterEventListener_NullListener) {
- android::binder::Status status = mDnsResolver->registerEventListener(
- android::interface_cast<INetdEventListener>(nullptr));
+ ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(nullptr);
ASSERT_FALSE(status.isOk());
- ASSERT_EQ(EINVAL, status.serviceSpecificErrorCode());
+ ASSERT_EQ(EINVAL, status.getServiceSpecificError());
}
TEST_F(DnsResolverBinderTest, RegisterEventListener_DuplicateSubscription) {
class DummyListener : public android::net::metrics::BaseMetricsListener {};
// Expect to subscribe successfully.
- android::sp<DummyListener> dummyListener = new DummyListener();
- android::binder::Status status = mDnsResolver->registerEventListener(
- android::interface_cast<INetdEventListener>(dummyListener));
- ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+ std::shared_ptr<DummyListener> dummyListener = ndk::SharedRefBase::make<DummyListener>();
+ ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(dummyListener);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
// Expect to subscribe failed with registered listener instance.
- status = mDnsResolver->registerEventListener(
- android::interface_cast<INetdEventListener>(dummyListener));
+ status = mDnsResolver->registerEventListener(dummyListener);
ASSERT_FALSE(status.isOk());
- ASSERT_EQ(EEXIST, status.serviceSpecificErrorCode());
+ ASSERT_EQ(EEXIST, status.getServiceSpecificError());
}
// TODO: Move this test to resolv_integration_test.cpp
@@ -178,7 +130,7 @@
// Start the Binder thread pool.
// TODO: Consider doing this once if there has another event listener unit test.
- android::ProcessState::self()->startThreadPool();
+ ABinderProcess_startThreadPool();
// Setup network.
// TODO: Setup device configuration and DNS responser server as resolver test does.
@@ -204,10 +156,10 @@
dns.clearQueries();
// Register event listener.
- android::sp<TestOnDnsEvent> testOnDnsEvent = new TestOnDnsEvent(expectedResults);
- android::binder::Status status = mDnsResolver->registerEventListener(
- android::interface_cast<INetdEventListener>(testOnDnsEvent));
- ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+ std::shared_ptr<TestOnDnsEvent> testOnDnsEvent =
+ ndk::SharedRefBase::make<TestOnDnsEvent>(expectedResults);
+ ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(testOnDnsEvent);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
// DNS queries.
// Once all expected events of expectedResults are received by the listener, the unit test will
@@ -277,18 +229,18 @@
for (size_t i = 0; i < std::size(kTlsTestData); i++) {
const auto& td = kTlsTestData[i];
- const auto resolverParams = makeResolverParamsParcel(
+ const auto resolverParams = DnsResponderClient::makeResolverParamsParcel(
TEST_NETID, test_params, LOCALLY_ASSIGNED_DNS, {}, td.tlsName, td.servers);
- binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
+ ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
if (td.expectedReturnCode == 0) {
- SCOPED_TRACE(String8::format("test case %zu should have passed", i));
- SCOPED_TRACE(status.toString8());
- EXPECT_EQ(0, status.exceptionCode());
+ SCOPED_TRACE(StringPrintf("test case %zu should have passed", i));
+ SCOPED_TRACE(status.getMessage());
+ EXPECT_EQ(0, status.getServiceSpecificError());
} else {
- SCOPED_TRACE(String8::format("test case %zu should have failed", i));
- EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
- EXPECT_EQ(td.expectedReturnCode, status.serviceSpecificErrorCode());
+ SCOPED_TRACE(StringPrintf("test case %zu should have failed", i));
+ EXPECT_EQ(EX_SERVICE_SPECIFIC, status.getExceptionCode());
+ EXPECT_EQ(td.expectedReturnCode, status.getServiceSpecificError());
}
}
}
@@ -303,10 +255,10 @@
100, // BASE_TIMEOUT_MSEC
3, // retry count
};
- const auto resolverParams =
- makeResolverParamsParcel(TEST_NETID, testParams, servers, domains, "", {});
- binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ const auto resolverParams = DnsResponderClient::makeResolverParamsParcel(
+ TEST_NETID, testParams, servers, domains, "", {});
+ ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
+ EXPECT_TRUE(status.isOk()) << status.getMessage();
std::vector<std::string> res_servers;
std::vector<std::string> res_domains;
@@ -318,7 +270,7 @@
¶ms32, &stats32,
&wait_for_pending_req_timeout_count32);
- EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ EXPECT_TRUE(status.isOk()) << status.getMessage();
EXPECT_EQ(servers.size(), res_servers.size());
EXPECT_EQ(domains.size(), res_domains.size());
EXPECT_EQ(0U, res_tls_servers.size());
@@ -354,7 +306,7 @@
// create it again, expect a EEXIST.
EXPECT_EQ(EEXIST,
- mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).serviceSpecificErrorCode());
+ mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).getServiceSpecificError());
// destroy it.
EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
@@ -369,9 +321,16 @@
EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
}
+TEST_F(DnsResolverBinderTest, FlushNetworkCache) {
+ SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsResolver.get(), 4);
+ // cache has beed created in DnsResolverBinderTest constructor
+ EXPECT_TRUE(mDnsResolver->flushNetworkCache(TEST_NETID).isOk());
+ EXPECT_EQ(ENONET, mDnsResolver->flushNetworkCache(-1).getServiceSpecificError());
+}
+
TEST_F(DnsResolverBinderTest, setLogSeverity) {
// Expect fail
- EXPECT_EQ(EINVAL, mDnsResolver->setLogSeverity(-1).serviceSpecificErrorCode());
+ EXPECT_EQ(EINVAL, mDnsResolver->setLogSeverity(-1).getServiceSpecificError());
// Test set different log level
EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_VERBOSE).isOk());
diff --git a/tests/golddata.proto b/tests/golddata.proto
new file mode 100644
index 0000000..05133fc
--- /dev/null
+++ b/tests/golddata.proto
@@ -0,0 +1,202 @@
+/*
+ * 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 arguments used to send a DNS query by call type CALL_GETHOSTBYNAME.
+ message HostByName {
+ string host = 1;
+ AddressFamily family = 2;
+ }
+
+ // 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;
+ HostByName hostbyname = 3;
+ }
+ };
+
+ // 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
diff --git a/tests/resolv_gold_test.cpp b/tests/resolv_gold_test.cpp
new file mode 100644
index 0000000..5216268
--- /dev/null
+++ b/tests/resolv_gold_test.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ *
+ */
+
+#define LOG_TAG "resolv_gold_test"
+
+#include <Fwmark.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gmock/gmock-matchers.h>
+#include <google/protobuf/text_format.h>
+#include <gtest/gtest.h>
+
+#include "PrivateDnsConfiguration.h"
+#include "getaddrinfo.h"
+#include "gethnamaddr.h"
+#include "golddata.pb.h"
+#include "resolv_cache.h"
+#include "resolv_test_utils.h"
+#include "tests/dns_responder/dns_responder.h"
+#include "tests/dns_responder/dns_responder_client_ndk.h"
+#include "tests/dns_responder/dns_tls_certificate.h"
+#include "tests/dns_responder/dns_tls_frontend.h"
+
+namespace android::net {
+
+using android::base::StringPrintf;
+using android::netdutils::ScopedAddrinfo;
+using std::chrono::milliseconds;
+
+enum class DnsProtocol { CLEARTEXT, TLS };
+
+// The buffer size of resolv_gethostbyname().
+// TODO: Consider moving to packages/modules/DnsResolver/tests/resolv_test_utils.h.
+constexpr unsigned int MAXPACKET = 8 * 1024;
+
+const std::string kTestDataPath = android::base::GetExecutableDirectory() + "/testdata/";
+const std::vector<std::string> kGoldFilesGetAddrInfo = {
+ "getaddrinfo.topsite.google.pbtxt", "getaddrinfo.topsite.youtube.pbtxt",
+ "getaddrinfo.topsite.amazon.pbtxt", "getaddrinfo.topsite.yahoo.pbtxt",
+ "getaddrinfo.topsite.facebook.pbtxt", "getaddrinfo.topsite.reddit.pbtxt",
+ "getaddrinfo.topsite.wikipedia.pbtxt", "getaddrinfo.topsite.ebay.pbtxt",
+ "getaddrinfo.topsite.netflix.pbtxt", "getaddrinfo.topsite.bing.pbtxt"};
+const std::vector<std::string> kGoldFilesGetAddrInfoTls = {"getaddrinfo.tls.topsite.google.pbtxt"};
+const std::vector<std::string> kGoldFilesGetHostByName = {"gethostbyname.topsite.youtube.pbtxt"};
+const std::vector<std::string> kGoldFilesGetHostByNameTls = {
+ "gethostbyname.tls.topsite.youtube.pbtxt"};
+
+// Fixture test class definition.
+class TestBase : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // Create cache for test
+ resolv_create_cache_for_net(TEST_NETID);
+ }
+
+ void TearDown() override {
+ // Clear TLS configuration for test
+ gPrivateDnsConfiguration.clear(TEST_NETID);
+ // Delete cache for test
+ resolv_delete_cache_for_net(TEST_NETID);
+ }
+
+ void SetResolverConfiguration(const std::vector<std::string>& servers,
+ const std::vector<std::string>& domains,
+ const std::vector<std::string>& tlsServers = {},
+ const std::string& tlsHostname = "",
+ const std::string& caCert = "") {
+ // Determine the DNS configuration steps from setResolverConfiguration() in
+ // packages/modules/DnsResolver/ResolverController.cpp. The gold test just needs to setup
+ // simply DNS and DNS-over-TLS server configuration. Some implementation in
+ // setResolverConfiguration() are not required. For example, limiting TLS server amount is
+ // not necessary for gold test because gold test has only one TLS server for testing
+ // so far.
+ Fwmark fwmark;
+ fwmark.netId = TEST_NETID;
+ fwmark.explicitlySelected = true;
+ fwmark.protectedFromVpn = true;
+ fwmark.permission = PERMISSION_SYSTEM;
+ ASSERT_EQ(gPrivateDnsConfiguration.set(TEST_NETID, fwmark.intValue, tlsServers, tlsHostname,
+ caCert),
+ 0);
+ ASSERT_EQ(resolv_set_nameservers(TEST_NETID, servers, domains, kParams), 0);
+ }
+
+ void SetResolvers() { SetResolverConfiguration(kDefaultServers, kDefaultSearchDomains); }
+
+ void SetResolversWithTls() {
+ // Pass servers as both network-assigned and TLS servers. Tests can
+ // determine on which server and by which protocol queries arrived.
+ // See also DnsClient::SetResolversWithTls() in
+ // packages/modules/DnsResolver/tests/dns_responder/dns_responder_client.h.
+ SetResolverConfiguration(kDefaultServers, kDefaultSearchDomains, kDefaultServers,
+ kDefaultPrivateDnsHostName, kCaCert);
+ }
+
+ bool WaitForPrivateDnsValidation(const std::string& serverAddr) {
+ constexpr milliseconds retryIntervalMs{20};
+ constexpr milliseconds timeoutMs{3000};
+ android::base::Timer t;
+ while (t.duration() < timeoutMs) {
+ const auto& validatedServers =
+ gPrivateDnsConfiguration.getStatus(TEST_NETID).validatedServers();
+ for (const auto& server : validatedServers) {
+ if (serverAddr == ToString(&server.ss)) return true;
+ }
+ std::this_thread::sleep_for(retryIntervalMs);
+ }
+ return false;
+ }
+
+ GoldTest ToProto(const std::string& file) {
+ // Convert the testing configuration from .pbtxt file to proto.
+ std::string file_content;
+ const std::string file_name = kTestDataPath + file;
+ EXPECT_TRUE(android::base::ReadFileToString(file_name, &file_content))
+ << "Failed to read " << file_name << ": " << strerror(errno);
+ android::net::GoldTest goldtest;
+ EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(file_content, &goldtest));
+ return goldtest;
+ }
+
+ void SetupMappings(const android::net::GoldTest& goldtest, test::DNSResponder& dns) {
+ for (const auto& m : goldtest.packet_mapping()) {
+ // Convert string to bytes because .proto type "bytes" is "string" type in C++.
+ // See also the section "Scalar Value Types" in "Language Guide (proto3)".
+ // TODO: Use C++20 std::span in addMappingBinaryPacket. It helps to take both
+ // std::string and std::vector without conversions.
+ dns.addMappingBinaryPacket(
+ std::vector<uint8_t>(m.query().begin(), m.query().end()),
+ std::vector<uint8_t>(m.response().begin(), m.response().end()));
+ }
+ }
+
+ android_net_context GetNetContext(const DnsProtocol protocol) {
+ return protocol == DnsProtocol::TLS ? kNetcontextTls : kNetcontext;
+ }
+
+ template <class AddressType>
+ void VerifyAddress(const android::net::GoldTest& goldtest, const AddressType& result) {
+ if (goldtest.result().return_code() != GT_EAI_NO_ERROR) {
+ EXPECT_EQ(result, nullptr);
+ } else {
+ ASSERT_NE(result, nullptr);
+ const auto& addresses = goldtest.result().addresses();
+ EXPECT_THAT(ToStrings(result), ::testing::UnorderedElementsAreArray(addresses));
+ }
+ }
+
+ void VerifyGetAddrInfo(const android::net::GoldTest& goldtest, const DnsProtocol protocol) {
+ ASSERT_TRUE(goldtest.config().has_addrinfo());
+ const auto& args = goldtest.config().addrinfo();
+ const addrinfo hints = {
+ // Clear the flag AI_ADDRCONFIG to avoid flaky test because AI_ADDRCONFIG looks at
+ // whether connectivity is available. It makes that the resolver may send only A
+ // or AAAA DNS query per connectivity even AF_UNSPEC has been assigned. See also
+ // have_ipv6() and have_ipv4() in packages/modules/DnsResolver/getaddrinfo.cpp.
+ // TODO: Consider keeping the configuration flag AI_ADDRCONFIG once the unit
+ // test can treat the IPv4 and IPv6 connectivity.
+ .ai_flags = args.ai_flags() & ~AI_ADDRCONFIG,
+ .ai_family = args.family(),
+ .ai_socktype = args.socktype(),
+ .ai_protocol = args.protocol(),
+ };
+ addrinfo* res = nullptr;
+ const android_net_context netcontext = GetNetContext(protocol);
+ NetworkDnsEventReported event;
+ const int rv =
+ resolv_getaddrinfo(args.host().c_str(), nullptr, &hints, &netcontext, &res, &event);
+ ScopedAddrinfo result(res);
+ ASSERT_EQ(rv, goldtest.result().return_code());
+ VerifyAddress(goldtest, result);
+ }
+
+ void VerifyGetHostByName(const android::net::GoldTest& goldtest, const DnsProtocol protocol) {
+ ASSERT_TRUE(goldtest.config().has_hostbyname());
+ const auto& args = goldtest.config().hostbyname();
+ hostent* hp = nullptr;
+ hostent hbuf;
+ char tmpbuf[MAXPACKET];
+ const android_net_context netcontext = GetNetContext(protocol);
+ NetworkDnsEventReported event;
+ const int rv = resolv_gethostbyname(args.host().c_str(), args.family(), &hbuf, tmpbuf,
+ sizeof(tmpbuf), &netcontext, &hp, &event);
+ ASSERT_EQ(rv, goldtest.result().return_code());
+ VerifyAddress(goldtest, hp);
+ }
+
+ void VerifyResolver(const android::net::GoldTest& goldtest, const test::DNSResponder& dns,
+ const test::DnsTlsFrontend& tls, const DnsProtocol protocol) {
+ size_t queries;
+ std::string name;
+
+ // Verify DNS query calls and results by proto. Then, determine expected query times and
+ // queried name for checking server query status later.
+ switch (const auto calltype = goldtest.config().call()) {
+ case android::net::CallType::CALL_GETADDRINFO:
+ ASSERT_TRUE(goldtest.config().has_addrinfo());
+ ASSERT_NO_FATAL_FAILURE(VerifyGetAddrInfo(goldtest, protocol));
+ queries = goldtest.config().addrinfo().family() == AF_UNSPEC ? 2U : 1U;
+ name = goldtest.config().addrinfo().host();
+ break;
+ case android::net::CallType::CALL_GETHOSTBYNAME:
+ ASSERT_TRUE(goldtest.config().has_hostbyname());
+ ASSERT_NO_FATAL_FAILURE(VerifyGetHostByName(goldtest, protocol));
+ queries = 1U;
+ name = goldtest.config().hostbyname().host();
+ break;
+ default:
+ FAIL() << "Unsupported call type: " << calltype;
+ }
+
+ // Verify DNS server query status.
+ EXPECT_EQ(GetNumQueries(dns, name.c_str()), queries);
+ if (protocol == DnsProtocol::TLS) EXPECT_EQ(tls.queries(), static_cast<int>(queries));
+ }
+
+ static constexpr res_params kParams = {
+ .sample_validity = 300,
+ .success_threshold = 25,
+ .min_samples = 8,
+ .max_samples = 8,
+ .base_timeout_msec = 1000,
+ .retry_count = 2,
+ };
+ static constexpr android_net_context kNetcontext = {
+ .app_netid = TEST_NETID,
+ .app_mark = MARK_UNSET,
+ .dns_netid = TEST_NETID,
+ .dns_mark = MARK_UNSET,
+ .uid = NET_CONTEXT_INVALID_UID,
+ };
+ static constexpr android_net_context kNetcontextTls = {
+ .app_netid = TEST_NETID,
+ .app_mark = MARK_UNSET,
+ .dns_netid = TEST_NETID,
+ .dns_mark = MARK_UNSET,
+ .uid = NET_CONTEXT_INVALID_UID,
+ // Set TLS flags. See also maybeFixupNetContext() in
+ // packages/modules/DnsResolver/DnsProxyListener.cpp.
+ .flags = NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS,
+ };
+};
+class ResolvGetAddrInfo : public TestBase {};
+
+// Fixture tests.
+TEST_F(ResolvGetAddrInfo, RemovePacketMapping) {
+ test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+ ASSERT_TRUE(dns.startServer());
+ ASSERT_NO_FATAL_FAILURE(SetResolvers());
+
+ dns.addMappingBinaryPacket(kHelloExampleComQueryV4, kHelloExampleComResponseV4);
+
+ addrinfo* res = nullptr;
+ const addrinfo hints = {.ai_family = AF_INET};
+ NetworkDnsEventReported event;
+ int rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+ ScopedAddrinfo result(res);
+ ASSERT_NE(result, nullptr);
+ ASSERT_EQ(rv, 0);
+ EXPECT_EQ(ToString(result), kHelloExampleComAddrV4);
+
+ // Remove existing DNS record.
+ dns.removeMappingBinaryPacket(kHelloExampleComQueryV4);
+
+ // Expect to have no answer in DNS query result.
+ rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+ result.reset(res);
+ ASSERT_EQ(result, nullptr);
+ ASSERT_EQ(rv, EAI_NODATA);
+}
+
+TEST_F(ResolvGetAddrInfo, ReplacePacketMapping) {
+ test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+ ASSERT_TRUE(dns.startServer());
+ ASSERT_NO_FATAL_FAILURE(SetResolvers());
+
+ // Register the record which uses IPv4 address 1.2.3.4.
+ dns.addMappingBinaryPacket(kHelloExampleComQueryV4, kHelloExampleComResponseV4);
+
+ // Expect that the DNS query returns IPv4 address 1.2.3.4.
+ addrinfo* res = nullptr;
+ const addrinfo hints = {.ai_family = AF_INET};
+ NetworkDnsEventReported event;
+ int rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+ ScopedAddrinfo result(res);
+ ASSERT_NE(result, nullptr);
+ ASSERT_EQ(rv, 0);
+ EXPECT_EQ(ToString(result), "1.2.3.4");
+
+ // Replace the registered record with a record which uses new IPv4 address 5.6.7.8.
+ std::vector<uint8_t> newHelloExampleComResponseV4 = {
+ /* Header */
+ 0x00, 0x00, /* Transaction ID: 0x0000 */
+ 0x81, 0x80, /* Flags: qr rd ra */
+ 0x00, 0x01, /* Questions: 1 */
+ 0x00, 0x01, /* Answer RRs: 1 */
+ 0x00, 0x00, /* Authority RRs: 0 */
+ 0x00, 0x00, /* Additional RRs: 0 */
+ /* Queries */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ /* Answers */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ 0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+ 0x00, 0x04, /* Data length: 4 */
+ 0x05, 0x06, 0x07, 0x08 /* Address: 5.6.7.8 */
+ };
+ dns.addMappingBinaryPacket(kHelloExampleComQueryV4, newHelloExampleComResponseV4);
+
+ // Expect that DNS query returns new IPv4 address 5.6.7.8.
+ rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+ result.reset(res);
+ ASSERT_NE(result, nullptr);
+ ASSERT_EQ(rv, 0);
+ EXPECT_EQ(ToString(result), "5.6.7.8");
+}
+
+TEST_F(ResolvGetAddrInfo, BasicTlsQuery) {
+ test::DNSResponder dns;
+ dns.addMapping(kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4);
+ dns.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6);
+ ASSERT_TRUE(dns.startServer());
+
+ test::DnsTlsFrontend tls;
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_NO_FATAL_FAILURE(SetResolversWithTls());
+ EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address()));
+
+ dns.clearQueries();
+ 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 = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+ NetworkDnsEventReported event;
+ const int rv =
+ resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontextTls, &res, &event);
+ ScopedAddrinfo result(res);
+ ASSERT_EQ(rv, 0);
+ EXPECT_EQ(GetNumQueries(dns, kHelloExampleCom), 2U);
+ const std::vector<std::string> result_strs = ToStrings(result);
+ EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(
+ {kHelloExampleComAddrV4, kHelloExampleComAddrV6}));
+ EXPECT_EQ(tls.queries(), 3);
+}
+
+// Parameterized test class definition.
+using GoldTestParamType = std::tuple<DnsProtocol, std::string /* filename */>;
+class ResolvGoldTest : public TestBase, public ::testing::WithParamInterface<GoldTestParamType> {
+ public:
+ // Generate readable string for test name from test parameters.
+ static std::string Name(::testing::TestParamInfo<GoldTestParamType> info) {
+ const auto& [protocol, file] = info.param;
+ std::string name = StringPrintf(
+ "%s_%s", protocol == DnsProtocol::CLEARTEXT ? "CLEARTEXT" : "TLS", file.c_str());
+ std::replace_if(
+ std::begin(name), std::end(name), [](char ch) { return !std::isalnum(ch); }, '_');
+ return name;
+ }
+};
+
+// GetAddrInfo tests.
+INSTANTIATE_TEST_SUITE_P(GetAddrInfo, ResolvGoldTest,
+ ::testing::Combine(::testing::Values(DnsProtocol::CLEARTEXT),
+ ::testing::ValuesIn(kGoldFilesGetAddrInfo)),
+ ResolvGoldTest::Name);
+INSTANTIATE_TEST_SUITE_P(GetAddrInfoTls, ResolvGoldTest,
+ ::testing::Combine(::testing::Values(DnsProtocol::TLS),
+ ::testing::ValuesIn(kGoldFilesGetAddrInfoTls)),
+ ResolvGoldTest::Name);
+
+// GetHostByName tests.
+INSTANTIATE_TEST_SUITE_P(GetHostByName, ResolvGoldTest,
+ ::testing::Combine(::testing::Values(DnsProtocol::CLEARTEXT),
+ ::testing::ValuesIn(kGoldFilesGetHostByName)),
+ ResolvGoldTest::Name);
+INSTANTIATE_TEST_SUITE_P(GetHostByNameTls, ResolvGoldTest,
+ ::testing::Combine(::testing::Values(DnsProtocol::TLS),
+ ::testing::ValuesIn(kGoldFilesGetHostByNameTls)),
+ ResolvGoldTest::Name);
+
+TEST_P(ResolvGoldTest, GoldData) {
+ const auto& [protocol, file] = GetParam();
+
+ // Setup DNS server configuration.
+ test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+ ASSERT_TRUE(dns.startServer());
+ test::DnsTlsFrontend tls;
+
+ if (protocol == DnsProtocol::CLEARTEXT) {
+ ASSERT_NO_FATAL_FAILURE(SetResolvers());
+ } else if (protocol == DnsProtocol::TLS) {
+ ASSERT_TRUE(tls.startServer());
+ ASSERT_NO_FATAL_FAILURE(SetResolversWithTls());
+ EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address()));
+ tls.clearQueries();
+ }
+
+ // Read test configuration from proto text file to proto.
+ const auto goldtest = ToProto(file);
+
+ // Register packet mappings (query, response) from proto.
+ SetupMappings(goldtest, dns);
+
+ // Verify the resolver by proto.
+ VerifyResolver(goldtest, dns, tls, protocol);
+}
+
+} // namespace android::net
diff --git a/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
similarity index 76%
rename from resolv_integration_test.cpp
rename to tests/resolv_integration_test.cpp
index 27e21d2..6ea88da 100644
--- a/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <android/multinetwork.h> // ResNsendFlags
@@ -33,9 +34,10 @@
#include <netdutils/InternetAddresses.h>
#include <netdutils/NetworkConstants.h> // SHA256_SIZE
#include <netdutils/ResponseCode.h>
+#include <netdutils/Slice.h>
#include <netdutils/SocketOption.h>
+#include <netdutils/Stopwatch.h>
#include <netinet/in.h>
-#include <openssl/base64.h>
#include <poll.h> /* poll */
#include <private/android_filesystem_config.h>
#include <resolv.h>
@@ -51,16 +53,18 @@
#include <numeric>
#include <thread>
+#include <aidl/android/net/IDnsResolver.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include "NetdClient.h"
#include "ResolverStats.h"
-#include "android/net/IDnsResolver.h"
-#include "binder/IServiceManager.h"
-#include "netd_resolv/params.h" // MAXNS
-#include "netid_client.h" // NETID_UNSET
+#include "netid_client.h" // NETID_UNSET
+#include "params.h" // MAXNS
#include "test_utils.h"
#include "tests/dns_metrics_listener/dns_metrics_listener.h"
#include "tests/dns_responder/dns_responder.h"
-#include "tests/dns_responder/dns_responder_client.h"
+#include "tests/dns_responder/dns_responder_client_ndk.h"
+#include "tests/dns_responder/dns_tls_certificate.h"
#include "tests/dns_responder/dns_tls_frontend.h"
#include "tests/resolv_test_utils.h"
@@ -71,26 +75,26 @@
// Use maximum reserved appId for applications to avoid conflict with existing uids.
static const int TEST_UID = 99999;
-// Currently the hostname of TLS server must match the CN filed on the server's certificate.
-// Inject a test CA whose hostname is "example.com" for DNS-OVER-TLS tests.
-static const std::string kDefaultPrivateDnsHostName = "example.com";
-static const std::string kDefaultIncorrectPrivateDnsHostName = "www.example.com";
-
// Semi-public Bionic hook used by the NDK (frameworks/base/native/android/net.c)
// Tested here for convenience.
extern "C" int android_getaddrinfofornet(const char* hostname, const char* servname,
const addrinfo* hints, unsigned netid, unsigned mark,
struct addrinfo** result);
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::INetd;
+using aidl::android::net::ResolverParamsParcel;
using android::base::ParseInt;
using android::base::StringPrintf;
using android::base::unique_fd;
-using android::net::INetd;
using android::net::ResolverStats;
using android::net::metrics::DnsMetricsListener;
using android::netdutils::enableSockopt;
+using android::netdutils::makeSlice;
using android::netdutils::ResponseCode;
using android::netdutils::ScopedAddrinfo;
+using android::netdutils::Stopwatch;
+using android::netdutils::toHex;
// TODO: move into libnetdutils?
namespace {
@@ -104,6 +108,13 @@
return ScopedAddrinfo(result);
}
+std::pair<ScopedAddrinfo, int> safe_getaddrinfo_time_taken(const char* node, const char* service,
+ const addrinfo& hints) {
+ Stopwatch s;
+ ScopedAddrinfo result = safe_getaddrinfo(node, service, &hints);
+ return {std::move(result), s.timeTakenUs() / 1000};
+}
+
} // namespace
class ResolverTest : public ::testing::Test {
@@ -113,49 +124,45 @@
// Note that |mDnsClient| is not used for getting binder service in this static function.
// The reason is that wants to keep |mDnsClient| as a non-static data member. |mDnsClient|
// which sets up device network configuration could be independent from every test.
- // TODO: Perhaps add a static function in resolv_test_utils.{cpp,h} to get binder service.
- auto resolvBinder =
- android::defaultServiceManager()->getService(android::String16("dnsresolver"));
- auto resolvService = android::interface_cast<android::net::IDnsResolver>(resolvBinder);
+ // TODO: Perhaps add a static function in resolv_test_binder_utils.{cpp,h} to get binder
+ // service.
+
+ AIBinder* binder = AServiceManager_getService("dnsresolver");
+ ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(binder);
+ auto resolvService = aidl::android::net::IDnsResolver::fromBinder(resolvBinder);
ASSERT_NE(nullptr, resolvService.get());
// Subscribe the death recipient to the service IDnsResolver for detecting Netd death.
- sResolvDeathRecipient = new ResolvDeathRecipient();
- ASSERT_EQ(android::NO_ERROR, resolvBinder->linkToDeath(sResolvDeathRecipient));
-
- // Subscribe the DNS listener for verifying DNS metrics event contents.
- sDnsMetricsListener = new DnsMetricsListener(TEST_NETID /*monitor specific network*/);
- ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
-
- // Start the binder thread pool for listening DNS metrics events and receiving death
- // recipient.
- android::ProcessState::self()->startThreadPool();
- }
-
- protected:
- struct DnsRecord {
- std::string host_name; // host name
- ns_type type; // record type
- std::string addr; // ipv4/v6 address
- };
-
- class ResolvDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- ~ResolvDeathRecipient() override = default;
-
// GTEST assertion macros are not invoked for generating a test failure in the death
// recipient because the macros can't indicate failed test if Netd died between tests.
// Moreover, continuing testing may have no meaningful after Netd death. Therefore, the
// death recipient aborts process by GTEST_LOG_(FATAL) once Netd died.
- void binderDied(const android::wp<android::IBinder>& /*who*/) override {
+ sResolvDeathRecipient = AIBinder_DeathRecipient_new([](void*) {
constexpr char errorMessage[] = "Netd died";
LOG(ERROR) << errorMessage;
GTEST_LOG_(FATAL) << errorMessage;
- }
- };
+ });
+ ASSERT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, sResolvDeathRecipient, nullptr));
+ // Subscribe the DNS listener for verifying DNS metrics event contents.
+ sDnsMetricsListener = ndk::SharedRefBase::make<DnsMetricsListener>(
+ TEST_NETID /*monitor specific network*/);
+ ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
+
+ // Start the binder thread pool for listening DNS metrics events and receiving death
+ // recipient.
+ ABinderProcess_startThreadPool();
+ }
+ static void TearDownTestCase() { AIBinder_DeathRecipient_delete(sResolvDeathRecipient); }
+
+ protected:
void SetUp() { mDnsClient.SetUp(); }
- void TearDown() { mDnsClient.TearDown(); }
+ void TearDown() {
+ // Ensure the dump works at the end of each test.
+ DumpResolverService();
+
+ mDnsClient.TearDown();
+ }
void StartDns(test::DNSResponder& dns, const std::vector<DnsRecord>& records) {
for (const auto& r : records) {
@@ -166,6 +173,14 @@
dns.clearQueries();
}
+ void DumpResolverService() {
+ unique_fd fd(open("/dev/null", O_WRONLY));
+ EXPECT_EQ(mDnsClient.resolvService()->dump(fd, nullptr, 0), 0);
+
+ const char* querylogCmd[] = {"querylog"}; // Keep it sync with DnsQueryLog::DUMP_KEYWORD.
+ EXPECT_EQ(mDnsClient.resolvService()->dump(fd, querylogCmd, std::size(querylogCmd)), 0);
+ }
+
bool WaitForNat64Prefix(ExpectNat64PrefixStatus status,
std::chrono::milliseconds timeout = std::chrono::milliseconds(1000)) {
return sDnsMetricsListener->waitForNat64Prefix(status, timeout);
@@ -184,17 +199,17 @@
// may temporarily hold lots of dead listeners until the unit test process terminates.
// TODO: Perhaps add an unregistering listener binder call or fork a listener process which
// could be terminated earlier.
- static android::sp<DnsMetricsListener> sDnsMetricsListener; // Initialized in SetUpTestCase.
+ static std::shared_ptr<DnsMetricsListener>
+ sDnsMetricsListener; // Initialized in SetUpTestCase.
// Use a shared static death recipient to monitor the service death. The static death
// recipient could monitor the death not only during the test but also between tests.
- static android::sp<ResolvDeathRecipient>
- sResolvDeathRecipient; // Initialized in SetUpTestCase.
+ static AIBinder_DeathRecipient* sResolvDeathRecipient; // Initialized in SetUpTestCase.
};
// Initialize static member of class.
-android::sp<DnsMetricsListener> ResolverTest::sDnsMetricsListener;
-android::sp<ResolverTest::ResolvDeathRecipient> ResolverTest::sResolvDeathRecipient;
+std::shared_ptr<DnsMetricsListener> ResolverTest::sDnsMetricsListener;
+AIBinder_DeathRecipient* ResolverTest::sResolvDeathRecipient;
TEST_F(ResolverTest, GetHostByName) {
constexpr char nonexistent_host_name[] = "nonexistent.example.com.";
@@ -392,7 +407,6 @@
}
TEST_F(ResolverTest, BinderSerialization) {
- using android::net::IDnsResolver;
std::vector<int> params_offsets = {
IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY,
IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD,
@@ -410,9 +424,7 @@
}
TEST_F(ResolverTest, GetHostByName_Binder) {
- using android::net::IDnsResolver;
-
- std::vector<std::string> domains = { "example.com" };
+ std::vector<std::string> domains = {"example.com"};
std::vector<std::unique_ptr<test::DNSResponder>> dns;
std::vector<std::string> servers;
std::vector<DnsResponderClient::Mapping> mappings;
@@ -442,9 +454,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
- &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(servers.size(), res_servers.size());
EXPECT_EQ(domains.size(), res_domains.size());
EXPECT_EQ(0U, res_tls_servers.size());
@@ -488,7 +500,7 @@
// Could be A or AAAA
std::string result_str = ToString(result);
EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
- << ", result_str='" << result_str << "'";
+ << ", result_str='" << result_str << "'";
// Verify that the name is cached.
size_t old_found = found;
@@ -498,8 +510,7 @@
EXPECT_LE(1U, found);
EXPECT_EQ(old_found, found);
result_str = ToString(result);
- EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
- << result_str;
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4") << result_str;
// Change the DNS resolver, ensure that queries are still cached.
ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr2}));
@@ -516,7 +527,7 @@
// Could be A or AAAA
result_str = ToString(result);
EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
- << ", result_str='" << result_str << "'";
+ << ", result_str='" << result_str << "'";
}
TEST_F(ResolverTest, GetAddrInfoV4) {
@@ -559,7 +570,6 @@
const addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_PACKET,
- .ai_protocol = ANY,
};
addrinfo* result = nullptr;
// This is a valid hint, but the query won't be sent because the socket type is
@@ -633,9 +643,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers,
- &res_domains, &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
+ &res_tls_servers, &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(0, wait_for_pending_req_timeout_count);
});
@@ -725,7 +735,7 @@
TEST_F(ResolverTest, MultidomainResolution) {
constexpr char host_name[] = "nihao.example2.com.";
- std::vector<std::string> searchDomains = { "example1.com", "example2.com", "example3.com" };
+ std::vector<std::string> searchDomains = {"example1.com", "example2.com", "example3.com"};
test::DNSResponder dns("127.0.0.6");
StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
@@ -775,10 +785,10 @@
StartDns(dns0, {{host_name, ns_type::ns_t_aaaa, "2001:db8::5"}});
StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
- std::vector<std::string> servers = { listen_addr0, listen_addr1 };
+ std::vector<std::string> servers = {listen_addr0, listen_addr1};
// <sample validity in s> <success threshold in percent> <min samples> <max samples>
int sample_count = 8;
- const std::vector<int> params = { 300, 25, sample_count, sample_count };
+ const std::vector<int> params = {300, 25, sample_count, sample_count};
ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains, params));
// Repeatedly perform resolutions for non-existing domains until MAXNSSAMPLES resolutions have
@@ -868,11 +878,11 @@
StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
StartDns(dns2, {{host_name, ns_type::ns_t_aaaa, "2001:db8::7"}});
- const std::vector<std::string> servers = { listen_addr0, listen_addr1, listen_addr2 };
+ const std::vector<std::string> servers = {listen_addr0, listen_addr1, listen_addr2};
std::vector<std::thread> threads(10);
for (std::thread& thread : threads) {
- thread = std::thread([this, &servers]() {
- unsigned delay = arc4random_uniform(1*1000*1000); // <= 1s
+ thread = std::thread([this, &servers]() {
+ unsigned delay = arc4random_uniform(1 * 1000 * 1000); // <= 1s
usleep(delay);
std::vector<std::string> serverSubset;
for (const auto& server : servers) {
@@ -902,14 +912,238 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
- &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(0, wait_for_pending_req_timeout_count);
}
+TEST_F(ResolverTest, SkipBadServersDueToInternalError) {
+ constexpr char listen_addr1[] = "fe80::1";
+ constexpr char listen_addr2[] = "255.255.255.255";
+ constexpr char listen_addr3[] = "127.0.0.3";
+
+ test::DNSResponder dns(listen_addr3);
+ ASSERT_TRUE(dns.startServer());
+
+ ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ parcel.servers = {listen_addr1, listen_addr2, listen_addr3};
+
+ // Bad servers can be distinguished after two attempts.
+ parcel.minSamples = 2;
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ // Start querying five times.
+ for (int i = 0; i < 5; i++) {
+ std::string hostName = StringPrintf("hello%d.com.", i);
+ dns.addMapping(hostName, ns_type::ns_t_a, "1.2.3.4");
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ EXPECT_TRUE(safe_getaddrinfo(hostName.c_str(), nullptr, &hints) != nullptr);
+ }
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
+
+ // Verify the result by means of the statistics.
+ EXPECT_EQ(res_stats[0].successes, 0);
+ EXPECT_EQ(res_stats[1].successes, 0);
+ EXPECT_EQ(res_stats[2].successes, 5);
+ EXPECT_EQ(res_stats[0].internal_errors, 2);
+ EXPECT_EQ(res_stats[1].internal_errors, 2);
+ EXPECT_EQ(res_stats[2].internal_errors, 0);
+}
+
+TEST_F(ResolverTest, SkipBadServersDueToTimeout) {
+ constexpr char listen_addr1[] = "127.0.0.3";
+ constexpr char listen_addr2[] = "127.0.0.4";
+
+ // Set dns1 non-responsive and dns2 workable.
+ test::DNSResponder dns1(listen_addr1, test::kDefaultListenService, static_cast<ns_rcode>(-1));
+ test::DNSResponder dns2(listen_addr2);
+ dns1.setResponseProbability(0.0);
+ ASSERT_TRUE(dns1.startServer());
+ ASSERT_TRUE(dns2.startServer());
+
+ ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ parcel.servers = {listen_addr1, listen_addr2};
+
+ // Bad servers can be distinguished after two attempts.
+ parcel.minSamples = 2;
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ // Start querying five times.
+ for (int i = 0; i < 5; i++) {
+ std::string hostName = StringPrintf("hello%d.com.", i);
+ dns1.addMapping(hostName, ns_type::ns_t_a, "1.2.3.4");
+ dns2.addMapping(hostName, ns_type::ns_t_a, "1.2.3.5");
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ EXPECT_TRUE(safe_getaddrinfo(hostName.c_str(), nullptr, &hints) != nullptr);
+ }
+
+ std::vector<std::string> res_servers;
+ std::vector<std::string> res_domains;
+ std::vector<std::string> res_tls_servers;
+ res_params res_params;
+ std::vector<ResolverStats> res_stats;
+ int wait_for_pending_req_timeout_count;
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
+
+ // Verify the result by means of the statistics as well as the query counts.
+ EXPECT_EQ(res_stats[0].successes, 0);
+ EXPECT_EQ(res_stats[1].successes, 5);
+ EXPECT_EQ(res_stats[0].timeouts, 2);
+ EXPECT_EQ(res_stats[1].timeouts, 0);
+ EXPECT_EQ(dns1.queries().size(), 2U);
+ EXPECT_EQ(dns2.queries().size(), 5U);
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable_InvalidInput) {
+ constexpr char hostnameNoip[] = "noip.example.com.";
+ constexpr char hostnameInvalidip[] = "invalidip.example.com.";
+ const std::vector<aidl::android::net::ResolverHostsParcel> invalidCustHosts = {
+ {"", hostnameNoip},
+ {"wrong IP", hostnameInvalidip},
+ };
+ test::DNSResponder dns;
+ StartDns(dns, {});
+ auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+ resolverParams.experimentalOptions.hosts = invalidCustHosts;
+ ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+ for (const auto& hostname : {hostnameNoip, hostnameInvalidip}) {
+ // The query won't get data from customized table because of invalid customized table
+ // and DNSResponder also has no records. hostnameNoip has never registered and
+ // hostnameInvalidip has registered but wrong IP.
+ const addrinfo hints = {.ai_family = AF_UNSPEC};
+ ScopedAddrinfo result = safe_getaddrinfo(hostname, nullptr, &hints);
+ ASSERT_TRUE(result == nullptr);
+ EXPECT_EQ(4U, GetNumQueries(dns, hostname));
+ }
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable) {
+ constexpr char hostnameV4[] = "v4only.example.com.";
+ constexpr char hostnameV6[] = "v6only.example.com.";
+ constexpr char hostnameV4V6[] = "v4v6.example.com.";
+ constexpr char custAddrV4[] = "1.2.3.4";
+ constexpr char custAddrV6[] = "::1.2.3.4";
+ constexpr char dnsSvAddrV4[] = "1.2.3.5";
+ constexpr char dnsSvAddrV6[] = "::1.2.3.5";
+ const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4 = {
+ {custAddrV4, hostnameV4},
+ };
+ const std::vector<aidl::android::net::ResolverHostsParcel> custHostV6 = {
+ {custAddrV6, hostnameV6},
+ };
+ const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4V6 = {
+ {custAddrV4, hostnameV4V6},
+ {custAddrV6, hostnameV4V6},
+ };
+ const std::vector<DnsRecord> dnsSvHostV4 = {
+ {hostnameV4, ns_type::ns_t_a, dnsSvAddrV4},
+ };
+ const std::vector<DnsRecord> dnsSvHostV6 = {
+ {hostnameV6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+ };
+ const std::vector<DnsRecord> dnsSvHostV4V6 = {
+ {hostnameV4V6, ns_type::ns_t_a, dnsSvAddrV4},
+ {hostnameV4V6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+ };
+ struct TestConfig {
+ const std::string name;
+ const std::vector<aidl::android::net::ResolverHostsParcel> customizedHosts;
+ const std::vector<DnsRecord> dnsserverHosts;
+ const std::vector<std::string> queryResult;
+ std::string asParameters() const {
+ return StringPrintf("name: %s, customizedHosts: %s, dnsserverHosts: %s", name.c_str(),
+ customizedHosts.empty() ? "No" : "Yes",
+ dnsserverHosts.empty() ? "No" : "Yes");
+ }
+ } testConfigs[]{
+ // clang-format off
+ {hostnameV4, {}, {}, {}},
+ {hostnameV4, {}, dnsSvHostV4, {dnsSvAddrV4}},
+ {hostnameV4, custHostV4, {}, {custAddrV4}},
+ {hostnameV4, custHostV4, dnsSvHostV4, {custAddrV4}},
+ {hostnameV6, {}, {}, {}},
+ {hostnameV6, {}, dnsSvHostV6, {dnsSvAddrV6}},
+ {hostnameV6, custHostV6, {}, {custAddrV6}},
+ {hostnameV6, custHostV6, dnsSvHostV6, {custAddrV6}},
+ {hostnameV4V6, {}, {}, {}},
+ {hostnameV4V6, {}, dnsSvHostV4V6, {dnsSvAddrV4, dnsSvAddrV6}},
+ {hostnameV4V6, custHostV4V6, {}, {custAddrV4, custAddrV6}},
+ {hostnameV4V6, custHostV4V6, dnsSvHostV4V6, {custAddrV4, custAddrV6}},
+ // clang-format on
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ test::DNSResponder dns;
+ StartDns(dns, config.dnsserverHosts);
+
+ auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+ resolverParams.experimentalOptions.hosts = config.customizedHosts;
+ ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+ const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+ ScopedAddrinfo result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
+ if (config.customizedHosts.empty() && config.dnsserverHosts.empty()) {
+ ASSERT_TRUE(result == nullptr);
+ EXPECT_EQ(2U, GetNumQueries(dns, config.name.c_str()));
+ } else {
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray(config.queryResult));
+ EXPECT_EQ(config.customizedHosts.empty() ? 2U : 0U,
+ GetNumQueries(dns, config.name.c_str()));
+ }
+
+ EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+ }
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable_Modify) {
+ constexpr char hostnameV4V6[] = "v4v6.example.com.";
+ constexpr char custAddrV4[] = "1.2.3.4";
+ constexpr char custAddrV6[] = "::1.2.3.4";
+ constexpr char dnsSvAddrV4[] = "1.2.3.5";
+ constexpr char dnsSvAddrV6[] = "::1.2.3.5";
+ const std::vector<DnsRecord> dnsSvHostV4V6 = {
+ {hostnameV4V6, ns_type::ns_t_a, dnsSvAddrV4},
+ {hostnameV4V6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+ };
+ const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4V6 = {
+ {custAddrV4, hostnameV4V6},
+ {custAddrV6, hostnameV4V6},
+ };
+ test::DNSResponder dns;
+ StartDns(dns, dnsSvHostV4V6);
+ auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+
+ resolverParams.experimentalOptions.hosts = custHostV4V6;
+ ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+ const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+ ScopedAddrinfo result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
+ EXPECT_EQ(0U, GetNumQueries(dns, hostnameV4V6));
+
+ resolverParams.experimentalOptions.hosts = {};
+ ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+ result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({dnsSvAddrV4, dnsSvAddrV6}));
+ EXPECT_EQ(2U, GetNumQueries(dns, hostnameV4V6));
+}
+
TEST_F(ResolverTest, EmptySetup) {
- using android::net::IDnsResolver;
std::vector<std::string> servers;
std::vector<std::string> domains;
ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
@@ -919,9 +1153,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
- &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(0U, res_servers.size());
EXPECT_EQ(0U, res_domains.size());
EXPECT_EQ(0U, res_tls_servers.size());
@@ -941,8 +1175,8 @@
constexpr char listen_addr[] = "127.0.0.13";
constexpr char host_name1[] = "test13.domain1.org.";
constexpr char host_name2[] = "test13.domain2.org.";
- std::vector<std::string> servers = { listen_addr };
- std::vector<std::string> domains = { "domain1.org" };
+ std::vector<std::string> servers = {listen_addr};
+ std::vector<std::string> domains = {"domain1.org"};
const std::vector<DnsRecord> records = {
{host_name1, ns_type::ns_t_aaaa, "2001:db8::13"},
@@ -960,7 +1194,7 @@
EXPECT_EQ("2001:db8::13", ToString(result));
// Test that changing the domain search path on its own works.
- domains = { "domain2.org" };
+ domains = {"domain2.org"};
ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
dns.clearQueries();
@@ -973,7 +1207,7 @@
namespace {
-std::vector<std::string> getResolverDomains(android::net::IDnsResolver* dnsResolverService,
+std::vector<std::string> getResolverDomains(aidl::android::net::IDnsResolver* dnsResolverService,
unsigned netId) {
std::vector<std::string> res_servers;
std::vector<std::string> res_domains;
@@ -981,8 +1215,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- GetResolverInfo(dnsResolverService, netId, &res_servers, &res_domains, &res_tls_servers,
- &res_params, &res_stats, &wait_for_pending_req_timeout_count);
+ DnsResponderClient::GetResolverInfo(dnsResolverService, netId, &res_servers, &res_domains,
+ &res_tls_servers, &res_params, &res_stats,
+ &wait_for_pending_req_timeout_count);
return res_domains;
}
@@ -1105,9 +1340,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
- &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
// Check the size of the stats and its contents.
EXPECT_EQ(static_cast<size_t>(MAXNS), res_servers.size());
@@ -1155,9 +1390,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
- &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+ &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(1, res_stats[0].timeouts);
EXPECT_EQ(1, res_stats[1].errors);
@@ -1171,7 +1406,7 @@
test::DNSResponder dns;
StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
- std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> servers = {listen_addr};
// There's nothing listening on this address, so validation will either fail or
/// hang. Either way, queries will continue to flow to the DNSResponder.
@@ -1199,14 +1434,14 @@
test::DNSResponder dns;
StartDns(dns, records);
- std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> servers = {listen_addr};
// Bind the specified private DNS socket but don't respond to any client sockets yet.
int s = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
ASSERT_TRUE(s >= 0);
struct sockaddr_in tlsServer = {
- .sin_family = AF_INET,
- .sin_port = htons(853),
+ .sin_family = AF_INET,
+ .sin_port = htons(853),
};
ASSERT_TRUE(inet_pton(AF_INET, listen_addr, &tlsServer.sin_addr));
ASSERT_TRUE(enableSockopt(s, SOL_SOCKET, SO_REUSEPORT).ok());
@@ -1219,7 +1454,7 @@
struct sockaddr_storage cliaddr;
socklen_t sin_size = sizeof(cliaddr);
- int new_fd = accept4(s, reinterpret_cast<struct sockaddr *>(&cliaddr), &sin_size, SOCK_CLOEXEC);
+ int new_fd = accept4(s, reinterpret_cast<struct sockaddr*>(&cliaddr), &sin_size, SOCK_CLOEXEC);
ASSERT_TRUE(new_fd > 0);
// We've received the new file descriptor but not written to it or closed, so the
@@ -1261,7 +1496,7 @@
test::DNSResponder dns;
StartDns(dns, records);
- std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> servers = {listen_addr};
test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
ASSERT_TRUE(tls.startServer());
@@ -1273,7 +1508,7 @@
EXPECT_EQ("1.2.3.1", ToString(result));
// Wait for query to get counted.
- EXPECT_TRUE(tls.waitForQueries(2, 5000));
+ EXPECT_TRUE(tls.waitForQueries(2));
// Stop the TLS server. Since we're in opportunistic mode, queries will
// fall back to the locally-assigned (clear text) nameservers.
@@ -1285,8 +1520,8 @@
EXPECT_EQ("1.2.3.2", ToString(result));
const auto queries = dns.queries();
EXPECT_EQ(1U, queries.size());
- EXPECT_EQ("tls2.example.com.", queries[0].first);
- EXPECT_EQ(ns_t_a, queries[0].second);
+ EXPECT_EQ("tls2.example.com.", queries[0].name);
+ EXPECT_EQ(ns_t_a, queries[0].type);
// Reset the resolvers without enabling TLS. Queries should still be routed
// to the UDP endpoint.
@@ -1318,7 +1553,7 @@
StartDns(dns1, records1);
StartDns(dns2, records2);
- std::vector<std::string> servers = { listen_addr1, listen_addr2 };
+ std::vector<std::string> servers = {listen_addr1, listen_addr2};
test::DnsTlsFrontend tls1(listen_addr1, listen_tls, listen_addr1, listen_udp);
test::DnsTlsFrontend tls2(listen_addr2, listen_tls, listen_addr2, listen_udp);
@@ -1334,7 +1569,7 @@
EXPECT_EQ("1.2.3.1", ToString(result));
// Wait for query to get counted.
- EXPECT_TRUE(tls1.waitForQueries(2, 5000));
+ EXPECT_TRUE(tls1.waitForQueries(2));
// No new queries should have reached tls2.
EXPECT_EQ(1, tls2.queries());
@@ -1345,7 +1580,7 @@
EXPECT_EQ("1.2.3.4", ToString(result));
// Wait for query to get counted.
- EXPECT_TRUE(tls2.waitForQueries(2, 5000));
+ EXPECT_TRUE(tls2.waitForQueries(2));
// No additional queries should have reached the insecure servers.
EXPECT_EQ(2U, dns1.queries().size());
@@ -1363,7 +1598,7 @@
test::DNSResponder dns;
StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.1"}});
- std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> servers = {listen_addr};
test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
ASSERT_TRUE(tls.startServer());
@@ -1393,7 +1628,7 @@
test::DNSResponder dns;
StartDns(dns, records);
- std::vector<std::string> servers = { listen_addr };
+ std::vector<std::string> servers = {listen_addr};
test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
ASSERT_TRUE(tls.startServer());
@@ -1409,9 +1644,9 @@
// Could be A or AAAA
std::string result_str = ToString(result);
EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
- << ", result_str='" << result_str << "'";
+ << ", result_str='" << result_str << "'";
// Wait for both A and AAAA queries to get counted.
- EXPECT_TRUE(tls.waitForQueries(3, 5000));
+ EXPECT_TRUE(tls.waitForQueries(3));
// Clear TLS bit.
ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
@@ -1434,7 +1669,7 @@
const char cleartext_addr[] = "127.0.0.53";
const char cleartext_port[] = "53";
const char tls_port[] = "853";
- const std::vector<std::string> servers = { cleartext_addr };
+ const std::vector<std::string> servers = {cleartext_addr};
test::DNSResponder dns(cleartext_addr);
ASSERT_TRUE(dns.startServer());
@@ -1442,15 +1677,14 @@
test::DnsTlsFrontend tls(cleartext_addr, tls_port, cleartext_addr, cleartext_port);
ASSERT_TRUE(tls.startServer());
+ // clang-format off
struct TestConfig {
const std::string mode;
const bool withWorkingTLS;
const std::string method;
std::string asHostName() const {
- return StringPrintf("%s.%s.%s.",
- mode.c_str(),
- withWorkingTLS ? "tlsOn" : "tlsOff",
+ return StringPrintf("%s.%s.%s.", mode.c_str(), withWorkingTLS ? "tlsOn" : "tlsOff",
method.c_str());
}
} testConfigs[]{
@@ -1473,6 +1707,7 @@
{OPPORTUNISTIC, false, GETADDRINFOFORNET},
{STRICT, false, GETADDRINFOFORNET},
};
+ // clang-format on
for (const auto& config : testConfigs) {
const std::string testHostName = config.asHostName();
@@ -1496,21 +1731,19 @@
if (config.mode == OFF) {
ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains,
kDefaultParams));
- } else if (config.mode == OPPORTUNISTIC) {
+ } else /* OPPORTUNISTIC or STRICT */ {
+ const char* tls_hostname = (config.mode == STRICT) ? kDefaultPrivateDnsHostName : "";
ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
- kDefaultParams, ""));
+ kDefaultParams, tls_hostname));
// Wait for the validation event. If the server is running, the validation should
- // be successful; otherwise, the validation should be failed.
+ // succeed; otherwise, the validation should fail.
EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), config.withWorkingTLS));
- } else if (config.mode == STRICT) {
- ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
- kDefaultParams, kDefaultPrivateDnsHostName));
-
- // Wait for the validation event.
- EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), config.withWorkingTLS));
+ if (config.withWorkingTLS) {
+ EXPECT_TRUE(tls.waitForQueries(1));
+ tls.clearQueries();
+ }
}
- tls.clearQueries();
const hostent* h_result = nullptr;
ScopedAddrinfo ai_result;
@@ -1534,7 +1767,7 @@
// Could be A or AAAA
const std::string result_str = ToString(ai_result);
EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
- << ", result_str='" << result_str << "'";
+ << ", result_str='" << result_str << "'";
} else if (config.method == GETADDRINFOFORNET) {
addrinfo* raw_ai_result = nullptr;
EXPECT_EQ(0, android_getaddrinfofornet(host_name, /*servname=*/nullptr,
@@ -1546,7 +1779,7 @@
// Could be A or AAAA
const std::string result_str = ToString(ai_result);
EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
- << ", result_str='" << result_str << "'";
+ << ", result_str='" << result_str << "'";
}
EXPECT_EQ(0, tls.queries());
@@ -1559,7 +1792,7 @@
TEST_F(ResolverTest, StrictMode_NoTlsServers) {
constexpr char cleartext_addr[] = "127.0.0.53";
- const std::vector<std::string> servers = { cleartext_addr };
+ const std::vector<std::string> servers = {cleartext_addr};
constexpr char host_name[] = "strictmode.notlsips.example.com.";
const std::vector<DnsRecord> records = {
{host_name, ns_type::ns_t_a, "1.2.3.4"},
@@ -1604,12 +1837,12 @@
int ancount, n = 0;
ns_rr rr;
- if (ns_initparse((const uint8_t*) buf, bufLen, &handle) >= 0) {
+ if (ns_initparse((const uint8_t*)buf, bufLen, &handle) >= 0) {
ancount = ns_msg_count(handle, ns_s_an);
if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
const uint8_t* rdata = ns_rr_rdata(rr);
char buffer[INET6_ADDRSTRLEN];
- if (inet_ntop(ipType, (const char*) rdata, buffer, sizeof(buffer))) {
+ if (inet_ntop(ipType, (const char*)rdata, buffer, sizeof(buffer))) {
return buffer;
}
}
@@ -1630,7 +1863,7 @@
.sun_path = "/dev/socket/dnsproxyd",
};
- if (TEMP_FAILURE_RETRY(connect(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr))) !=
+ if (TEMP_FAILURE_RETRY(connect(s, (const struct sockaddr*)&proxy_addr, sizeof(proxy_addr))) !=
0) {
close(s);
return -1;
@@ -2146,13 +2379,14 @@
const char CLEARTEXT_ADDR[] = "127.0.0.53";
const char CLEARTEXT_PORT[] = "53";
const char TLS_PORT[] = "853";
- const std::vector<std::string> servers = { CLEARTEXT_ADDR };
+ const std::vector<std::string> servers = {CLEARTEXT_ADDR};
test::DNSResponder dns(CLEARTEXT_ADDR, CLEARTEXT_PORT, ns_rcode::ns_r_servfail);
ASSERT_TRUE(dns.startServer());
test::DnsTlsFrontend tls(CLEARTEXT_ADDR, TLS_PORT, CLEARTEXT_ADDR, CLEARTEXT_PORT);
+ // clang-format off
static const struct TestConfig {
std::string mode;
std::string method;
@@ -2208,6 +2442,7 @@
//{OPPORTUNISTIC_TLS, GETADDRINFO, Edns::DROP, EXPECT_FAILURE},
//{STRICT, GETADDRINFO, Edns::DROP, EXPECT_FAILURE},
};
+ // clang-format on
for (const auto& config : testConfigs) {
const std::string testHostName = config.asHostName();
@@ -2515,6 +2750,7 @@
EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
+ // clang-format off
static const struct TestConfig {
std::string name;
std::string addr;
@@ -2527,6 +2763,7 @@
{MULTICAST, ADDR_MULTICAST},
{LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
};
+ // clang-format on
for (const auto& config : testConfigs) {
const std::string testHostName = config.asHostName();
@@ -2619,6 +2856,7 @@
EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
+ // clang-format off
// If node is null, return address is listed by libc/getaddrinfo.c as follows.
// - passive socket -> anyaddr (0.0.0.0 or ::)
// - non-passive socket -> localhost (127.0.0.1 or ::1)
@@ -2635,6 +2873,7 @@
{0 /* non-passive */, ADDR_LOCALHOST_V4, ADDR_LOCALHOST_V6},
{AI_PASSIVE, ADDR_ANYADDR_V4, ADDR_ANYADDR_V6}
};
+ // clang-format on
for (const auto& config : testConfigs) {
SCOPED_TRACE(config.asParameters());
@@ -2799,7 +3038,7 @@
// Expect no DNS queries; localhost is resolved via /etc/hosts.
EXPECT_EQ(0U, GetNumQueries(dns, host_name));
- ASSERT_EQ(sizeof(in6_addr), (unsigned) result->h_length);
+ ASSERT_EQ(sizeof(in6_addr), (unsigned)result->h_length);
ASSERT_EQ(AF_INET6, result->h_addrtype);
std::string result_str = ToString(result);
EXPECT_EQ(result_str, host_addr);
@@ -2863,6 +3102,7 @@
EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
+ // clang-format off
static const struct TestConfig {
int flag;
int family;
@@ -2883,6 +3123,7 @@
{0, AF_INET6, "2001:db8::102:304", "v4v6.example.com"},
{0, AF_INET6, "2001:db8::506:708", "2001:db8::506:708"}, // unmapped
};
+ // clang-format on
// Reverse IPv4/IPv6 DNS query. Prefix should have no effect on it.
for (const auto& config : testConfigs) {
@@ -2896,14 +3137,14 @@
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
inet_pton(AF_INET, config.addr.c_str(), &sin.sin_addr);
- rv = getnameinfo((const struct sockaddr*) &sin, sizeof(sin), host, sizeof(host),
- nullptr, 0, config.flag);
+ rv = getnameinfo((const struct sockaddr*)&sin, sizeof(sin), host, sizeof(host), nullptr,
+ 0, config.flag);
if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v4));
} else if (config.family == AF_INET6) {
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
- rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+ rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host),
nullptr, 0, config.flag);
if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6));
}
@@ -2942,6 +3183,7 @@
EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
+ // clang-format off
static const struct TestConfig {
bool hasSynthesizedPtrRecord;
int flag;
@@ -2960,6 +3202,7 @@
{true, NI_NUMERICHOST, "64:ff9b::506:708", "64:ff9b::506:708"},
{true, 0, "64:ff9b::506:708", "v6synthesis.example.com"}
};
+ // clang-format on
// hasSynthesizedPtrRecord = false
// Synthesized PTR record doesn't exist on DNS server
@@ -2979,7 +3222,7 @@
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
- int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+ int rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host),
nullptr, 0, config.flag);
ASSERT_EQ(0, rv);
if (config.flag == NI_NAMEREQD) {
@@ -3004,6 +3247,7 @@
constexpr char listen_addr[] = "::1";
test::DNSResponder dns(listen_addr);
+
StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}});
const std::vector<std::string> servers = {listen_addr};
ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
@@ -3018,7 +3262,7 @@
char host[NI_MAXHOST];
struct sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
inet_pton(AF_INET6, host_addr, &sin6.sin6_addr);
- int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host), nullptr,
+ int rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host), nullptr,
0, NI_NAMEREQD);
ASSERT_EQ(0, rv);
// Expect no DNS queries; localhost is resolved via /etc/hosts.
@@ -3148,13 +3392,13 @@
EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
+ // clang-format off
static const struct TestConfig {
std::string name;
std::string addr;
std::string asHostName() const {
- return StringPrintf("%s.example.com.",
- name.c_str());
+ return StringPrintf("%s.example.com.", name.c_str());
}
} testConfigs[]{
{THIS_NETWORK, ADDR_THIS_NETWORK},
@@ -3163,6 +3407,7 @@
{MULTICAST, ADDR_MULTICAST},
{LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
};
+ // clang-format on
for (const auto& config : testConfigs) {
const std::string testHostName = config.asHostName();
@@ -3197,6 +3442,7 @@
// Setup OPPORTUNISTIC mode and wait for the validation complete.
ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, ""));
EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
+ EXPECT_TRUE(tls.waitForQueries(1));
tls.clearQueries();
// Start NAT64 prefix discovery and wait for it complete.
@@ -3204,8 +3450,8 @@
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
// Verify it bypassed TLS even though there's a TLS server available.
- EXPECT_EQ(0, tls.queries());
- EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+ EXPECT_EQ(0, tls.queries()) << dns.dumpQueries();
+ EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries();
// Restart the testing network to reset the cache.
mDnsClient.TearDown();
@@ -3216,6 +3462,7 @@
ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams,
kDefaultPrivateDnsHostName));
EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
+ EXPECT_TRUE(tls.waitForQueries(1));
tls.clearQueries();
// Start NAT64 prefix discovery and wait for it to complete.
@@ -3223,8 +3470,8 @@
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
// Verify it bypassed TLS despite STRICT mode.
- EXPECT_EQ(0, tls.queries());
- EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+ EXPECT_EQ(0, tls.queries()) << dns.dumpQueries();
+ EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries();
}
namespace {
@@ -3279,8 +3526,8 @@
return true;
}
-android::net::UidRangeParcel makeUidRangeParcel(int start, int stop) {
- android::net::UidRangeParcel res;
+aidl::android::net::UidRangeParcel makeUidRangeParcel(int start, int stop) {
+ aidl::android::net::UidRangeParcel res;
res.start = start;
res.stop = stop;
@@ -3440,30 +3687,51 @@
EXPECT_TRUE(netdService->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, false).isOk());
}
+namespace {
+
+const std::string kDotConnectTimeoutMsFlag(
+ "persist.device_config.netd_native.dot_connect_timeout_ms");
+
+class ScopedSystemProperties {
+ public:
+ explicit ScopedSystemProperties(const std::string& key, const std::string& value)
+ : mStoredKey(key) {
+ mStoredValue = android::base::GetProperty(key, "");
+ android::base::SetProperty(key, value);
+ }
+ ~ScopedSystemProperties() { android::base::SetProperty(mStoredKey, mStoredValue); }
+
+ private:
+ std::string mStoredKey;
+ std::string mStoredValue;
+};
+
+} // namespace
+
TEST_F(ResolverTest, ConnectTlsServerTimeout) {
- constexpr char listen_addr[] = "127.0.0.3";
- constexpr char listen_udp[] = "53";
- constexpr char listen_tls[] = "853";
- constexpr char host_name[] = "tls.example.com.";
- const std::vector<std::string> servers = {listen_addr};
+ constexpr int expectedTimeout = 1000;
+ constexpr char hostname1[] = "query1.example.com.";
+ constexpr char hostname2[] = "query2.example.com.";
const std::vector<DnsRecord> records = {
- {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {hostname1, ns_type::ns_t_a, "1.2.3.4"},
+ {hostname2, ns_type::ns_t_a, "1.2.3.5"},
};
test::DNSResponder dns;
StartDns(dns, records);
-
- test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+ test::DnsTlsFrontend tls;
ASSERT_TRUE(tls.startServer());
- // Opportunistic mode.
- ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, {}));
+ // The resolver will adjust the timeout value to 1000ms since the value is too small.
+ ScopedSystemProperties scopedSystemProperties(kDotConnectTimeoutMsFlag, "100");
- // Wait for the server being marked as validated so that PrivateDnsStatus::validatedServers()
- // won't return empty list.
+ // Set up resolver to opportunistic mode with the default configuration.
+ const ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
- dns.clearQueries();
+ EXPECT_TRUE(tls.waitForQueries(1));
tls.clearQueries();
+ dns.clearQueries();
// The server becomes unresponsive to the handshake request.
tls.setHangOnHandshakeForTesting(true);
@@ -3472,17 +3740,470 @@
// 1. Connect to the private DNS server.
// 2. SSL handshake times out.
// 3. Fallback to UDP transport, and then get the answer.
- const auto start = std::chrono::steady_clock::now();
- addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
- ScopedAddrinfo result = safe_getaddrinfo("tls", nullptr, &hints);
- const auto end = std::chrono::steady_clock::now();
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ auto [result, timeTakenMs] = safe_getaddrinfo_time_taken(hostname1, nullptr, hints);
- EXPECT_TRUE(result != nullptr);
+ EXPECT_NE(nullptr, result);
EXPECT_EQ(0, tls.queries());
- EXPECT_EQ(1U, GetNumQueries(dns, host_name));
- EXPECT_EQ("1.2.3.4", ToString(result));
+ EXPECT_EQ(1U, GetNumQueries(dns, hostname1));
+ EXPECT_EQ(records.at(0).addr, ToString(result));
- // 3000ms is a loose upper bound. Theoretically, it takes a bit more than 1000ms.
- EXPECT_GE(3000, std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
- EXPECT_LE(1000, std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
+ // A loose upper bound is set by adding 2000ms buffer time. Theoretically, getaddrinfo()
+ // should just take a bit more than expetTimeout milliseconds.
+ EXPECT_GE(timeTakenMs, expectedTimeout);
+ EXPECT_LE(timeTakenMs, expectedTimeout + 2000);
+
+ // Set the server to be responsive. Verify that the resolver will attempt to reconnect
+ // to the server and then get the result within the timeout.
+ tls.setHangOnHandshakeForTesting(false);
+ std::tie(result, timeTakenMs) = safe_getaddrinfo_time_taken(hostname2, nullptr, hints);
+
+ EXPECT_NE(nullptr, result);
+ EXPECT_EQ(1, tls.queries());
+ EXPECT_EQ(1U, GetNumQueries(dns, hostname2));
+ EXPECT_EQ(records.at(1).addr, ToString(result));
+
+ EXPECT_LE(timeTakenMs, expectedTimeout);
+}
+
+TEST_F(ResolverTest, FlushNetworkCache) {
+ SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+ test::DNSResponder dns;
+ StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ const hostent* result = gethostbyname("hello");
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+
+ // get result from cache
+ result = gethostbyname("hello");
+ EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+
+ EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+
+ result = gethostbyname("hello");
+ EXPECT_EQ(2U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+}
+
+TEST_F(ResolverTest, FlushNetworkCache_random) {
+ SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+ constexpr int num_flush = 10;
+ constexpr int num_queries = 20;
+ test::DNSResponder dns;
+ StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+ const addrinfo hints = {.ai_family = AF_INET};
+
+ std::thread t([this]() {
+ for (int i = 0; i < num_flush; ++i) {
+ unsigned delay = arc4random_uniform(10 * 1000); // 10ms
+ usleep(delay);
+ EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+ }
+ });
+
+ for (int i = 0; i < num_queries; ++i) {
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+ }
+ t.join();
+}
+
+// flush cache while one query is wait-for-response, another is pending.
+TEST_F(ResolverTest, FlushNetworkCache_concurrent) {
+ SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+ const char* listen_addr1 = "127.0.0.9";
+ const char* listen_addr2 = "127.0.0.10";
+ test::DNSResponder dns1(listen_addr1);
+ test::DNSResponder dns2(listen_addr2);
+ StartDns(dns1, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+ StartDns(dns2, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+ addrinfo hints = {.ai_family = AF_INET};
+
+ // step 1: set server#1 into deferred responding mode
+ dns1.setDeferredResp(true);
+ std::thread t1([&listen_addr1, &hints, this]() {
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr1}));
+ // step 3: query
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ // step 9: check result
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+ });
+
+ // step 2: wait for the query to reach the server
+ while (GetNumQueries(dns1, kHelloExampleCom) == 0) {
+ usleep(1000); // 1ms
+ }
+
+ std::thread t2([&listen_addr2, &hints, &dns2, this]() {
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr2}));
+ // step 5: query (should be blocked in resolver)
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ // step 7: check result
+ EXPECT_TRUE(result != nullptr);
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+ EXPECT_EQ(1U, GetNumQueriesForType(dns2, ns_type::ns_t_a, kHelloExampleCom));
+ });
+
+ // step 4: wait a bit for the 2nd query to enter pending state
+ usleep(100 * 1000); // 100ms
+ // step 6: flush cache (will unblock pending queries)
+ EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+ t2.join();
+
+ // step 8: resume server#1
+ dns1.setDeferredResp(false);
+ t1.join();
+
+ // step 10: verify if result is correctly cached
+ dns2.clearQueries();
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ EXPECT_EQ(0U, GetNumQueries(dns2, kHelloExampleCom));
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+}
+
+// TODO: Perhaps to have a boundary conditions test for TCP and UDP.
+TEST_F(ResolverTest, TcpQueryWithOversizePayload) {
+ test::DNSResponder dns;
+ StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ int fd = dns_open_proxy();
+ ASSERT_TRUE(fd > 0);
+
+ // Sending DNS query over TCP once the packet sizes exceed 512 bytes.
+ // The raw data is combined with Question section and Additional section
+ // Question section : query "hello.example.com", type A, class IN
+ // Additional section : type OPT (41), Option PADDING, Option Length 546
+ // Padding option which allows DNS clients and servers to artificially
+ // increase the size of a DNS message by a variable number of bytes.
+ // See also RFC7830, section 3
+ const std::string query =
+ "+c0BAAABAAAAAAABBWhlbGxvB2V4YW1wbGUDY29tAAABAAEAACkgAAAAgAACJgAMAiIAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+ const std::string cmd =
+ "resnsend " + std::to_string(TEST_NETID) + " 0 " /* ResNsendFlags */ + query + '\0';
+ ssize_t rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size()));
+ EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size()));
+ expectAnswersValid(fd, AF_INET, kHelloExampleComAddrV4);
+ EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom));
+ EXPECT_EQ(0U, GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom));
+}
+
+TEST_F(ResolverTest, TruncatedRspMode) {
+ constexpr char listen_addr[] = "127.0.0.4";
+ constexpr char listen_addr2[] = "127.0.0.5";
+ constexpr char listen_srv[] = "53";
+
+ test::DNSResponder dns(listen_addr, listen_srv, static_cast<ns_rcode>(-1));
+ test::DNSResponder dns2(listen_addr2, listen_srv, static_cast<ns_rcode>(-1));
+ // dns supports UDP only, dns2 support UDP and TCP
+ dns.setResponseProbability(0.0, IPPROTO_TCP);
+ StartDns(dns, kLargeCnameChainRecords);
+ StartDns(dns2, kLargeCnameChainRecords);
+
+ const struct TestConfig {
+ const std::optional<int32_t> tcMode;
+ const bool ret;
+ const unsigned numQueries;
+ std::string asParameters() const {
+ return StringPrintf("tcMode: %d, ret: %s, numQueries: %u", tcMode.value_or(-1),
+ ret ? "true" : "false", numQueries);
+ }
+ } testConfigs[]{
+ // clang-format off
+ {std::nullopt, true, 0}, /* mode unset */
+ {aidl::android::net::IDnsResolver::TC_MODE_DEFAULT, true, 0}, /* default mode */
+ {aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP, true, 1}, /* alternative mode */
+ {-666, false, 1}, /* invalid input */
+ // clang-format on
+ };
+
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(config.asParameters());
+
+ ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ parcel.servers = {listen_addr, listen_addr2};
+ if (config.tcMode) {
+ parcel.experimentalOptions.tcMode = config.tcMode.value();
+ }
+ ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(), config.ret);
+
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_EQ(ToString(result), kHelloExampleComAddrV4);
+ // TC_MODE_DEFAULT: resolver retries on TCP-only on each name server.
+ // TC_MODE_UDP_TCP: resolver retries on TCP on the same server, falls back to UDP from next.
+ ASSERT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom), 1U);
+ ASSERT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom), 1U);
+ ASSERT_EQ(GetNumQueriesForProtocol(dns2, IPPROTO_UDP, kHelloExampleCom), config.numQueries);
+ ASSERT_EQ(GetNumQueriesForProtocol(dns2, IPPROTO_TCP, kHelloExampleCom), 1U);
+
+ dns.clearQueries();
+ dns2.clearQueries();
+ ASSERT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+ }
+}
+
+// Parameterized tests.
+// TODO: Merge the existing tests as parameterized test if possible.
+// TODO: Perhaps move parameterized tests to an independent file.
+enum class CallType { GETADDRINFO, GETHOSTBYNAME };
+class ResolverParameterizedTest : public ResolverTest,
+ public testing::WithParamInterface<CallType> {
+ protected:
+ void VerifyQueryHelloExampleComV4(const test::DNSResponder& dns, const CallType calltype,
+ const bool verifyNumQueries = true) {
+ if (calltype == CallType::GETADDRINFO) {
+ const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+ ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+ ASSERT_TRUE(result != nullptr);
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+ } else if (calltype == CallType::GETHOSTBYNAME) {
+ const hostent* result = gethostbyname("hello");
+ ASSERT_TRUE(result != nullptr);
+ ASSERT_EQ(4, result->h_length);
+ ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+ EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+ EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+ } else {
+ FAIL() << "Unsupported call type: " << static_cast<uint32_t>(calltype);
+ }
+ if (verifyNumQueries) EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(QueryCallTest, ResolverParameterizedTest,
+ testing::Values(CallType::GETADDRINFO, CallType::GETHOSTBYNAME),
+ [](const testing::TestParamInfo<CallType>& info) {
+ switch (info.param) {
+ case CallType::GETADDRINFO:
+ return "GetAddrInfo";
+ case CallType::GETHOSTBYNAME:
+ return "GetHostByName";
+ default:
+ return "InvalidParameter"; // Should not happen.
+ }
+ });
+
+TEST_P(ResolverParameterizedTest, AuthoritySectionAndAdditionalSection) {
+ // DNS response may have more information in authority section and additional section.
+ // Currently, getanswer() of packages/modules/DnsResolver/getaddrinfo.cpp doesn't parse the
+ // content of authority section and additional section. Test these sections if they crash
+ // the resolver, just in case. See also RFC 1035 section 4.1.
+ const auto& calltype = GetParam();
+ test::DNSHeader header(kDefaultDnsHeader);
+
+ // Create a DNS response which has a authoritative nameserver record in authority
+ // section and its relevant address record in additional section.
+ //
+ // Question
+ // hello.example.com. IN A
+ // Answer
+ // hello.example.com. IN A 1.2.3.4
+ // Authority:
+ // hello.example.com. IN NS ns1.example.com.
+ // Additional:
+ // ns1.example.com. IN A 5.6.7.8
+ //
+ // A response may have only question, answer, and authority section. Current testing response
+ // should be able to cover this condition.
+
+ // Question section.
+ test::DNSQuestion question{
+ .qname = {.name = kHelloExampleCom},
+ .qtype = ns_type::ns_t_a,
+ .qclass = ns_c_in,
+ };
+ header.questions.push_back(std::move(question));
+
+ // Answer section.
+ test::DNSRecord recordAnswer{
+ .name = {.name = kHelloExampleCom},
+ .rtype = ns_type::ns_t_a,
+ .rclass = ns_c_in,
+ .ttl = 0, // no cache
+ };
+ EXPECT_TRUE(test::DNSResponder::fillRdata(kHelloExampleComAddrV4, recordAnswer));
+ header.answers.push_back(std::move(recordAnswer));
+
+ // Authority section.
+ test::DNSRecord recordAuthority{
+ .name = {.name = kHelloExampleCom},
+ .rtype = ns_type::ns_t_ns,
+ .rclass = ns_c_in,
+ .ttl = 0, // no cache
+ };
+ EXPECT_TRUE(test::DNSResponder::fillRdata("ns1.example.com.", recordAuthority));
+ header.authorities.push_back(std::move(recordAuthority));
+
+ // Additional section.
+ test::DNSRecord recordAdditional{
+ .name = {.name = "ns1.example.com."},
+ .rtype = ns_type::ns_t_a,
+ .rclass = ns_c_in,
+ .ttl = 0, // no cache
+ };
+ EXPECT_TRUE(test::DNSResponder::fillRdata("5.6.7.8", recordAdditional));
+ header.additionals.push_back(std::move(recordAdditional));
+
+ // Start DNS server.
+ test::DNSResponder dns(test::DNSResponder::MappingType::DNS_HEADER);
+ dns.addMappingDnsHeader(kHelloExampleCom, ns_type::ns_t_a, header);
+ ASSERT_TRUE(dns.startServer());
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+ dns.clearQueries();
+
+ // Expect that get the address and the resolver doesn't crash.
+ VerifyQueryHelloExampleComV4(dns, calltype);
+}
+
+TEST_P(ResolverParameterizedTest, MessageCompression) {
+ const auto& calltype = GetParam();
+
+ // The response with compressed domain name by a pointer. See RFC 1035 section 4.1.4.
+ //
+ // Ignoring the other fields of the message, the domain name of question section and answer
+ // section are presented as:
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 12 | 5 | h |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 14 | e | l |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 16 | l | o |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 18 | 7 | e |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 20 | x | a |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 22 | m | p |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 24 | l | e |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 26 | 3 | c |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 28 | o | m |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 30 | 0 | ... |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ //
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 35 | 1 1| 12 |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ const std::vector<uint8_t> kResponseAPointer = {
+ /* Header */
+ 0x00, 0x00, /* Transaction ID: 0x0000 */
+ 0x81, 0x80, /* Flags: qr rd ra */
+ 0x00, 0x01, /* Questions: 1 */
+ 0x00, 0x01, /* Answer RRs: 1 */
+ 0x00, 0x00, /* Authority RRs: 0 */
+ 0x00, 0x00, /* Additional RRs: 0 */
+ /* Queries */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ /* Answers */
+ 0xc0, 0x0c, /* Name: hello.example.com (a pointer) */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ 0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+ 0x00, 0x04, /* Data length: 4 */
+ 0x01, 0x02, 0x03, 0x04 /* Address: 1.2.3.4 */
+ };
+
+ // The response with compressed domain name by a sequence of labels ending with a pointer. See
+ // RFC 1035 section 4.1.4.
+ //
+ // Ignoring the other fields of the message, the domain name of question section and answer
+ // section are presented as:
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 12 | 5 | h |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 14 | e | l |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 16 | l | o |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 18 | 7 | e |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 20 | x | a |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 22 | m | p |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 24 | l | e |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 26 | 3 | c |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 28 | o | m |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 30 | 0 | ... |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ //
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 35 | 5 | h |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 37 | e | l |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 39 | l | o |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // 41 | 1 1| 18 |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ const std::vector<uint8_t> kResponseLabelEndingWithAPointer = {
+ /* Header */
+ 0x00, 0x00, /* Transaction ID: 0x0000 */
+ 0x81, 0x80, /* Flags: qr rd ra */
+ 0x00, 0x01, /* Questions: 1 */
+ 0x00, 0x01, /* Answer RRs: 1 */
+ 0x00, 0x00, /* Authority RRs: 0 */
+ 0x00, 0x00, /* Additional RRs: 0 */
+ /* Queries */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ /* Answers */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xc0,
+ 0x12, /* Name: hello.example.com (a label ending with a pointer) */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ 0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+ 0x00, 0x04, /* Data length: 4 */
+ 0x01, 0x02, 0x03, 0x04 /* Address: 1.2.3.4 */
+ };
+
+ for (const auto& response : {kResponseAPointer, kResponseLabelEndingWithAPointer}) {
+ SCOPED_TRACE(StringPrintf("Hex dump: %s", toHex(makeSlice(response)).c_str()));
+
+ test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+ dns.addMappingBinaryPacket(kHelloExampleComQueryV4, response);
+ StartDns(dns, {});
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ // Expect no cache because the TTL of testing responses are 0.
+ VerifyQueryHelloExampleComV4(dns, calltype);
+ }
+}
+
+TEST_P(ResolverParameterizedTest, TruncatedResponse) {
+ const auto& calltype = GetParam();
+
+ test::DNSResponder dns;
+ StartDns(dns, kLargeCnameChainRecords);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ // Expect UDP response is truncated. The resolver retries over TCP. See RFC 1035 section 4.2.1.
+ VerifyQueryHelloExampleComV4(dns, calltype, false);
+ EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom));
+ EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom));
}
diff --git a/tests/resolv_stats_test_utils.cpp b/tests/resolv_stats_test_utils.cpp
new file mode 100644
index 0000000..d31e6c0
--- /dev/null
+++ b/tests/resolv_stats_test_utils.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ *
+ */
+
+#include "resolv_stats_test_utils.h"
+
+#include <iostream>
+#include <regex>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+using android::net::NetworkDnsEventReported;
+
+// The fromNetworkDnsEventReportedStr is a function to generate a protocol buffer message
+// NetworkDnsEventReported from a string. How to define the sting for NetworkDnsEventReported,
+// please refer to test case AlphabeticalHostname.
+// There are 3 proto messages(1. NetworkDnsEventReported 2. dns_query_events 3. dns_query_event)
+// The names of these 3 messages will not be verified, please do not change.
+// Each field will be parsed into corresponding message by those char pairs "{" & "}".
+// Example: (Don't need to fill Level 1 fields in NetworkDnsEventReported. Currently,
+// no verification is performed.)
+// NetworkDnsEventReported { <= construct the NetworkDnsEventReported message (Level 1)
+// dns_query_events:(Level 2)
+// { <= construct the dns_query_events message
+// dns_query_event:(Level 3)
+// { <= construct the 1st dns_query_event message
+// [field]: value,
+// [field]: value,
+// } <= finish the 1st dns_query_event message
+// dns_query_event:(Level 3)
+// { <= construct 2nd dns_query_event message
+// [field]: value,
+// [field]: value,
+// } <= finish the 1nd dns_query_event message
+// } <= finish the dns_query_events message
+// } <= finish the NetworkDnsEventReported message
+
+NetworkDnsEventReported fromNetworkDnsEventReportedStr(const std::string& str) {
+ using android::base::ParseInt;
+ using android::base::Split;
+ // Remove unnecessary space
+ std::regex re(": ");
+ std::string s = std::regex_replace(str, re, ":");
+ // Using space to separate each parse line
+ static const std::regex words_regex("[^\\s]+");
+ auto words_begin = std::sregex_iterator(s.begin(), s.end(), words_regex);
+ auto words_end = std::sregex_iterator();
+ // Using strproto to identify the position of NetworkDnsEventReported proto
+ int strproto = 0;
+ NetworkDnsEventReported event;
+ DnsQueryEvent* dnsQueryEvent = nullptr;
+ for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
+ std::string match_str = (*i).str();
+ // Using "{" and "}" to identify the Start/End of each proto
+ if (match_str == "{") {
+ // strproto 1.NetworkDnsEventReported 2.dns_query_events 3.dns_query_event
+ if (++strproto == 3) {
+ dnsQueryEvent = event.mutable_dns_query_events()->add_dns_query_event();
+ }
+ continue;
+ }
+ if (match_str == "}" | match_str == "},") {
+ strproto--;
+ continue;
+ }
+ // Parsing each field of the proto and fill it into NetworkDnsEventReported event
+ static const std::regex pieces_regex("([a-zA-Z0-9_]+)\\:([0-9]+),");
+ std::smatch protoField;
+ std::regex_match(match_str, protoField, pieces_regex);
+ int value = 0;
+ LOG(DEBUG) << "Str:" << match_str << " Name:" << protoField[1]
+ << " Value:" << protoField[2];
+ // Parsing each field of the proto NetworkDnsEventReported
+ if (strproto == 1) {
+ if (protoField[1] == "event_type" && ParseInt(protoField[2], &value)) {
+ event.set_event_type(static_cast<EventType>(value));
+ } else if (protoField[1] == "return_code" && ParseInt(protoField[2], &value)) {
+ event.set_return_code(static_cast<ReturnCode>(value));
+ } else if (protoField[1] == "latency_micros" && ParseInt(protoField[2], &value)) {
+ event.set_latency_micros(value);
+ } else if (protoField[1] == "hints_ai_flags" && ParseInt(protoField[2], &value)) {
+ event.set_hints_ai_flags(value);
+ } else if (protoField[1] == "res_nsend_flags" && ParseInt(protoField[2], &value)) {
+ event.set_res_nsend_flags(value);
+ } else if (protoField[1] == "network_type" && ParseInt(protoField[2], &value)) {
+ event.set_network_type(static_cast<NetworkType>(value));
+ } else if (protoField[1] == "private_dns_modes" && ParseInt(protoField[2], &value)) {
+ event.set_private_dns_modes(static_cast<PrivateDnsModes>(value));
+ } else if (protoField[1] == "sampling_rate_denom" && ParseInt(protoField[2], &value)) {
+ event.set_sampling_rate_denom(value);
+ }
+ }
+ // Parsing each field of the proto DnsQueryEvent
+ if (strproto == 3) {
+ if (dnsQueryEvent == nullptr) continue;
+ if (protoField[1] == "rcode" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(value));
+ } else if (protoField[1] == "type" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_type(static_cast<NsType>(value));
+ } else if (protoField[1] == "cache_hit" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(value));
+ } else if (protoField[1] == "ip_version" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_ip_version(static_cast<IpVersion>(value));
+ } else if (protoField[1] == "protocol" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_protocol(static_cast<Protocol>(value));
+ } else if (protoField[1] == "retry_times" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_retry_times(value);
+ } else if (protoField[1] == "dns_server_index" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_dns_server_index(value);
+ } else if (protoField[1] == "connected" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_connected(static_cast<bool>(value));
+ } else if (protoField[1] == "latency_micros" && ParseInt(protoField[2], &value)) {
+ dnsQueryEvent->set_latency_micros(value);
+ }
+ }
+ }
+ return event;
+}
+
+void PrintTo(const DnsQueryEvents& event, std::ostream* os) {
+ *os << "query events: {\n";
+ *os << " dns_query_event_size: " << event.dns_query_event_size() << "\n";
+ *os << "}";
+}
+
+void PrintTo(const DnsQueryEvent& event, std::ostream* os) {
+ *os << "dns query event: {\n";
+ *os << " rcode: " << event.rcode() << "\n";
+ *os << " ns_type: " << event.type() << "\n";
+ *os << " cache_hit: " << event.cache_hit() << "\n";
+ *os << " ip_version: " << event.ip_version() << "\n";
+ *os << " protocol: " << event.protocol() << "\n";
+ *os << " retry_times: " << event.retry_times() << "\n";
+ *os << " dns_server_index: " << event.dns_server_index() << "\n";
+ *os << " connected: " << event.connected() << "\n";
+ *os << " latency_micros: " << event.latency_micros() << "\n";
+ *os << "}";
+}
+
+void PrintTo(const NetworkDnsEventReported& event, std::ostream* os) {
+ *os << "network dns event: {\n";
+ *os << " event_type: " << event.event_type() << "\n";
+ *os << " return_code: " << event.return_code() << "\n";
+ *os << " latency_micros: " << event.latency_micros() << "\n";
+ *os << " hints_ai_flags: " << event.hints_ai_flags() << "\n";
+ *os << " res_nsend_flags: " << event.res_nsend_flags() << "\n";
+ *os << " network_type: " << event.network_type() << "\n";
+ *os << " private_dns_modes: " << event.private_dns_modes() << "\n";
+ *os << " dns_query_event_size: " << event.dns_query_events().dns_query_event_size() << "\n";
+ *os << "}";
+}
+
+} // namespace android::net
diff --git a/tests/resolv_stats_test_utils.h b/tests/resolv_stats_test_utils.h
new file mode 100644
index 0000000..cdfbe26
--- /dev/null
+++ b/tests/resolv_stats_test_utils.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+android::net::NetworkDnsEventReported fromNetworkDnsEventReportedStr(const std::string& str);
+
+// Used for gmock printing our desired debug messages. (Another approach is defining operator<<())
+void PrintTo(const DnsQueryEvents& event, std::ostream* os);
+void PrintTo(const DnsQueryEvent& event, std::ostream* os);
+void PrintTo(const android::net::NetworkDnsEventReported& event, std::ostream* os);
+
+MATCHER_P(DnsQueryEventEq, other, "") {
+ return ::testing::ExplainMatchResult(
+ ::testing::AllOf(
+ ::testing::Property("rcode", &DnsQueryEvent::rcode,
+ ::testing::Eq(other.rcode())),
+ ::testing::Property("ns_type", &DnsQueryEvent::type,
+ ::testing::Eq(other.type())),
+ ::testing::Property("cache_hit", &DnsQueryEvent::cache_hit,
+ ::testing::Eq(other.cache_hit())),
+ ::testing::Property("ip_version", &DnsQueryEvent::ip_version,
+ ::testing::Eq(other.ip_version())),
+ ::testing::Property("protocol", &DnsQueryEvent::protocol,
+ ::testing::Eq(other.protocol())),
+ ::testing::Property("retry_times", &DnsQueryEvent::retry_times,
+ ::testing::Eq(other.retry_times())),
+ ::testing::Property("dns_server_index", &DnsQueryEvent::dns_server_index,
+ ::testing::Eq(other.dns_server_index())),
+ // Removing the latency check, because we can't predict the time.
+ /* ::testing::Property("latency_micros", &DnsQueryEvent::latency_micros,
+ ::testing::Eq(other.latency_micros())),*/
+ ::testing::Property("connected", &DnsQueryEvent::connected,
+ ::testing::Eq(other.connected()))),
+ arg, result_listener);
+}
+
+MATCHER_P(DnsQueryEventsEq, other, "") {
+ const int eventSize = arg.dns_query_event_size();
+ if (eventSize != other.dns_query_event_size()) {
+ *result_listener << "Expected dns query event size: " << other.dns_query_event_size()
+ << " \n";
+ for (int i = 0; i < other.dns_query_event_size(); ++i)
+ PrintTo(other.dns_query_event(i), result_listener->stream());
+ *result_listener << "Actual dns query event size: " << eventSize << "\n";
+ for (int i = 0; i < eventSize; ++i)
+ PrintTo(arg.dns_query_event(i), result_listener->stream());
+ return false;
+ }
+
+ for (int i = 0; i < eventSize; ++i) {
+ bool result =
+ ::testing::Value(arg.dns_query_event(i), DnsQueryEventEq(other.dns_query_event(i)));
+ if (!result) {
+ *result_listener << "Expected event num: " << i << " \n";
+ PrintTo(arg.dns_query_event(i), result_listener->stream());
+ *result_listener << "Actual event num: " << i << " ";
+ PrintTo(other.dns_query_event(i), result_listener->stream());
+ return false;
+ }
+ }
+ return true;
+}
+
+MATCHER_P(NetworkDnsEventEq, other, "") {
+ return ::testing::ExplainMatchResult(
+ ::testing::AllOf(
+ // Removing following fields check, because we can't verify those fields in unit
+ // test.
+ /*
+ ::testing::Property("event_type",
+ &android::net::NetworkDnsEventReported::event_type,
+ ::testing::Eq(other.event_type())),
+ ::testing::Property("return_code",
+ &android::net::NetworkDnsEventReported::return_code,
+ ::testing::Eq(other.return_code())),
+ ::testing::Property("latency_micros",
+ &android::net::NetworkDnsEventReported::latency_micros,
+ ::testing::Eq(other.latency_micros())),
+ ::testing::Property("hints_ai_flags",
+ &android::net::NetworkDnsEventReported::hints_ai_flags,
+ ::testing::Eq(other.hints_ai_flags())),
+ ::testing::Property("res_nsend_flags",
+ &android::net::NetworkDnsEventReported::res_nsend_flags,
+ ::testing::Eq(other.res_nsend_flags())),
+ ::testing::Property("network_type",
+ &android::net::NetworkDnsEventReported::network_type,
+ ::testing::Eq(other.network_type())),
+ ::testing::Property("private_dns_modes",
+ &android::net::NetworkDnsEventReported::private_dns_modes,
+ ::testing::Eq(other.private_dns_modes())),
+ */
+ ::testing::Property("dns_query_events",
+ &android::net::NetworkDnsEventReported::dns_query_events,
+ DnsQueryEventsEq(other.dns_query_events()))),
+ arg, result_listener);
+}
+
+} // namespace android::net
diff --git a/tests/resolv_stats_test_utils_test.cpp b/tests/resolv_stats_test_utils_test.cpp
new file mode 100644
index 0000000..f6fecd8
--- /dev/null
+++ b/tests/resolv_stats_test_utils_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <resolv_stats_test_utils.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+TEST(ResolvStatsUtils, NetworkDnsEventEq) {
+ NetworkDnsEventReported event1;
+ // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+ // So don't need to config those values: event_type, return_code, latency_micros,
+ // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+ constexpr char event2[] = R"Event(
+ NetworkDnsEventReported {
+ dns_query_events:
+ {
+ dns_query_event:[
+ {
+ rcode: 3,
+ type: 28,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 3,
+ retry_times: 28,
+ dns_server_index: 0,
+ connected: 1,
+ latency_micros: 5,
+ },
+ {
+ rcode: 0,
+ type: 1,
+ cache_hit: 1,
+ ip_version: 1,
+ protocol: 1,
+ retry_times: 56,
+ dns_server_index: 1,
+ connected: 0,
+ latency_micros: 0,
+ }
+ ]
+ }
+ })Event";
+
+ // TODO: Add integration test to verify Level 1 fields of NetworkDnsEventReported.
+ // Level 1 fields, including event_type, return_code, hints_ai_flags, network_type, etc.
+ DnsQueryEvent* dnsQueryEvent1 = event1.mutable_dns_query_events()->add_dns_query_event();
+ dnsQueryEvent1->set_rcode(NS_R_NXDOMAIN);
+ dnsQueryEvent1->set_type(NS_T_AAAA);
+ dnsQueryEvent1->set_cache_hit(CS_NOTFOUND);
+ dnsQueryEvent1->set_ip_version(IV_IPV4);
+ dnsQueryEvent1->set_protocol(PROTO_DOT);
+ dnsQueryEvent1->set_retry_times(28);
+ dnsQueryEvent1->set_dns_server_index(0);
+ dnsQueryEvent1->set_connected(1);
+ dnsQueryEvent1->set_latency_micros(5);
+ DnsQueryEvent* dnsQueryEvent2 = event1.mutable_dns_query_events()->add_dns_query_event();
+ dnsQueryEvent2->set_rcode(NS_R_NO_ERROR);
+ dnsQueryEvent2->set_type(NS_T_A);
+ dnsQueryEvent2->set_cache_hit(CS_NOTFOUND);
+ dnsQueryEvent2->set_ip_version(IV_IPV4);
+ dnsQueryEvent2->set_protocol(PROTO_UDP);
+ dnsQueryEvent2->set_retry_times(56);
+ dnsQueryEvent2->set_dns_server_index(1);
+ dnsQueryEvent2->set_connected(0);
+ dnsQueryEvent2->set_latency_micros(5);
+ EXPECT_THAT(event1, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event2)));
+}
+
+} // namespace android::net
diff --git a/tests/resolv_stress_test.cpp b/tests/resolv_stress_test.cpp
index f3b6126..9058f17 100644
--- a/tests/resolv_stress_test.cpp
+++ b/tests/resolv_stress_test.cpp
@@ -26,8 +26,8 @@
#include <gtest/gtest.h>
#include "ResolverStats.h"
-#include "dns_responder/dns_responder_client.h"
-#include "netd_resolv/params.h" // MAX_NS
+#include "dns_responder/dns_responder_client_ndk.h"
+#include "params.h" // MAX_NS
#include "resolv_test_utils.h"
using android::base::StringPrintf;
@@ -87,9 +87,9 @@
res_params res_params;
std::vector<ResolverStats> res_stats;
int wait_for_pending_req_timeout_count;
- ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers,
- &res_domains, &res_tls_servers, &res_params, &res_stats,
- &wait_for_pending_req_timeout_count));
+ ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+ mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
+ &res_tls_servers, &res_params, &res_stats, &wait_for_pending_req_timeout_count));
EXPECT_EQ(0, wait_for_pending_req_timeout_count);
}
diff --git a/tests/resolv_test_utils.cpp b/tests/resolv_test_utils.cpp
index 4b2a83f..31245f3 100644
--- a/tests/resolv_test_utils.cpp
+++ b/tests/resolv_test_utils.cpp
@@ -21,7 +21,6 @@
#include <netdutils/InternetAddresses.h>
-using android::net::ResolverStats;
using android::netdutils::ScopedAddrinfo;
std::string ToString(const hostent* he) {
@@ -49,6 +48,36 @@
return ToString(ai.get());
}
+std::string ToString(const sockaddr_storage* addr) {
+ if (!addr) return "<null>";
+ char host[NI_MAXHOST];
+ int rv = getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), host, sizeof(host),
+ nullptr, 0, NI_NUMERICHOST);
+ if (rv != 0) return gai_strerror(rv);
+ return host;
+}
+
+std::vector<std::string> ToStrings(const hostent* he) {
+ std::vector<std::string> hosts;
+ if (he == nullptr) {
+ hosts.push_back("<null>");
+ return hosts;
+ }
+ uint32_t i = 0;
+ while (he->h_addr_list[i] != nullptr) {
+ char host[INET6_ADDRSTRLEN];
+ if (!inet_ntop(he->h_addrtype, he->h_addr_list[i], host, sizeof(host))) {
+ hosts.push_back("<invalid>");
+ return hosts;
+ } else {
+ hosts.push_back(host);
+ }
+ i++;
+ }
+ if (hosts.empty()) hosts.push_back("<invalid>");
+ return hosts;
+}
+
std::vector<std::string> ToStrings(const addrinfo* ai) {
std::vector<std::string> hosts;
if (!ai) {
@@ -76,10 +105,22 @@
}
size_t GetNumQueries(const test::DNSResponder& dns, const char* name) {
- auto queries = dns.queries();
+ std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
size_t found = 0;
for (const auto& p : queries) {
- if (p.first == name) {
+ if (p.name == name) {
+ ++found;
+ }
+ }
+ return found;
+}
+
+size_t GetNumQueriesForProtocol(const test::DNSResponder& dns, const int protocol,
+ const char* name) {
+ std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
+ size_t found = 0;
+ for (const auto& p : queries) {
+ if (p.protocol == protocol && p.name == name) {
++found;
}
}
@@ -87,42 +128,12 @@
}
size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name) {
- auto queries = dns.queries();
+ std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
size_t found = 0;
for (const auto& p : queries) {
- if (p.second == type && p.first == name) {
+ if (p.type == type && p.name == name) {
++found;
}
}
return found;
}
-
-bool GetResolverInfo(android::net::IDnsResolver* dnsResolverService, unsigned netId,
- std::vector<std::string>* servers, std::vector<std::string>* domains,
- std::vector<std::string>* tlsServers, res_params* params,
- std::vector<ResolverStats>* stats, int* wait_for_pending_req_timeout_count) {
- using android::net::IDnsResolver;
- std::vector<int32_t> params32;
- std::vector<int32_t> stats32;
- std::vector<int32_t> wait_for_pending_req_timeout_count32{0};
- auto rv = dnsResolverService->getResolverInfo(netId, servers, domains, tlsServers, ¶ms32,
- &stats32, &wait_for_pending_req_timeout_count32);
-
- if (!rv.isOk() || params32.size() != static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT)) {
- return false;
- }
- *params = res_params{
- .sample_validity =
- static_cast<uint16_t>(params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
- .success_threshold =
- static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
- .min_samples =
- static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]),
- .max_samples =
- static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]),
- .base_timeout_msec = params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
- .retry_count = params32[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT],
- };
- *wait_for_pending_req_timeout_count = wait_for_pending_req_timeout_count32[0];
- return ResolverStats::decodeAll(stats32, stats);
-}
\ No newline at end of file
diff --git a/tests/resolv_test_utils.h b/tests/resolv_test_utils.h
index 987d289..5495185 100644
--- a/tests/resolv_test_utils.h
+++ b/tests/resolv_test_utils.h
@@ -25,24 +25,65 @@
#include <netdutils/InternetAddresses.h>
-#include "ResolverStats.h"
-
-#include "android/net/IDnsResolver.h"
#include "dns_responder/dns_responder.h"
-#include "netd_resolv/params.h"
+
+struct DnsRecord {
+ std::string host_name; // host name
+ ns_type type; // record type
+ std::string addr; // ipv4/v6 address
+};
// TODO: make this dynamic and stop depending on implementation details.
constexpr int TEST_NETID = 30;
-// Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates that any type or
-// protocol can be returned by getaddrinfo().
-constexpr unsigned int ANY = 0;
-
static constexpr char kLocalHost[] = "localhost";
static constexpr char kLocalHostAddr[] = "127.0.0.1";
static constexpr char kIp6LocalHost[] = "ip6-localhost";
static constexpr char kIp6LocalHostAddr[] = "::1";
static constexpr char kHelloExampleCom[] = "hello.example.com.";
+static constexpr char kHelloExampleComAddrV4[] = "1.2.3.4";
+static constexpr char kHelloExampleComAddrV6[] = "::1.2.3.4";
+static constexpr char kExampleComDomain[] = ".example.com";
+
+constexpr size_t kMaxmiumLabelSize = 63; // see RFC 1035 section 2.3.4.
+
+static const std::vector<uint8_t> kHelloExampleComQueryV4 = {
+ /* Header */
+ 0x00, 0x00, /* Transaction ID: 0x0000 */
+ 0x01, 0x00, /* Flags: rd */
+ 0x00, 0x01, /* Questions: 1 */
+ 0x00, 0x00, /* Answer RRs: 0 */
+ 0x00, 0x00, /* Authority RRs: 0 */
+ 0x00, 0x00, /* Additional RRs: 0 */
+ /* Queries */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01 /* Class: IN */
+};
+
+static const std::vector<uint8_t> kHelloExampleComResponseV4 = {
+ /* Header */
+ 0x00, 0x00, /* Transaction ID: 0x0000 */
+ 0x81, 0x80, /* Flags: qr rd ra */
+ 0x00, 0x01, /* Questions: 1 */
+ 0x00, 0x01, /* Answer RRs: 1 */
+ 0x00, 0x00, /* Authority RRs: 0 */
+ 0x00, 0x00, /* Additional RRs: 0 */
+ /* Queries */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ /* Answers */
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+ 0x00, 0x01, /* Type: A */
+ 0x00, 0x01, /* Class: IN */
+ 0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+ 0x00, 0x04, /* Data length: 4 */
+ 0x01, 0x02, 0x03, 0x04 /* Address: 1.2.3.4 */
+};
// Illegal hostnames
static constexpr char kBadCharAfterPeriodHost[] = "hello.example.^com.";
@@ -64,16 +105,40 @@
.ad = false, // non-authenticated data is unacceptable
};
+// The CNAME chain records for building a response message which exceeds 512 bytes.
+//
+// Ignoring the other fields of the message, the response message has 8 CNAMEs in 5 answer RRs
+// and each CNAME has 77 bytes as the follows. The response message at least has 616 bytes in
+// answer section and has already exceeded 512 bytes totally.
+//
+// The CNAME is presented as:
+// 0 1 64 65 72 73 76 77
+// +---+--........--+---+---+---+---+---+---+---+---+---+---+---+---+---+
+// | 63| {x, .., x} | 7 | e | x | a | m | p | l | e | 3 | c | o | m | 0 |
+// +---+--........--+---+---+---+---+---+---+---+---+---+---+---+---+---+
+// ^-- x = {a, b, c, d}
+//
+const std::string kCnameA = std::string(kMaxmiumLabelSize, 'a') + kExampleComDomain + ".";
+const std::string kCnameB = std::string(kMaxmiumLabelSize, 'b') + kExampleComDomain + ".";
+const std::string kCnameC = std::string(kMaxmiumLabelSize, 'c') + kExampleComDomain + ".";
+const std::string kCnameD = std::string(kMaxmiumLabelSize, 'd') + kExampleComDomain + ".";
+const std::vector<DnsRecord> kLargeCnameChainRecords = {
+ {kHelloExampleCom, ns_type::ns_t_cname, kCnameA},
+ {kCnameA, ns_type::ns_t_cname, kCnameB},
+ {kCnameB, ns_type::ns_t_cname, kCnameC},
+ {kCnameC, ns_type::ns_t_cname, kCnameD},
+ {kCnameD, ns_type::ns_t_a, kHelloExampleComAddrV4},
+};
+
+// TODO: Integrate GetNumQueries relevent functions
size_t GetNumQueries(const test::DNSResponder& dns, const char* name);
+size_t GetNumQueriesForProtocol(const test::DNSResponder& dns, const int protocol,
+ const char* name);
size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name);
std::string ToString(const hostent* he);
std::string ToString(const addrinfo* ai);
std::string ToString(const android::netdutils::ScopedAddrinfo& ai);
+std::string ToString(const sockaddr_storage* addr);
+std::vector<std::string> ToStrings(const hostent* he);
std::vector<std::string> ToStrings(const addrinfo* ai);
std::vector<std::string> ToStrings(const android::netdutils::ScopedAddrinfo& ai);
-
-bool GetResolverInfo(android::net::IDnsResolver* dnsResolverService, unsigned netId,
- std::vector<std::string>* servers, std::vector<std::string>* domains,
- std::vector<std::string>* tlsServers, res_params* params,
- std::vector<android::net::ResolverStats>* stats,
- int* wait_for_pending_req_timeout_count);
diff --git a/tests/testdata/README.md b/tests/testdata/README.md
new file mode 100644
index 0000000..4ebd2b0
--- /dev/null
+++ b/tests/testdata/README.md
@@ -0,0 +1,157 @@
+
+# Resolv Gold Test
+The "Resolv Gold Test" targets to run automatically in presubmit, as a change
+detector to ensure that the resolver doesn't send the query or parse the
+response unexpectedly.
+
+## Build testing pbtext
+The testing pbtext is built manually so far. Fill expected API parameters to
+'config' and expected answers to 'result' in pbtext. Then, record the
+corresponding DNS query and response packets. Fill the packets with the \x
+formatting into 'query' and 'response' in pbtext. Perhaps have a mechanism
+to generate the pbtxt automatically in the future.
+
+### Using 'ping' utility to be an example for building pbtext
+Here demonstrates how the pbtext is built.
+
+1. Enable resolver debug log level to VERBOSE (0)
+```
+$ adb shell service call dnsresolver 10 i32 0
+```
+2. Ping a website
+```
+$ adb shell ping www.google.com
+```
+3. Get bugreport
+```
+$ adb bugreport
+```
+4. Search the log pattern as the follows in bugreport
+```
+# API arguments
+resolv : logArguments: argv[0]=gethostbyname
+resolv : logArguments: argv[1]=0
+resolv : logArguments: argv[2]=www.google.com
+resolv : logArguments: argv[3]=2
+
+# Query packet
+resolv : ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29827
+resolv : ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
+resolv : ;; QUERY SECTION:
+resolv : ;; www.google.com, type = A, class = IN
+resolv :
+resolv : Hex dump:
+resolv : 7483010000010000000000000377777706676f6f676c6503636f6d0000010001
+
+# Response packet
+resolv : ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29827
+resolv : ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+resolv : ;; QUERY SECTION:
+resolv : ;; www.google.com, type = A, class = IN
+resolv :
+resolv : ;; ANSWER SECTION:
+resolv : ;; www.google.com. 2m19s IN A 172.217.160.100
+resolv :
+resolv : Hex dump:
+resolv : 7483818000010001000000000377777706676f6f676c6503636f6d0000010001
+resolv : c00c000100010000008b0004acd9a064
+```
+
+5. Convert the logging into pbtext. Then, Clear the 'id' which is 0x7483 in
+this example from 'query' and 'response' because 'id' is regenerated per
+session. The follows is result pbtext.
+```
+config {
+ call: CALL_GETHOSTBYNAME
+ hostbyname {
+ host: "www.google.com."
+ family: GT_AF_INET
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "172.217.160.100"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ "\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x8b\x00\x04\xac\xd9\xa0\x64"
+}
+```
+
+## Decode packets in pbtext
+You can invoke the [scapy](https://scapy.net) of python module to extract
+binary packet record in pbtext file. Here are the instructions and example
+for parsing packet.
+
+### Instructions
+Run the following instruction to decode.
+```
+$ python
+>>> from scapy import all as scapy
+>>> scapy.DNS("<paste_hex_string>").show2()
+```
+
+### Example
+Using 'getaddrinfo.topsite.youtube.pbtxt' to be an example here.
+
+1. Find the packet record 'query' or 'response' in .pbtext file.
+```
+query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+ "\x01"
+```
+2. Run the following instruction.
+
+Start python
+```
+$ python
+```
+Import scapy
+```
+>>> from scapy import all as scapy
+```
+Assign the binary packet to be decoded into a variable. Beware of using
+backslash '\\' for new line if required
+```
+>>> raw_packet=\
+ "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77" \
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00" \
+ "\x01"
+```
+Decode packet
+```
+>>> scapy.DNS(raw_packet).show2()
+###[ DNS ]###
+ id = 0
+ qr = 0
+ opcode = QUERY
+ aa = 0
+ tc = 0
+ rd = 1
+ ra = 0
+ z = 0
+ ad = 0
+ cd = 0
+ rcode = ok
+ qdcount = 1
+ ancount = 0
+ nscount = 0
+ arcount = 0
+ \qd \
+ |###[ DNS Question Record ]###
+ | qname = 'www.youtube.com.'
+ | qtype = AAAA
+ | qclass = IN
+ an = None
+ ns = None
+ ar = None
+```
+
+## Running the tests
+Run the following instruction to test.
+```
+atest resolv_gold_test
+```
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt b/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt
new file mode 100644
index 0000000..a4c1661
--- /dev/null
+++ b/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt
@@ -0,0 +1,61 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 26 NOV 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.google.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2404:6800:4008:801::2004"
+ addresses: "172.217.27.132"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ "\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x55\x00\x0c\x00\x51\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ "\xc0\x0c\x00\x1c\x00\x01\x00\x00\x01\x2b\x00\x10\x24\x04\x68\x00"
+ "\x40\x08\x08\x01\x00\x00\x00\x00\x00\x00\x20\x04\x00\x00\x29\x02"
+ "\x00\x00\x00\x80\x00\x01\x8d\x00\x0c\x01\x89\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ "\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x55\x00\x0c\x00\x51\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ "\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x86\x00\x04\xac\xd9\x1b\x84"
+ "\x00\x00\x29\x02\x00\x00\x00\x80\x00\x01\x99\x00\x0c\x01\x95\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.amazon.pbtxt b/tests/testdata/getaddrinfo.topsite.amazon.pbtxt
new file mode 100644
index 0000000..4b305f1
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.amazon.pbtxt
@@ -0,0 +1,45 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.amazon.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "64:ff9b::d23:9aa5"
+ addresses: "104.124.254.132"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ "\xc0\x0c\x00\x05\x00\x01\x00\x00\x03\x45\x00\x0a\x03\x77\x77\x77"
+ "\x03\x63\x64\x6e\xc0\x10\xc0\x2c\x00\x05\x00\x01\x00\x00\x00\x06"
+ "\x00\x1f\x0e\x64\x33\x61\x67\x34\x68\x75\x6b\x6b\x68\x36\x32\x79"
+ "\x6e\x0a\x63\x6c\x6f\x75\x64\x66\x72\x6f\x6e\x74\x03\x6e\x65\x74"
+ "\x00\xc0\x42\x00\x1c\x00\x01\x00\x00\x00\x3b\x00\x10\x00\x64\xff"
+ "\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x23\x9a\xa5"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x04\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xbe\x00\x0a\x03\x77\x77\x77"
+ "\x03\x63\x64\x6e\xc0\x10\xc0\x2c\x00\x05\x00\x01\x00\x00\x00\x20"
+ "\x00\x1c\x03\x77\x77\x77\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f"
+ "\x6d\x07\x65\x64\x67\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x42"
+ "\x00\x05\x00\x01\x00\x00\x01\x10\x00\x18\x06\x65\x31\x35\x33\x31"
+ "\x36\x03\x65\x32\x32\x0a\x61\x6b\x61\x6d\x61\x69\x65\x64\x67\x65"
+ "\xc0\x59\xc0\x6a\x00\x01\x00\x01\x00\x00\x00\x13\x00\x04\x68\x7c"
+ "\xfe\x84"
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.bing.pbtxt b/tests/testdata/getaddrinfo.topsite.bing.pbtxt
new file mode 100644
index 0000000..b7bfb50
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.bing.pbtxt
@@ -0,0 +1,48 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.bing.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2620:1ec:c11::200"
+ addresses: "204.79.197.200"
+ addresses: "13.107.21.200"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+ "\x00\x05\x00\x01\x00\x00\x0b\x41\x00\x2a\x06\x61\x2d\x30\x30\x30"
+ "\x31\x0a\x61\x2d\x61\x66\x64\x65\x6e\x74\x72\x79\x03\x6e\x65\x74"
+ "\x0e\x74\x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\x72\x03"
+ "\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00\x00\x00\x04\x00\x17"
+ "\x0b\x64\x75\x61\x6c\x2d\x61\x2d\x30\x30\x30\x31\x08\x61\x2d\x6d"
+ "\x73\x65\x64\x67\x65\xc0\x4f\xc0\x60\x00\x1c\x00\x01\x00\x00\x00"
+ "\x01\x00\x10\x26\x20\x01\xec\x0c\x11\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x02\x00"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x04\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+ "\x00\x05\x00\x01\x00\x00\x0c\x69\x00\x2a\x06\x61\x2d\x30\x30\x30"
+ "\x31\x0a\x61\x2d\x61\x66\x64\x65\x6e\x74\x72\x79\x03\x6e\x65\x74"
+ "\x0e\x74\x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\x72\x03"
+ "\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00\x00\x00\x0d\x00\x17"
+ "\x0b\x64\x75\x61\x6c\x2d\x61\x2d\x30\x30\x30\x31\x08\x61\x2d\x6d"
+ "\x73\x65\x64\x67\x65\xc0\x4f\xc0\x60\x00\x01\x00\x01\x00\x00\x00"
+ "\x2c\x00\x04\xcc\x4f\xc5\xc8\xc0\x60\x00\x01\x00\x01\x00\x00\x00"
+ "\x2c\x00\x04\x0d\x6b\x15\xc8"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.ebay.pbtxt b/tests/testdata/getaddrinfo.topsite.ebay.pbtxt
new file mode 100644
index 0000000..2f562f0
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.ebay.pbtxt
@@ -0,0 +1,44 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.ebay.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "64:ff9b::685d:168"
+ addresses: "104.93.1.104"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+ "\x00\x05\x00\x01\x00\x00\x00\x0b\x00\x1f\x08\x73\x6c\x6f\x74\x39"
+ "\x34\x32\x38\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x07\x65\x64\x67"
+ "\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00"
+ "\x00\x07\xe6\x00\x15\x05\x65\x39\x34\x32\x38\x01\x62\x0a\x61\x6b"
+ "\x61\x6d\x61\x69\x65\x64\x67\x65\xc0\x44\xc0\x55\x00\x1c\x00\x01"
+ "\x00\x00\x00\x13\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x68\x5d\x01\x68"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+ "\x00\x05\x00\x01\x00\x00\x00\x76\x00\x1f\x08\x73\x6c\x6f\x74\x39"
+ "\x34\x32\x38\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x07\x65\x64\x67"
+ "\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00"
+ "\x00\x08\x28\x00\x15\x05\x65\x39\x34\x32\x38\x01\x62\x0a\x61\x6b"
+ "\x61\x6d\x61\x69\x65\x64\x67\x65\xc0\x44\xc0\x55\x00\x01\x00\x01"
+ "\x00\x00\x00\x13\x00\x04\x68\x5d\x01\x68"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.facebook.pbtxt b/tests/testdata/getaddrinfo.topsite.facebook.pbtxt
new file mode 100644
index 0000000..9f910f2
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.facebook.pbtxt
@@ -0,0 +1,34 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "facebook.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2a03:2880:f102:83:face:b00c:0:25de"
+ addresses: "31.13.95.36"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x08\x66\x61\x63"
+ "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x08\x66\x61\x63"
+ "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+ "\x00\x1c\x00\x01\x00\x00\x01\x2b\x00\x10\x2a\x03\x28\x80\xf1\x02"
+ "\x00\x83\xfa\xce\xb0\x0c\x00\x00\x25\xde"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x08\x66\x61\x63"
+ "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x08\x66\x61\x63"
+ "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+ "\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\x1f\x0d\x5f\x24"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.google.pbtxt b/tests/testdata/getaddrinfo.topsite.google.pbtxt
new file mode 100644
index 0000000..bc40a33
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.google.pbtxt
@@ -0,0 +1,86 @@
+# Location: Tokyo, Japan
+# Network: IIJ Mobile
+# Date: 16 AUG 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.google.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2404:6800:4004:80f::2004"
+ addresses: "172.217.26.4"
+}
+packet_mapping {
+ query: # Header
+ "\x00\x00" # Transaction ID: 0x0000
+ "\x01\x00" # Flags: rd
+ "\x00\x01" # Questions: 1
+ "\x00\x00" # Answer RRs: 0
+ "\x00\x00" # Authority RRs: 0
+ "\x00\x00" # Additional RRs: 0
+ # Queries
+ "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+ # Name: www.google.com
+ "\x00\x1c" # Type: AAAA
+ "\x00\x01" # Class: IN
+ response: # Header
+ "\x00\x00" # Transaction ID: 0x0000
+ "\x81\x80" # Flags: qr rd ra
+ "\x00\x01" # Questions: 1
+ "\x00\x01" # Answer RRs: 1
+ "\x00\x00" # Authority RRs: 0
+ "\x00\x00" # Additional RRs: 0
+ # Queries
+ "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+ # Name: www.google.com
+ "\x00\x1c" # Type: AAAA
+ "\x00\x01" # Class: IN
+ # Answers
+ "\xc0\x0c" # Name: www.google.com
+ "\x00\x1c" # Type: AAAA
+ "\x00\x01" # Class: IN
+ "\x00\x00\x00\xee" # Time to live: 238
+ "\x00\x10" # Data length: 16
+ "\x24\x04\x68\x00\x40\x04\x08\x0f\x00\x00\x00\x00\x00\x00\x20\x04"
+ # Address: 2404:6800:4004:80f::2004
+}
+packet_mapping {
+ query: # Header
+ "\x00\x00" # Transaction ID: 0x0000
+ "\x01\x00" # Flags: rd
+ "\x00\x01" # Questions: 1
+ "\x00\x00" # Answer RRs: 0
+ "\x00\x00" # Authority RRs: 0
+ "\x00\x00" # Additional RRs: 0
+ # Queries
+ "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+ # Name: www.google.com
+ "\x00\x01" # Type: A
+ "\x00\x01" # Class: IN
+ response: # Header
+ "\x00\x00" # Transaction ID: 0x0000
+ "\x81\x80" # Flags: qr rd ra
+ "\x00\x01" # Questions: 1
+ "\x00\x01" # Answer RRs: 1
+ "\x00\x00" # Authority RRs: 0
+ "\x00\x00" # Additional RRs: 0
+ # Queries
+ "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+ # Name: www.google.com
+ "\x00\x01" # Type: A
+ "\x00\x01" # Class: IN
+ # Answers
+ "\xc0\x0c" # Name: www.google.com
+ "\x00\x01" # Type: A
+ "\x00\x01" # Class: IN
+ "\x00\x00\x00\x03" # Time to live: 3
+ "\x00\x04" # Data length: 4
+ "\xac\xd9\x1a\x04" # Address: 172.217.26.4
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.netflix.pbtxt b/tests/testdata/getaddrinfo.topsite.netflix.pbtxt
new file mode 100644
index 0000000..1b12eb4
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.netflix.pbtxt
@@ -0,0 +1,77 @@
+# Location: Tokyo, Japan
+# Network: IIJ Mobile
+# Date: 14 AUG 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.netflix.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2620:108:700f::22d1:6443"
+ addresses: "2620:108:700f::36c8:5c97"
+ addresses: "2620:108:700f::36ba:3a73"
+ addresses: "2620:108:700f::36bf:5e94"
+ addresses: "2620:108:700f::3644:8d41"
+ addresses: "2620:108:700f::36bb:ed4c"
+ addresses: "2620:108:700f::3645:f643"
+ addresses: "2620:108:700f::3694:303e"
+ addresses: "52.35.228.185"
+ addresses: "52.38.194.36"
+ addresses: "52.33.42.218"
+ addresses: "52.33.159.26"
+ addresses: "52.35.47.68"
+ addresses: "52.36.31.140"
+ addresses: "52.35.125.204"
+ addresses: "52.39.27.188"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+ "\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x0a\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x79\x00\x0a\x03\x77\x77"
+ "\x77\x03\x67\x65\x6f\xc0\x10\xc0\x2d\x00\x05\x00\x01\x00\x00\x00"
+ "\x79\x00\x17\x03\x77\x77\x77\x09\x75\x73\x2d\x77\x65\x73\x74\x2d"
+ "\x32\x06\x70\x72\x6f\x64\x61\x61\xc0\x10\xc0\x43\x00\x1c\x00\x01"
+ "\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00"
+ "\x00\x00\x22\xd1\x64\x43\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f"
+ "\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xc8"
+ "\x5c\x97\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20"
+ "\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xba\x3a\x73\xc0\x43"
+ "\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f"
+ "\x00\x00\x00\x00\x00\x00\x36\xbf\x5e\x94\xc0\x43\x00\x1c\x00\x01"
+ "\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00"
+ "\x00\x00\x36\x44\x8d\x41\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f"
+ "\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xbb"
+ "\xed\x4c\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20"
+ "\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\x45\xf6\x43\xc0\x43"
+ "\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f"
+ "\x00\x00\x00\x00\x00\x00\x36\x94\x30\x3e"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x0a\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x82\x00\x0a\x03\x77\x77"
+ "\x77\x03\x67\x65\x6f\xc0\x10\xc0\x2d\x00\x05\x00\x01\x00\x00\x00"
+ "\x82\x00\x17\x03\x77\x77\x77\x09\x75\x73\x2d\x77\x65\x73\x74\x2d"
+ "\x32\x06\x70\x72\x6f\x64\x61\x61\xc0\x10\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x23\xe4\xb9\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x26\xc2\x24\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x21\x2a\xda\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x21\x9f\x1a\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x23\x2f\x44\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x24\x1f\x8c\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x23\x7d\xcc\xc0\x43\x00\x01\x00\x01"
+ "\x00\x00\x00\x0c\x00\x04\x34\x27\x1b\xbc"
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.reddit.pbtxt b/tests/testdata/getaddrinfo.topsite.reddit.pbtxt
new file mode 100644
index 0000000..3ccf721
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.reddit.pbtxt
@@ -0,0 +1,54 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.reddit.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "64:ff9b::9765:18c"
+ addresses: "64:ff9b::9765:418c"
+ addresses: "64:ff9b::9765:818c"
+ addresses: "64:ff9b::9765:c18c"
+ addresses: "151.101.1.140"
+ addresses: "151.101.65.140"
+ addresses: "151.101.129.140"
+ addresses: "151.101.193.140"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x53\x00\x17\x06\x72\x65\x64"
+ "\x64\x69\x74\x03\x6d\x61\x70\x06\x66\x61\x73\x74\x6c\x79\x03\x6e"
+ "\x65\x74\x00\xc0\x2c\x00\x1c\x00\x01\x00\x00\x00\x1d\x00\x10\x00"
+ "\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x97\x65\x01\x8c\xc0"
+ "\x2c\x00\x1c\x00\x01\x00\x00\x00\x1d\x00\x10\x00\x64\xff\x9b\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x97\x65\x41\x8c\xc0\x2c\x00\x1c\x00"
+ "\x01\x00\x00\x00\x1d\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x97\x65\x81\x8c\xc0\x2c\x00\x1c\x00\x01\x00\x00\x00"
+ "\x1d\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x97"
+ "\x65\xc1\x8c"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xc9\x00\x17\x06\x72\x65\x64"
+ "\x64\x69\x74\x03\x6d\x61\x70\x06\x66\x61\x73\x74\x6c\x79\x03\x6e"
+ "\x65\x74\x00\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+ "\x65\x01\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+ "\x65\x41\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+ "\x65\x81\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+ "\x65\xc1\x8c"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt b/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt
new file mode 100644
index 0000000..c6415f0
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt
@@ -0,0 +1,40 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.wikipedia.org."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2001:df2:e500:ed1a::1"
+ addresses: "103.102.166.224"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+ "\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+ "\x1c\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x52\x6a\x00\x11\x04"
+ "\x64\x79\x6e\x61\x09\x77\x69\x6b\x69\x6d\x65\x64\x69\x61\xc0\x1a"
+ "\xc0\x2f\x00\x1c\x00\x01\x00\x00\x02\x57\x00\x10\x20\x01\x0d\xf2"
+ "\xe5\x00\xed\x1a\x00\x00\x00\x00\x00\x00\x00\x01"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+ "\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+ "\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x51\xa8\x00\x11\x04"
+ "\x64\x79\x6e\x61\x09\x77\x69\x6b\x69\x6d\x65\x64\x69\x61\xc0\x1a"
+ "\xc0\x2f\x00\x01\x00\x01\x00\x00\x02\x57\x00\x04\x67\x66\xa6\xe0"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt b/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt
new file mode 100644
index 0000000..9223dad
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt
@@ -0,0 +1,44 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.yahoo.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2406:2000:ec:815::3"
+ addresses: "2406:2000:ec:815::4"
+ addresses: "124.108.103.104"
+ addresses: "124.108.103.103"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0"
+ "\x0c\x00\x05\x00\x01\x00\x00\x02\x29\x00\x16\x0d\x61\x74\x73\x76"
+ "\x32\x2d\x66\x70\x2d\x73\x68\x65\x64\x03\x77\x67\x31\x01\x62\xc0"
+ "\x10\xc0\x2b\x00\x1c\x00\x01\x00\x00\x00\x36\x00\x10\x24\x06\x20"
+ "\x00\x00\xec\x08\x15\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x2b\x00"
+ "\x1c\x00\x01\x00\x00\x00\x36\x00\x10\x24\x06\x20\x00\x00\xec\x08"
+ "\x15\x00\x00\x00\x00\x00\x00\x00\x04"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0"
+ "\x0c\x00\x05\x00\x01\x00\x00\x06\xe3\x00\x16\x0d\x61\x74\x73\x76"
+ "\x32\x2d\x66\x70\x2d\x73\x68\x65\x64\x03\x77\x67\x31\x01\x62\xc0"
+ "\x10\xc0\x2b\x00\x01\x00\x01\x00\x00\x00\x0e\x00\x04\x7c\x6c\x67"
+ "\x68\xc0\x2b\x00\x01\x00\x01\x00\x00\x00\x0e\x00\x04\x7c\x6c\x67"
+ "\x67"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.youtube.pbtxt b/tests/testdata/getaddrinfo.topsite.youtube.pbtxt
new file mode 100644
index 0000000..6ee64e4
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.youtube.pbtxt
@@ -0,0 +1,49 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+ call: CALL_GETADDRINFO
+ addrinfo {
+ host: "www.youtube.com."
+ family: GT_AF_UNSPEC
+ socktype: GT_SOCK_DGRAM
+ protocol: GT_IPPROTO_IP
+ ai_flags: 1024
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "2404:6800:4012:1::200e"
+ addresses: "172.217.24.14"
+ addresses: "216.58.200.46"
+ addresses: "172.217.160.78"
+ addresses: "172.217.27.142"
+ addresses: "172.217.160.110"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+ "\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x4c\x00\x16\x0a\x79\x6f"
+ "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+ "\x65\xc0\x18\xc0\x2d\x00\x1c\x00\x01\x00\x00\x01\x18\x00\x10\x24"
+ "\x04\x68\x00\x40\x12\x00\x01\x00\x00\x00\x00\x00\x00\x20\x0e"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x2c\x00\x16\x0a\x79\x6f"
+ "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+ "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+ "\xd9\x18\x0e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xd8"
+ "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+ "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+ "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+ "\xd9\xa0\x6e"
+}
\ No newline at end of file
diff --git a/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt b/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt
new file mode 100644
index 0000000..5f32518
--- /dev/null
+++ b/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt
@@ -0,0 +1,48 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 26 NOV 2019
+
+config {
+ call: CALL_GETHOSTBYNAME
+ hostbyname {
+ host: "www.youtube.com."
+ family: GT_AF_INET
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "216.58.200.46"
+ addresses: "172.217.160.78"
+ addresses: "216.58.200.238"
+ addresses: "172.217.27.142"
+ addresses: "172.217.160.110"
+ addresses: "172.217.24.14"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x54\x00\x0c\x00\x50"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x07\x00\x00\x00\x01\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x24\x00\x16\x0a\x79\x6f"
+ "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+ "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+ "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+ "\x3a\xc8\xee\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\xa0\x6e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\x18\x0e\x00\x00\x29\x02\x00\x00\x00\x80\x00\x01\x26\x00\x0c"
+ "\x01\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00"
+}
\ No newline at end of file
diff --git a/tests/testdata/gethostbyname.topsite.youtube.pbtxt b/tests/testdata/gethostbyname.topsite.youtube.pbtxt
new file mode 100644
index 0000000..d113e55
--- /dev/null
+++ b/tests/testdata/gethostbyname.topsite.youtube.pbtxt
@@ -0,0 +1,34 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 15 OCT 2019
+
+config {
+ call: CALL_GETHOSTBYNAME
+ hostbyname {
+ host: "www.youtube.com."
+ family: GT_AF_INET
+ };
+}
+result {
+ return_code: GT_EAI_NO_ERROR
+ addresses: "216.58.200.46"
+ addresses: "172.217.160.78"
+ addresses: "172.217.27.142"
+ addresses: "172.217.160.110"
+ addresses: "172.217.24.14"
+}
+packet_mapping {
+ query: "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01"
+ response: "\x00\x00\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x03\x77\x77\x77"
+ "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+ "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x5f\x00\x16\x0a\x79\x6f"
+ "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+ "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+ "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\xa0\x6e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+ "\xd9\x18\x0e"
+}
\ No newline at end of file
diff --git a/util.cpp b/util.cpp
new file mode 100644
index 0000000..b8df74c
--- /dev/null
+++ b/util.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ */
+
+#include "util.h"
+
+#include <android-base/parseint.h>
+#include <server_configurable_flags/get_flags.h>
+
+using android::base::ParseInt;
+using server_configurable_flags::GetServerConfigurableFlag;
+
+socklen_t sockaddrSize(const sockaddr* sa) {
+ if (sa == nullptr) return 0;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ return sizeof(sockaddr_in);
+ case AF_INET6:
+ return sizeof(sockaddr_in6);
+ default:
+ return 0;
+ }
+}
+
+socklen_t sockaddrSize(const sockaddr_storage& ss) {
+ return sockaddrSize(reinterpret_cast<const sockaddr*>(&ss));
+}
+
+int getExperimentFlagInt(const std::string& flagName, int defaultValue) {
+ int val = defaultValue;
+ ParseInt(GetServerConfigurableFlag("netd_native", flagName, ""), &val);
+ return val;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..43b1376
--- /dev/null
+++ b/util.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <string>
+
+#include <netinet/in.h>
+
+socklen_t sockaddrSize(const sockaddr* sa);
+socklen_t sockaddrSize(const sockaddr_storage& ss);
+
+// TODO: getExperimentFlagString
+int getExperimentFlagInt(const std::string& flagName, int defaultValue);