aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:29:03 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:29:03 +0000
commit8be05f6c1eb5c6ddfa50ebde58ec8036f3e74ffe (patch)
tree63d39864908560daca338562b76258646b38cfd6
parent4088c80e055a0bfeca13f5d9b1eea14d82e2c58a (diff)
parentfb137123a8c763b7a95a013fabd97435032be17c (diff)
downloadDnsResolver-8be05f6c1eb5c6ddfa50ebde58ec8036f3e74ffe.tar.gz
Snap for 10453563 from fb137123a8c763b7a95a013fabd97435032be17c to mainline-tzdata5-release
Change-Id: Ic9518aa60ae1f107807cce80230122359c1b7090
-rw-r--r--Android.bp35
-rw-r--r--DnsQueryLog.cpp11
-rw-r--r--DnsQueryLog.h14
-rw-r--r--DnsQueryLogTest.cpp46
-rw-r--r--DnsResolver.cpp2
-rw-r--r--Experiments.h1
-rw-r--r--PrivateDnsConfiguration.cpp163
-rw-r--r--PrivateDnsConfiguration.h26
-rw-r--r--PrivateDnsConfigurationTest.cpp11
-rw-r--r--doh.h2
-rw-r--r--doh/config.rs3
-rw-r--r--doh/connection/driver.rs79
-rw-r--r--doh/connection/mod.rs29
-rw-r--r--doh/doh.rs1
-rw-r--r--doh/doh_test_superset_for_fuzzer.rs1
-rw-r--r--doh/ffi.rs6
-rw-r--r--doh/metrics.rs157
-rw-r--r--doh/network/driver.rs36
-rw-r--r--doh/network/mod.rs2
-rw-r--r--gethnamaddr.cpp9
-rw-r--r--res_cache.cpp3
-rw-r--r--res_send.cpp7
-rw-r--r--sethostent.cpp12
-rw-r--r--tests/Android.bp5
-rw-r--r--tests/doh_ffi_test.cpp4
-rw-r--r--tests/fuzzer/resolv_fuzzer_utils.cpp2
-rw-r--r--tests/fuzzer/resolv_getaddrinfo_fuzzer.cpp5
-rw-r--r--tests/fuzzer/resolv_gethostbyaddr_fuzzer.cpp5
-rw-r--r--tests/fuzzer/resolv_gethostbyname_fuzzer.cpp5
-rw-r--r--tests/resolv_cache_unit_test.cpp6
-rw-r--r--tests/resolv_integration_test.cpp2
-rw-r--r--tests/resolv_private_dns_test.cpp23
-rw-r--r--tests/resolv_test_utils.h5
-rw-r--r--util.h7
34 files changed, 510 insertions, 215 deletions
diff --git a/Android.bp b/Android.bp
index beead98b..ef988163 100644
--- a/Android.bp
+++ b/Android.bp
@@ -44,8 +44,8 @@ cc_library_headers {
cc_library_headers {
name: "dnsproxyd_protocol_headers",
- sdk_version: "29",
- min_sdk_version: "29",
+ sdk_version: "30",
+ min_sdk_version: "30",
export_include_dirs: ["include/dnsproxyd_protocol"],
apex_available: [
"//apex_available:platform",
@@ -63,7 +63,7 @@ cc_library_static {
apex_available: [
"com.android.resolv",
],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
aidl_interface {
@@ -93,7 +93,7 @@ aidl_interface {
apex_available: [
"com.android.resolv",
],
- min_sdk_version: "29",
+ min_sdk_version: "30",
},
},
versions: [
@@ -129,7 +129,7 @@ cc_defaults {
"liblog",
],
// This field is required to make test compatible with Q devices.
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
cc_defaults {
@@ -249,15 +249,11 @@ cc_library {
"libcrypto",
"liblog", //Used by libstatslog_resolv
"libssl",
+ "libstatssocket",
],
header_libs: [
"libnetdbinder_utils_headers",
],
- runtime_libs: [
- // Causes the linkerconfig to create a namespace link from resolv to the
- // libstatssocket library within the statsd apex
- "libstatssocket",
- ],
export_include_dirs: ["include"],
product_variables: {
@@ -275,7 +271,7 @@ cc_library {
cfi: true,
},
apex_available: ["com.android.resolv"],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
cc_library_static {
@@ -289,7 +285,7 @@ cc_library_static {
"stats.proto",
],
apex_available: ["com.android.resolv"],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
genrule {
@@ -326,7 +322,7 @@ cc_library_static {
"libgtest_prod_headers", // Used by libstatspush_compat
],
apex_available: ["com.android.resolv"],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
filegroup {
@@ -362,6 +358,7 @@ doh_rust_deps = [
"liblibc",
"liblog_rust",
"libring",
+ "libstatslog_rust",
"libthiserror",
"libtokio",
"liburl",
@@ -385,7 +382,7 @@ rust_ffi_static {
"//apex_available:platform", // Needed by doh_ffi_test
"com.android.resolv",
],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
rust_test {
@@ -396,7 +393,7 @@ rust_test {
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: doh_rust_deps + ["libquiche_static"],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
// It's required by unit tests.
@@ -408,6 +405,11 @@ rust_ffi_static {
rlibs: doh_rust_deps + ["libquiche_static"],
prefer_rlib: true,
+
+ shared_libs: [
+ "libstatssocket",
+ ],
+
// TODO(b/194022174), for unit tests to run on the Android 10 platform,
// libunwind must be statically linked.
whole_static_libs: ["libunwind"],
@@ -415,7 +417,7 @@ rust_ffi_static {
"//apex_available:platform", // Needed by doh_ffi_test
"com.android.resolv",
],
- min_sdk_version: "29",
+ min_sdk_version: "30",
}
rust_ffi_static {
@@ -438,6 +440,7 @@ rust_ffi_static {
"liblog_rust",
"libquiche_static",
"libring",
+ "libstatslog_rust",
"libthiserror",
"libtokio",
"liburl",
diff --git a/DnsQueryLog.cpp b/DnsQueryLog.cpp
index 9cc3ca4e..52f444da 100644
--- a/DnsQueryLog.cpp
+++ b/DnsQueryLog.cpp
@@ -51,14 +51,17 @@ void DnsQueryLog::push(Record&& record) {
mQueue.push(std::move(record));
}
+uint64_t DnsQueryLog::getLogSizeFromSysProp() {
+ const uint64_t logSize = android::base::GetUintProperty<uint64_t>(
+ "persist.net.dns_query_log_size", kDefaultLogSize);
+ return logSize <= kMaxLogSize ? logSize : kDefaultLogSize;
+}
+
void DnsQueryLog::dump(netdutils::DumpWriter& dw) const {
- dw.println("DNS query log (last %lld minutes):", (mValidityTimeMs / 60000).count());
+ dw.println("DNS query log:");
netdutils::ScopedIndent indentStats(dw);
- const auto now = std::chrono::system_clock::now();
for (const auto& record : mQueue.copy()) {
- 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);
diff --git a/DnsQueryLog.h b/DnsQueryLog.h
index 3e6478e5..c46ab4e8 100644
--- a/DnsQueryLog.h
+++ b/DnsQueryLog.h
@@ -49,23 +49,23 @@ class DnsQueryLog {
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)
- : mQueue(size), mValidityTimeMs(time) {}
+ DnsQueryLog() : DnsQueryLog(getLogSizeFromSysProp()) {}
+
+ // Allow the tests to set the capacity.
+ DnsQueryLog(size_t size) : mQueue(size) {}
void push(Record&& record);
void dump(netdutils::DumpWriter& dw) const;
private:
LockedRingBuffer<Record> mQueue;
- const std::chrono::milliseconds mValidityTimeMs;
// The capacity of the circular buffer.
static constexpr size_t kDefaultLogSize = 200;
+ // The upper bound of the circular buffer.
+ static constexpr size_t kMaxLogSize = 10000;
- // Limit to dump the queries within last |kDefaultValidityMinutes| minutes.
- static constexpr std::chrono::minutes kDefaultValidityMinutes{60};
+ uint64_t getLogSizeFromSysProp();
};
} // namespace android::net
diff --git a/DnsQueryLogTest.cpp b/DnsQueryLogTest.cpp
index 3731ea88..b3652a1b 100644
--- a/DnsQueryLogTest.cpp
+++ b/DnsQueryLogTest.cpp
@@ -23,6 +23,7 @@
#include <netdutils/NetNativeTestBase.h>
#include "DnsQueryLog.h"
+#include "tests/resolv_test_utils.h"
using namespace std::chrono_literals;
@@ -140,26 +141,37 @@ TEST_F(DnsQueryLogTest, CapacityFull) {
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});
+TEST_F(DnsQueryLogTest, SizeCustomization) {
+ const size_t logSize = 3;
+ const ScopedSystemProperties sp(kQueryLogSize, std::to_string(logSize));
+ DnsQueryLog queryLog;
- std::this_thread::sleep_for(150ms);
+ for (int i = 0; i < 200; i++) {
+ DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10);
+ queryLog.push(std::move(record));
+ }
- // The record is expired thus not shown in the output.
- output = captureDumpOutput(queryLog);
- verifyDumpOutput(output, {});
+ // Verify that there are exact customized number of records in queryLog.
+ const std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, std::vector(logSize, 30));
+}
- // 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});
+TEST_F(DnsQueryLogTest, InvalidSizeCustomization) {
+ // The max log size defined in DnsQueryLog.h is 10000.
+ for (const auto& logSize : {"-1", "10001", "non-digit"}) {
+ const ScopedSystemProperties sp(kQueryLogSize, logSize);
+ DnsQueryLog queryLog;
+
+ for (int i = 0; i < 300; i++) {
+ DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10);
+ queryLog.push(std::move(record));
+ }
+
+ // Verify that queryLog has the default number of records. The default size defined in
+ // DnsQueryLog.h is 200.
+ const std::string output = captureDumpOutput(queryLog);
+ verifyDumpOutput(output, std::vector(200, 30));
+ }
}
} // namespace android::net
diff --git a/DnsResolver.cpp b/DnsResolver.cpp
index c75c1f8c..5abfaea6 100644
--- a/DnsResolver.cpp
+++ b/DnsResolver.cpp
@@ -83,7 +83,7 @@ DnsResolver::DnsResolver() {
auto& dnsTlsDispatcher = DnsTlsDispatcher::getInstance();
auto& privateDnsConfiguration = PrivateDnsConfiguration::getInstance();
privateDnsConfiguration.setObserver(&dnsTlsDispatcher);
- if (isDoHEnabled()) privateDnsConfiguration.initDoh();
+ privateDnsConfiguration.initDoh();
}
bool DnsResolver::start() {
diff --git a/Experiments.h b/Experiments.h
index 520d4fb7..a9845629 100644
--- a/Experiments.h
+++ b/Experiments.h
@@ -47,7 +47,6 @@ class Experiments {
mutable std::mutex mMutex;
std::map<std::string_view, int> mFlagsMapInt GUARDED_BY(mMutex);
static constexpr const char* const kExperimentFlagKeyList[] = {
- "doh",
"doh_early_data",
"doh_idle_timeout_ms",
"doh_probe_timeout_ms",
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 08576999..013cd1a5 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -59,6 +59,44 @@ bool ensureNoInvalidIp(const std::vector<std::string>& servers) {
return true;
}
+FeatureFlags makeDohFeatureFlags() {
+ const Experiments* const instance = Experiments::getInstance();
+ const auto getTimeout = [&](const std::string_view key, int defaultValue) -> uint64_t {
+ static constexpr int kMinTimeoutMs = 1000;
+ uint64_t timeout = instance->getFlag(key, defaultValue);
+ if (timeout < kMinTimeoutMs) {
+ timeout = kMinTimeoutMs;
+ }
+ return timeout;
+ };
+
+ return FeatureFlags{
+ .probe_timeout_ms = getTimeout("doh_probe_timeout_ms",
+ PrivateDnsConfiguration::kDohProbeDefaultTimeoutMs),
+ .idle_timeout_ms = getTimeout("doh_idle_timeout_ms",
+ PrivateDnsConfiguration::kDohIdleDefaultTimeoutMs),
+ .use_session_resumption = instance->getFlag("doh_session_resumption", 0) == 1,
+ .enable_early_data = instance->getFlag("doh_early_data", 0) == 1,
+ };
+}
+
+std::string toString(const FeatureFlags& flags) {
+ return fmt::format(
+ "probe_timeout_ms={}, idle_timeout_ms={}, use_session_resumption={}, "
+ "enable_early_data={}",
+ flags.probe_timeout_ms, flags.idle_timeout_ms, flags.use_session_resumption,
+ flags.enable_early_data);
+}
+
+// Returns the sorted (sort IPv6 before IPv4) servers.
+std::vector<std::string> sortServers(const std::vector<std::string>& servers) {
+ std::vector<std::string> out = servers;
+ std::sort(out.begin(), out.end(), [](std::string a, std::string b) {
+ return IPAddress::forString(a) > IPAddress::forString(b);
+ });
+ return out;
+}
+
} // namespace
PrivateDnsModes convertEnumType(PrivateDnsMode mode) {
@@ -103,11 +141,8 @@ int PrivateDnsConfiguration::set(int32_t netId, uint32_t mark,
if (int n = setDot(netId, mark, encryptedServers, name, caCert); n != 0) {
return n;
}
- if (isDoHEnabled()) {
- return setDoh(netId, mark, encryptedServers, name, caCert);
- }
- return 0;
+ return setDoh(netId, mark, encryptedServers, name, caCert);
}
int PrivateDnsConfiguration::setDot(int32_t netId, uint32_t mark,
@@ -184,9 +219,8 @@ PrivateDnsStatus PrivateDnsConfiguration::getStatusLocked(unsigned netId) const
auto it = mDohTracker.find(netId);
if (it != mDohTracker.end()) {
- status.dohServersMap.emplace(
- netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort),
- it->second.status);
+ status.dohServersMap.emplace(IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort),
+ it->second.status);
}
return status;
@@ -516,7 +550,7 @@ base::Result<netdutils::IPSockAddr> PrivateDnsConfiguration::getDohServer(unsign
std::lock_guard guard(mPrivateDnsLock);
auto it = mDohTracker.find(netId);
if (it != mDohTracker.end()) {
- return netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort);
+ return IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort);
}
return Errorf("Failed to get DoH Server: netId {} not found", netId);
@@ -568,73 +602,43 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark,
return 0;
}
- const auto getTimeoutFromFlag = [&](const std::string_view key, int defaultValue) -> uint64_t {
- static constexpr int kMinTimeoutMs = 1000;
- uint64_t timeout = Experiments::getInstance()->getFlag(key, defaultValue);
- if (timeout < kMinTimeoutMs) {
- timeout = kMinTimeoutMs;
- }
- return timeout;
- };
+ const NetworkType networkType = resolv_get_network_types_for_net(netId);
+ const PrivateDnsStatus status = getStatusLocked(netId);
- // Sort the input servers to ensure that we could get the server vector at the same order.
- std::vector<std::string> sortedServers = servers;
- // Prefer ipv6.
- std::sort(sortedServers.begin(), sortedServers.end(), [](std::string a, std::string b) {
- IPAddress ipa = IPAddress::forString(a);
- IPAddress ipb = IPAddress::forString(b);
- return ipa > ipb;
- });
+ // Sort the input servers to prefer IPv6.
+ const std::vector<std::string> sortedServers = sortServers(servers);
initDohLocked();
- // TODO: 1. Improve how to choose the server
- // TODO: 2. Support multiple servers
- for (const auto& entry : mAvailableDoHProviders) {
- const auto& doh = entry.getDohIdentity(sortedServers, name);
- if (!doh.ok()) continue;
+ const auto& doh = makeDohIdentity(sortedServers, name);
+ if (!doh.ok()) {
+ LOG(INFO) << __func__ << ": No suitable DoH server found";
+ clearDoh(netId);
+ return 0;
+ }
- // Since the DnsResolver is expected to be configured by the system server, add the
- // restriction to prevent ResolverTestProvider from being used other than testing.
- if (entry.requireRootPermission && AIBinder_getCallingUid() != AID_ROOT) continue;
+ auto it = mDohTracker.find(netId);
+ // Skip if the same server already exists and its status == success.
+ if (it != mDohTracker.end() && it->second == doh.value() &&
+ it->second.status == Validation::success) {
+ return 0;
+ }
+ const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value());
+ const auto& dohId = dohIt->second;
- auto it = mDohTracker.find(netId);
- // Skip if the same server already exists and its status == success.
- if (it != mDohTracker.end() && it->second == doh.value() &&
- it->second.status == Validation::success) {
- return 0;
- }
- const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value());
- const auto& dohId = dohIt->second;
-
- RecordEntry record(netId,
- {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, kDohPort), name},
- dohId.status);
- mPrivateDnsLog.push(std::move(record));
- LOG(INFO) << __func__ << ": Upgrading server to DoH: " << name;
- resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, kDohPort);
-
- const FeatureFlags flags = {
- .probe_timeout_ms =
- getTimeoutFromFlag("doh_probe_timeout_ms", kDohProbeDefaultTimeoutMs),
- .idle_timeout_ms =
- getTimeoutFromFlag("doh_idle_timeout_ms", kDohIdleDefaultTimeoutMs),
- .use_session_resumption =
- Experiments::getInstance()->getFlag("doh_session_resumption", 0) == 1,
- .enable_early_data = Experiments::getInstance()->getFlag("doh_early_data", 0) == 1,
- };
- LOG(DEBUG) << __func__ << ": probe_timeout_ms=" << flags.probe_timeout_ms
- << ", idle_timeout_ms=" << flags.idle_timeout_ms
- << ", use_session_resumption=" << flags.use_session_resumption
- << ", enable_early_data=" << flags.enable_early_data;
-
- return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(),
- dohId.ipAddr.c_str(), mark, caCert.c_str(), &flags);
- }
-
- LOG(INFO) << __func__ << ": No suitable DoH server found";
- clearDoh(netId);
- return 0;
+ RecordEntry record(netId, {IPSockAddr::toIPSockAddr(dohId.ipAddr, kDohPort), name},
+ dohId.status);
+ mPrivateDnsLog.push(std::move(record));
+ LOG(INFO) << __func__ << ": Upgrading server to DoH: " << name;
+ resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, kDohPort);
+
+ const FeatureFlags flags = makeDohFeatureFlags();
+ LOG(DEBUG) << __func__ << ": " << toString(flags);
+
+ const PrivateDnsModes privateDnsMode = convertEnumType(status.mode);
+ return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(),
+ dohId.ipAddr.c_str(), mark, caCert.c_str(), &flags, networkType,
+ privateDnsMode);
}
void PrivateDnsConfiguration::clearDoh(unsigned netId) {
@@ -644,6 +648,21 @@ void PrivateDnsConfiguration::clearDoh(unsigned netId) {
resolv_stats_set_addrs(netId, PROTO_DOH, {}, kDohPort);
}
+base::Result<PrivateDnsConfiguration::DohIdentity> PrivateDnsConfiguration::makeDohIdentity(
+ const std::vector<std::string>& servers, const std::string& name) const {
+ for (const auto& entry : mAvailableDoHProviders) {
+ const auto& dohId = entry.getDohIdentity(servers, name);
+ if (!dohId.ok()) continue;
+
+ // Since the DnsResolver is expected to be configured by the system server, add the
+ // restriction to prevent ResolverTestProvider from being used other than testing.
+ if (entry.requireRootPermission && AIBinder_getCallingUid() != AID_ROOT) continue;
+
+ return dohId;
+ }
+ return Errorf("Cannot make a DohIdentity from current DNS configuration");
+}
+
ssize_t PrivateDnsConfiguration::dohQuery(unsigned netId, const Slice query, const Slice answer,
uint64_t timeoutMs) {
{
@@ -668,7 +687,7 @@ void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, co
Validation status = success ? Validation::success : Validation::fail;
it->second.status = status;
// Send the events to registered listeners.
- ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, kDohPort), host};
+ const ServerIdentity identity = {IPSockAddr::toIPSockAddr(ipAddr, kDohPort), host};
if (needReportEvent(netId, identity, success)) {
sendPrivateDnsValidationEvent(identity, netId, success);
}
@@ -679,8 +698,8 @@ void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, co
bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity identity,
bool success) const {
- // If the result is success or DoH is not enable, no concern to report the events.
- if (success || !isDoHEnabled()) return true;
+ // If the result is success, no concern to report the events.
+ if (success) return true;
// If the result is failure, check another transport's status to determine if we should report
// the event.
switch (identity.sockaddr.port()) {
diff --git a/PrivateDnsConfiguration.h b/PrivateDnsConfiguration.h
index 732db7ba..7d4f7efb 100644
--- a/PrivateDnsConfiguration.h
+++ b/PrivateDnsConfiguration.h
@@ -232,13 +232,24 @@ class PrivateDnsConfiguration {
std::string host;
std::string httpsTemplate;
bool requireRootPermission;
- base::Result<DohIdentity> getDohIdentity(const std::vector<std::string>& ips,
+
+ base::Result<DohIdentity> getDohIdentity(const std::vector<std::string>& sortedValidIps,
const std::string& host) const {
- if (!host.empty() && this->host != host) return Errorf("host {} not matched", host);
- for (const auto& ip : ips) {
- if (this->ips.find(ip) == this->ips.end()) continue;
+ // If the private DNS hostname is known, `sortedValidIps` are the IP addresses
+ // resolved from the hostname, and hostname verification will be performed during
+ // TLS handshake to ensure the validity of the server, so it's not necessary to
+ // check the IP address.
+ if (!host.empty()) {
+ if (this->host != host) return Errorf("host {} not matched", host);
+ if (!sortedValidIps.empty()) {
+ const auto& ip = sortedValidIps[0];
+ LOG(INFO) << fmt::format("getDohIdentity: {} {}", ip, host);
+ return DohIdentity{httpsTemplate, ip, host, Validation::in_process};
+ }
+ }
+ for (const auto& ip : sortedValidIps) {
+ if (ips.find(ip) == ips.end()) continue;
LOG(INFO) << fmt::format("getDohIdentity: {} {}", ip, host);
- // Only pick the first one for now.
return DohIdentity{httpsTemplate, ip, host, Validation::in_process};
}
return Errorf("server not matched");
@@ -279,6 +290,11 @@ class PrivateDnsConfiguration {
false},
}};
+ // Makes a DohIdentity by looking up the `mAvailableDoHProviders` by `servers` and `name`.
+ base::Result<DohIdentity> makeDohIdentity(const std::vector<std::string>& servers,
+ const std::string& name) const
+ REQUIRES(mPrivateDnsLock);
+
// For the metrics. Store the current DNS server list in the same order as what is passed
// in setResolverConfiguration().
std::map<unsigned, std::vector<std::string>> mUnorderedDnsTracker GUARDED_BY(mPrivateDnsLock);
diff --git a/PrivateDnsConfigurationTest.cpp b/PrivateDnsConfigurationTest.cpp
index b6d0c8fe..78fc48fd 100644
--- a/PrivateDnsConfigurationTest.cpp
+++ b/PrivateDnsConfigurationTest.cpp
@@ -57,8 +57,8 @@ class PrivateDnsConfigurationTest : public NetNativeTestBase {
// must wait until every validation thread finishes.
ON_CALL(mObserver, onValidationStateUpdate)
.WillByDefault([&](const std::string& server, Validation validation, uint32_t) {
+ std::lock_guard guard(mObserver.lock);
if (validation == Validation::in_process) {
- std::lock_guard guard(mObserver.lock);
auto it = mObserver.serverStateMap.find(server);
if (it == mObserver.serverStateMap.end() ||
it->second != Validation::in_process) {
@@ -73,7 +73,6 @@ class PrivateDnsConfigurationTest : public NetNativeTestBase {
validation == Validation::fail) {
mObserver.runningThreads--;
}
- std::lock_guard guard(mObserver.lock);
mObserver.serverStateMap[server] = validation;
});
@@ -81,7 +80,11 @@ class PrivateDnsConfigurationTest : public NetNativeTestBase {
EXPECT_EQ(0, resolv_create_cache_for_net(kNetId));
}
- void TearDown() { resolv_delete_cache_for_net(kNetId); }
+ void TearDown() {
+ // Reset the state for the next test.
+ resolv_delete_cache_for_net(kNetId);
+ mPdc.set(kNetId, kMark, {}, {}, {}, {});
+ }
protected:
class MockObserver : public PrivateDnsValidationObserver {
@@ -135,7 +138,7 @@ class PrivateDnsConfigurationTest : public NetNativeTestBase {
static constexpr char kServer2[] = "127.0.2.3";
MockObserver mObserver;
- PrivateDnsConfiguration mPdc;
+ inline static PrivateDnsConfiguration mPdc;
// TODO: Because incorrect CAs result in validation failed in strict mode, have
// PrivateDnsConfiguration run mocked code rather than DnsTlsTransport::validate().
diff --git a/doh.h b/doh.h
index 4fa8a5d7..6f82bea5 100644
--- a/doh.h
+++ b/doh.h
@@ -92,7 +92,7 @@ void doh_dispatcher_delete(DohDispatcher* doh);
/// `url`, `domain`, `ip_addr`, `cert_path` are null terminated strings.
int32_t doh_net_new(DohDispatcher* doh, uint32_t net_id, const char* url, const char* domain,
const char* ip_addr, uint32_t sk_mark, const char* cert_path,
- const FeatureFlags* flags);
+ const FeatureFlags* flags, uint32_t network_type, uint32_t private_dns_mode);
/// Sends a DNS query via the network associated to the given |net_id| and waits for the response.
/// The return code should be either one of the public constant RESULT_* to indicate the error or
diff --git a/doh/config.rs b/doh/config.rs
index 8aaaa46b..cc818f69 100644
--- a/doh/config.rs
+++ b/doh/config.rs
@@ -196,12 +196,11 @@ fn create_quiche_config() {
#[test]
fn shared_cache() {
let cache_a = Cache::new();
- let cache_b = cache_a.clone();
let config_a = cache_a
.get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true })
.unwrap();
assert_eq!(Arc::strong_count(&config_a.0), 2);
- let _config_b = cache_b
+ let _config_b = cache_a
.get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true })
.unwrap();
assert_eq!(Arc::strong_count(&config_a.0), 3);
diff --git a/doh/connection/driver.rs b/doh/connection/driver.rs
index 0ea5b78b..833d9150 100644
--- a/doh/connection/driver.rs
+++ b/doh/connection/driver.rs
@@ -17,12 +17,14 @@
use crate::boot_time;
use crate::boot_time::BootTime;
+use crate::metrics::log_handshake_event_stats;
use log::{debug, info, warn};
use quiche::h3;
use std::collections::HashMap;
use std::default::Default;
use std::future;
use std::io;
+use std::time::Instant;
use thiserror::Error;
use tokio::net::UdpSocket;
use tokio::select;
@@ -30,6 +32,50 @@ use tokio::sync::{mpsc, oneshot, watch};
use super::Status;
+#[derive(Copy, Clone, Debug)]
+pub enum Cause {
+ Probe,
+ Reconnect,
+ Retry,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub enum HandshakeResult {
+ Unknown,
+ Success,
+ Timeout,
+ TlsFail,
+ ServerUnreachable,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct HandshakeInfo {
+ pub cause: Cause,
+ pub sent_bytes: u64,
+ pub recv_bytes: u64,
+ pub elapsed: u128,
+ pub quic_version: u32,
+ pub network_type: u32,
+ pub private_dns_mode: u32,
+ pub session_hit_checker: bool,
+}
+
+impl std::fmt::Display for HandshakeInfo {
+ #[inline]
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(
+ f,
+ "cause={:?}, sent_bytes={}, recv_bytes={}, quic_version={}, session_hit_checker={}",
+ self.cause,
+ self.sent_bytes,
+ self.recv_bytes,
+ self.quic_version,
+ self.session_hit_checker
+ )
+ }
+}
+
#[derive(Error, Debug)]
pub enum Error {
#[error("network IO error: {0}")]
@@ -92,6 +138,8 @@ struct Driver {
// if we poll on a dead receiver in a select! it will immediately return None. As a result, we
// need this to gate whether or not to include .recv() in our select!
closing: bool,
+ handshake_info: HandshakeInfo,
+ connection_start: Instant,
}
struct H3Driver {
@@ -121,8 +169,9 @@ pub async fn drive(
quiche_conn: quiche::Connection,
socket: UdpSocket,
net_id: u32,
+ handshake_info: HandshakeInfo,
) -> Result<()> {
- Driver::new(request_rx, status_tx, quiche_conn, socket, net_id).drive().await
+ Driver::new(request_rx, status_tx, quiche_conn, socket, net_id, handshake_info).drive().await
}
impl Driver {
@@ -132,6 +181,7 @@ impl Driver {
quiche_conn: quiche::Connection,
socket: UdpSocket,
net_id: u32,
+ handshake_info: HandshakeInfo,
) -> Self {
Self {
request_rx,
@@ -141,10 +191,13 @@ impl Driver {
buffer: Box::new([0; MAX_UDP_PACKET_SIZE]),
net_id,
closing: false,
+ handshake_info,
+ connection_start: Instant::now(),
}
}
async fn drive(mut self) -> Result<()> {
+ self.connection_start = Instant::now();
// Prime connection
self.flush_tx().await?;
loop {
@@ -202,6 +255,13 @@ impl Driver {
self.quiche_conn.trace_id(),
self.net_id
);
+ self.handshake_info.elapsed = self.connection_start.elapsed().as_micros();
+ // In Stats, sent_bytes implements the way that omits the length of padding data
+ // append to the datagram.
+ self.handshake_info.sent_bytes = self.quiche_conn.stats().sent_bytes;
+ self.handshake_info.recv_bytes = self.quiche_conn.stats().recv_bytes;
+ self.handshake_info.quic_version = quiche::PROTOCOL_VERSION;
+ log_handshake_event_stats(HandshakeResult::Success, self.handshake_info);
let h3_config = h3::Config::new()?;
let h3_conn = h3::Connection::with_transport(&mut self.quiche_conn, &h3_config)?;
self = H3Driver::new(self, h3_conn).drive().await?;
@@ -213,7 +273,20 @@ impl Driver {
// If a quiche timer would fire, call their callback
_ = timer => {
info!("Driver: Timer expired on network {}", self.net_id);
- self.quiche_conn.on_timeout()
+ self.quiche_conn.on_timeout();
+
+ if !self.quiche_conn.is_established() && self.quiche_conn.is_closed() {
+ info!(
+ "Connection {} timeouted on network {}",
+ self.quiche_conn.trace_id(),
+ self.net_id
+ );
+ self.handshake_info.elapsed = self.connection_start.elapsed().as_micros();
+ log_handshake_event_stats(
+ HandshakeResult::Timeout,
+ self.handshake_info,
+ );
+ }
}
// If we got packets from our peer, pass them to quiche
Ok((size, from)) = self.socket.recv_from(self.buffer.as_mut()) => {
@@ -222,6 +295,7 @@ impl Driver {
debug!("Received {} bytes on network {}", size, self.net_id);
}
};
+
// Any of the actions in the select could require us to send packets to the peer
self.flush_tx().await?;
@@ -282,6 +356,7 @@ impl H3Driver {
// try to resend that first
if let Some(request) = self.buffered_request.take() {
self.handle_request(request)?;
+ self.driver.flush_tx().await?;
}
select! {
// Only attempt to enqueue new requests if we have no buffered request and aren't
diff --git a/doh/connection/mod.rs b/doh/connection/mod.rs
index 130930fa..f0b27d79 100644
--- a/doh/connection/mod.rs
+++ b/doh/connection/mod.rs
@@ -16,6 +16,9 @@
//! Module providing an async abstraction around a quiche HTTP/3 connection
use crate::boot_time::BootTime;
+use crate::connection::driver::Cause;
+use crate::connection::driver::HandshakeInfo;
+use crate::network::ServerInfo;
use crate::network::SocketTagger;
use log::{debug, error, warn};
use quiche::h3;
@@ -27,7 +30,7 @@ use tokio::net::UdpSocket;
use tokio::sync::{mpsc, oneshot, watch};
use tokio::task;
-mod driver;
+pub mod driver;
pub use driver::Stream;
use driver::{drive, Request};
@@ -129,14 +132,16 @@ impl Connection {
const MAX_PENDING_REQUESTS: usize = 10;
/// Create a new connection with a background task handling IO.
pub async fn new(
- server_name: Option<&str>,
- to: SocketAddr,
- socket_mark: u32,
- net_id: u32,
+ info: &ServerInfo,
tag_socket: &SocketTagger,
config: &mut quiche::Config,
session: Option<Vec<u8>>,
+ cause: Cause,
) -> Result<Self> {
+ let server_name = info.domain.as_deref();
+ let to = info.peer_addr;
+ let socket_mark = info.sk_mark;
+ let net_id = info.net_id;
let (request_tx, request_rx) = mpsc::channel(Self::MAX_PENDING_REQUESTS);
let (status_tx, status_rx) = watch::channel(Status::QUIC);
let scid = new_scid();
@@ -152,8 +157,20 @@ impl Connection {
quiche_conn.set_session(&session)?;
}
+ let handshake_info = HandshakeInfo {
+ cause,
+ sent_bytes: 0,
+ recv_bytes: 0,
+ elapsed: 0,
+ quic_version: 0,
+ network_type: info.network_type,
+ private_dns_mode: info.private_dns_mode,
+ session_hit_checker: quiche_conn.session().is_some(),
+ };
+
let driver = async move {
- let result = drive(request_rx, status_tx, quiche_conn, socket, net_id).await;
+ let result =
+ drive(request_rx, status_tx, quiche_conn, socket, net_id, handshake_info).await;
if let Err(ref e) = result {
warn!("Connection driver returns some Err: {:?}", e);
}
diff --git a/doh/doh.rs b/doh/doh.rs
index 5ceb5e81..42152a2c 100644
--- a/doh/doh.rs
+++ b/doh/doh.rs
@@ -22,4 +22,5 @@ mod connection;
mod dispatcher;
mod encoding;
mod ffi;
+mod metrics;
mod network;
diff --git a/doh/doh_test_superset_for_fuzzer.rs b/doh/doh_test_superset_for_fuzzer.rs
index 93c5985e..60315127 100644
--- a/doh/doh_test_superset_for_fuzzer.rs
+++ b/doh/doh_test_superset_for_fuzzer.rs
@@ -23,6 +23,7 @@ mod connection;
mod dispatcher;
mod encoding;
mod ffi;
+mod metrics;
mod network;
/// The Rust FFI bindings to C APIs for implementation of doh frontend.
mod tests;
diff --git a/doh/ffi.rs b/doh/ffi.rs
index 4ae49a80..2276f654 100644
--- a/doh/ffi.rs
+++ b/doh/ffi.rs
@@ -191,6 +191,8 @@ pub unsafe extern "C" fn doh_net_new(
sk_mark: libc::uint32_t,
cert_path: *const c_char,
flags: &FeatureFlags,
+ network_type: uint32_t,
+ private_dns_mode: uint32_t,
) -> int32_t {
let (url, domain, ip_addr, cert_path) = match (
std::ffi::CStr::from_ptr(url).to_str(),
@@ -236,6 +238,8 @@ pub unsafe extern "C" fn doh_net_new(
idle_timeout_ms: flags.idle_timeout_ms,
use_session_resumption: flags.use_session_resumption,
enable_early_data: flags.enable_early_data,
+ network_type,
+ private_dns_mode,
},
timeout: Duration::from_millis(flags.probe_timeout_ms),
};
@@ -388,6 +392,8 @@ mod tests {
idle_timeout_ms: 0,
use_session_resumption: true,
enable_early_data: true,
+ network_type: 2,
+ private_dns_mode: 3,
};
wrap_validation_callback(success_cb)(&info, true).await;
diff --git a/doh/metrics.rs b/doh/metrics.rs
new file mode 100644
index 00000000..9b7a96b1
--- /dev/null
+++ b/doh/metrics.rs
@@ -0,0 +1,157 @@
+// Copyright 2022, 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.
+
+//! This module provides convenience functions for doh logging.
+
+use crate::connection::driver::Cause;
+use crate::connection::driver::HandshakeInfo;
+use crate::connection::driver::HandshakeResult;
+use statslog_rust::network_dns_handshake_reported::{
+ Cause as StatsdCause, NetworkDnsHandshakeReported, NetworkType as StatsdNetworkType,
+ PrivateDnsMode as StatsdPrivateDnsMode, Protocol as StatsdProtocol, Result as StatsdResult,
+};
+
+const CELLULAR: u32 = 1;
+const WIFI: u32 = 2;
+const BLUETOOTH: u32 = 3;
+const ETHERNET: u32 = 4;
+const VPN: u32 = 5;
+const WIFI_AWARE: u32 = 6;
+const LOWPAN: u32 = 7;
+const CELLULAR_VPN: u32 = 8;
+const WIFI_VPN: u32 = 9;
+const BLUETOOTH_VPN: u32 = 10;
+const ETHERNET_VPN: u32 = 11;
+const WIFI_CELLULAR_VPN: u32 = 12;
+
+const OFF: u32 = 1;
+const OPPORTUNISTIC: u32 = 2;
+const STRICT: u32 = 3;
+
+const TLS1_3_VERSION: u32 = 3;
+
+fn create_default_handshake_atom() -> NetworkDnsHandshakeReported {
+ NetworkDnsHandshakeReported {
+ protocol: StatsdProtocol::ProtoUnknown,
+ result: StatsdResult::HrUnknown,
+ cause: StatsdCause::HcUnknown,
+ network_type: StatsdNetworkType::NtUnknown,
+ private_dns_mode: StatsdPrivateDnsMode::PdmUnknown,
+ latency_micros: -1,
+ bytes_sent: -1,
+ bytes_received: -1,
+ round_trips: -1,
+ tls_session_cache_hit: false,
+ tls_version: -1,
+ hostname_verification: false,
+ quic_version: -1,
+ server_index: -1,
+ sampling_rate_denom: -1,
+ }
+}
+
+fn construct_handshake_event_stats(
+ result: HandshakeResult,
+ handshake_info: HandshakeInfo,
+) -> NetworkDnsHandshakeReported {
+ let mut handshake_event_atom = create_default_handshake_atom();
+ handshake_event_atom.protocol = StatsdProtocol::ProtoDoh;
+ handshake_event_atom.result = match result {
+ HandshakeResult::Success => StatsdResult::HrSuccess,
+ HandshakeResult::Timeout => StatsdResult::HrTimeout,
+ _ => StatsdResult::HrUnknown,
+ };
+ handshake_event_atom.cause = match handshake_info.cause {
+ Cause::Probe => StatsdCause::HcServerProbe,
+ Cause::Reconnect => StatsdCause::HcReconnectAfterIdle,
+ Cause::Retry => StatsdCause::HcRetryAfterError,
+ };
+ handshake_event_atom.network_type = match handshake_info.network_type {
+ CELLULAR => StatsdNetworkType::NtCellular,
+ WIFI => StatsdNetworkType::NtWifi,
+ BLUETOOTH => StatsdNetworkType::NtBluetooth,
+ ETHERNET => StatsdNetworkType::NtEthernet,
+ VPN => StatsdNetworkType::NtVpn,
+ WIFI_AWARE => StatsdNetworkType::NtWifiAware,
+ LOWPAN => StatsdNetworkType::NtLowpan,
+ CELLULAR_VPN => StatsdNetworkType::NtCellularVpn,
+ WIFI_VPN => StatsdNetworkType::NtWifiVpn,
+ BLUETOOTH_VPN => StatsdNetworkType::NtBluetoothVpn,
+ ETHERNET_VPN => StatsdNetworkType::NtEthernetVpn,
+ WIFI_CELLULAR_VPN => StatsdNetworkType::NtWifiCellularVpn,
+ _ => StatsdNetworkType::NtUnknown,
+ };
+ handshake_event_atom.private_dns_mode = match handshake_info.private_dns_mode {
+ OFF => StatsdPrivateDnsMode::PdmOff,
+ OPPORTUNISTIC => StatsdPrivateDnsMode::PdmOpportunistic,
+ STRICT => StatsdPrivateDnsMode::PdmStrict,
+ _ => StatsdPrivateDnsMode::PdmUnknown,
+ };
+ handshake_event_atom.latency_micros = handshake_info.elapsed as i32;
+ handshake_event_atom.bytes_sent = handshake_info.sent_bytes as i32;
+ handshake_event_atom.bytes_received = handshake_info.recv_bytes as i32;
+ handshake_event_atom.tls_session_cache_hit = handshake_info.session_hit_checker;
+ handshake_event_atom.tls_version = TLS1_3_VERSION as i32;
+ handshake_event_atom.hostname_verification = matches!(handshake_info.private_dns_mode, STRICT);
+ handshake_event_atom.quic_version = handshake_info.quic_version as i32;
+ handshake_event_atom
+}
+
+/// Log hankshake events via statsd API.
+pub fn log_handshake_event_stats(result: HandshakeResult, handshake_info: HandshakeInfo) {
+ let handshake_event_stats = construct_handshake_event_stats(result, handshake_info);
+
+ let logging_result = handshake_event_stats.stats_write();
+ if let Err(e) = logging_result {
+ log::error!("Error in logging handshake event. {:?}", e);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_metrics_write() {
+ let handshake_info = HandshakeInfo {
+ cause: Cause::Retry,
+ network_type: WIFI,
+ private_dns_mode: STRICT,
+ elapsed: 42596,
+ sent_bytes: 761,
+ recv_bytes: 6420,
+ session_hit_checker: false,
+ quic_version: 1,
+ };
+ let result = HandshakeResult::Timeout;
+ let handshake_event_stats = construct_handshake_event_stats(result, handshake_info);
+ assert_eq!(handshake_event_stats.protocol as i32, StatsdProtocol::ProtoDoh as i32);
+ assert_eq!(handshake_event_stats.result as i32, HandshakeResult::Timeout as i32);
+ assert_eq!(handshake_event_stats.cause as i32, StatsdCause::HcRetryAfterError as i32);
+ assert_eq!(handshake_event_stats.network_type as i32, StatsdNetworkType::NtWifi as i32);
+ assert_eq!(
+ handshake_event_stats.private_dns_mode as i32,
+ StatsdPrivateDnsMode::PdmStrict as i32
+ );
+ assert_eq!(handshake_event_stats.latency_micros, 42596);
+ assert_eq!(handshake_event_stats.bytes_sent, 761);
+ assert_eq!(handshake_event_stats.bytes_received, 6420);
+ assert_eq!(handshake_event_stats.round_trips, -1);
+ assert!(!handshake_event_stats.tls_session_cache_hit);
+ assert!(handshake_event_stats.hostname_verification);
+ assert_eq!(handshake_event_stats.quic_version, 1);
+ assert_eq!(handshake_event_stats.server_index, -1);
+ assert_eq!(handshake_event_stats.sampling_rate_denom, -1);
+ }
+}
diff --git a/doh/network/driver.rs b/doh/network/driver.rs
index 118e5758..cad5584e 100644
--- a/doh/network/driver.rs
+++ b/doh/network/driver.rs
@@ -18,6 +18,7 @@
use crate::boot_time::{timeout, BootTime, Duration};
use crate::config::Config;
+use crate::connection::driver::Cause;
use crate::connection::Connection;
use crate::dispatcher::{QueryError, Response};
use crate::encoding;
@@ -76,18 +77,10 @@ async fn build_connection(
tag_socket: &SocketTagger,
config: &mut Config,
session: Option<Vec<u8>>,
+ cause: Cause,
) -> Result<Connection> {
use std::ops::DerefMut;
- Ok(Connection::new(
- info.domain.as_deref(),
- info.peer_addr,
- info.sk_mark,
- info.net_id,
- tag_socket,
- config.take().await.deref_mut(),
- session,
- )
- .await?)
+ Ok(Connection::new(info, tag_socket, config.take().await.deref_mut(), session, cause).await?)
}
impl Driver {
@@ -101,7 +94,8 @@ impl Driver {
) -> Result<(Self, mpsc::Sender<Command>, watch::Receiver<Status>)> {
let (command_tx, command_rx) = mpsc::channel(Self::MAX_BUFFERED_COMMANDS);
let (status_tx, status_rx) = watch::channel(Status::Unprobed);
- let connection = build_connection(&info, &tag_socket, &mut config, None).await?;
+ let connection =
+ build_connection(&info, &tag_socket, &mut config, None, Cause::Probe).await?;
Ok((
Self { info, config, connection, status_tx, command_rx, validation, tag_socket },
command_tx,
@@ -132,8 +126,14 @@ impl Driver {
debug!("Network is currently failed, reconnecting");
// If our network is currently failed, it may be due to issues with the connection.
// Re-establish before re-probing
- self.connection =
- build_connection(&self.info, &self.tag_socket, &mut self.config, None).await?;
+ self.connection = build_connection(
+ &self.info,
+ &self.tag_socket,
+ &mut self.config,
+ None,
+ Cause::Retry,
+ )
+ .await?;
self.status_tx.send(Status::Unprobed)?;
}
if self.status_tx.borrow().is_live() {
@@ -189,8 +189,14 @@ impl Driver {
let session =
if self.info.use_session_resumption { self.connection.session() } else { None };
// Try reconnecting
- self.connection =
- build_connection(&self.info, &self.tag_socket, &mut self.config, session).await?;
+ self.connection = build_connection(
+ &self.info,
+ &self.tag_socket,
+ &mut self.config,
+ session,
+ Cause::Reconnect,
+ )
+ .await?;
}
let request = encoding::dns_request(&query.query, &self.info.url)?;
let stream_fut = self.connection.query(request, Some(query.expiry)).await?;
diff --git a/doh/network/mod.rs b/doh/network/mod.rs
index 7e39f60b..6d9fbab6 100644
--- a/doh/network/mod.rs
+++ b/doh/network/mod.rs
@@ -50,6 +50,8 @@ pub struct ServerInfo {
pub idle_timeout_ms: u64,
pub use_session_resumption: bool,
pub enable_early_data: bool,
+ pub network_type: u32,
+ pub private_dns_mode: u32,
}
#[derive(Debug)]
diff --git a/gethnamaddr.cpp b/gethnamaddr.cpp
index 5bf1d8c8..b7fef8b2 100644
--- a/gethnamaddr.cpp
+++ b/gethnamaddr.cpp
@@ -279,9 +279,11 @@ static struct hostent* getanswer(const querybuf* _Nonnull answer, int anslen, co
}
break;
case T_A:
- case T_AAAA:
- if (strcasecmp(hent->h_name, bp) != 0) {
- LOG(DEBUG) << __func__ << ": asked for \"" << hent->h_name << "\", got \"" << bp
+ case T_AAAA: {
+ if (hent->h_name == NULL) goto no_recovery;
+ const char* h_name = hent->h_name;
+ if (strcasecmp(h_name, bp) != 0) {
+ LOG(DEBUG) << __func__ << ": asked for \"" << h_name << "\", got \"" << bp
<< "\"";
cp += n;
continue; /* XXX - had_error++ ? */
@@ -325,6 +327,7 @@ static struct hostent* getanswer(const querybuf* _Nonnull answer, int anslen, co
cp += n;
if (cp != erdata) goto no_recovery;
break;
+ }
default:
abort();
}
diff --git a/res_cache.cpp b/res_cache.cpp
index 701dbf39..bf0abdb9 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -154,6 +154,7 @@ using std::span;
* *****************************************
*/
const int MAX_ENTRIES_DEFAULT = 64 * 2 * 5;
+const int MAX_ENTRIES_LOWER_BOUND = 0;
const int MAX_ENTRIES_UPPER_BOUND = 100 * 1000;
constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
@@ -1007,7 +1008,7 @@ struct Cache {
MAX_ENTRIES_DEFAULT);
// Check both lower and upper bounds to prevent irrational values mistakenly pushed by
// server.
- if (entries < MAX_ENTRIES_DEFAULT || entries > MAX_ENTRIES_UPPER_BOUND) {
+ if (entries < MAX_ENTRIES_LOWER_BOUND || entries > MAX_ENTRIES_UPPER_BOUND) {
LOG(ERROR) << "Misconfiguration on max_cache_entries " << entries;
entries = MAX_ENTRIES_DEFAULT;
}
diff --git a/res_send.cpp b/res_send.cpp
index 82800b00..38b9c6ac 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -1305,7 +1305,6 @@ static int res_private_dns_send(ResState* statp, const Slice query, const Slice
PrivateDnsStatus privateDnsStatus = privateDnsConfiguration.getStatus(netId);
statp->event->set_private_dns_modes(convertEnumType(privateDnsStatus.mode));
- const bool enableDoH = isDoHEnabled();
ssize_t result = -1;
switch (privateDnsStatus.mode) {
case PrivateDnsMode::OFF: {
@@ -1314,7 +1313,7 @@ static int res_private_dns_send(ResState* statp, const Slice query, const Slice
}
case PrivateDnsMode::OPPORTUNISTIC: {
*fallback = true;
- if (enableDoH && privateDnsStatus.hasValidatedDohServers()) {
+ if (privateDnsStatus.hasValidatedDohServers()) {
result = res_doh_send(statp, query, answer, rcode);
if (result != DOH_RESULT_CAN_NOT_SEND) return result;
}
@@ -1323,7 +1322,7 @@ static int res_private_dns_send(ResState* statp, const Slice query, const Slice
}
case PrivateDnsMode::STRICT: {
*fallback = false;
- if (enableDoH && privateDnsStatus.hasValidatedDohServers()) {
+ if (privateDnsStatus.hasValidatedDohServers()) {
result = res_doh_send(statp, query, answer, rcode);
if (result != DOH_RESULT_CAN_NOT_SEND) return result;
}
@@ -1349,7 +1348,7 @@ static int res_private_dns_send(ResState* statp, const Slice query, const Slice
// ups.
privateDnsStatus = privateDnsConfiguration.getStatus(netId);
- if (enableDoH && privateDnsStatus.hasValidatedDohServers()) {
+ if (privateDnsStatus.hasValidatedDohServers()) {
result = res_doh_send(statp, query, answer, rcode);
if (result != DOH_RESULT_CAN_NOT_SEND) return result;
}
diff --git a/sethostent.cpp b/sethostent.cpp
index 4691b8e9..a9b0de6e 100644
--- a/sethostent.cpp
+++ b/sethostent.cpp
@@ -101,7 +101,12 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) {
break;
}
- if (strcasecmp(hp->h_name, name) != 0) {
+ if (hp->h_name == nullptr) {
+ free(buf);
+ return EAI_FAIL;
+ }
+ const char* h_name = hp->h_name;
+ if (strcasecmp(h_name, name) != 0) {
char** cp;
for (cp = hp->h_aliases; *cp != NULL; cp++)
if (strcasecmp(*cp, name) == 0) break;
@@ -113,11 +118,6 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) {
hent.h_addrtype = hp->h_addrtype;
hent.h_length = hp->h_length;
- if (hp->h_name == nullptr) {
- free(buf);
- return EAI_FAIL;
- }
- const char* h_name = hp->h_name;
HENT_SCOPY(hent.h_name, h_name, ptr, len);
for (anum = 0; hp->h_aliases[anum]; anum++) {
if (anum >= MAXALIASES) goto nospc;
diff --git a/tests/Android.bp b/tests/Android.bp
index 40b909f8..72fb008b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -102,6 +102,7 @@ cc_test {
],
shared_libs: [
"libbinder_ndk",
+ "libstatssocket",
],
static_libs: [
"dnsresolver_aidl_interface-lateststable-ndk",
@@ -251,6 +252,7 @@ cc_test {
],
shared_libs: [
"libbinder_ndk",
+ "libstatssocket",
],
static_libs: [
"dnsresolver_aidl_interface-lateststable-ndk",
@@ -332,9 +334,11 @@ cc_test {
"libgmock",
"libnetdutils",
"libssl",
+ "stats_proto",
],
shared_libs: [
"libnetd_client",
+ "libstatssocket",
],
}
@@ -354,6 +358,7 @@ cc_defaults {
],
shared_libs: [
"libbinder_ndk",
+ "libstatssocket",
],
static_libs: [
"dnsresolver_aidl_interface-lateststable-ndk",
diff --git a/tests/doh_ffi_test.cpp b/tests/doh_ffi_test.cpp
index b91c59ed..0e51402a 100644
--- a/tests/doh_ffi_test.cpp
+++ b/tests/doh_ffi_test.cpp
@@ -109,7 +109,9 @@ TEST_F(DoHFFITest, SmokeTest) {
// sk_mark doesn't matter here because this test doesn't have permission to set sk_mark.
// The DNS packet would be sent via default network.
EXPECT_EQ(doh_net_new(doh, dnsNetId, "https://dns.google/dns-query", /* domain */ "", server_ip,
- /* sk_mark */ 0, /* cert_path */ "", &flags),
+ /* sk_mark */ 0, /* cert_path */ "", &flags,
+ /* NetworkType::NT_WIFI */ 3,
+ /* PrivateDnsMode::STRICT */ 2),
0);
{
std::unique_lock<std::mutex> lk(m);
diff --git a/tests/fuzzer/resolv_fuzzer_utils.cpp b/tests/fuzzer/resolv_fuzzer_utils.cpp
index 6fda744a..8a903d59 100644
--- a/tests/fuzzer/resolv_fuzzer_utils.cpp
+++ b/tests/fuzzer/resolv_fuzzer_utils.cpp
@@ -87,4 +87,4 @@ void CleanUp() {
resolverCtrl.flushNetworkCache(TEST_NETID);
}
-} // namespace android::net \ No newline at end of file
+} // namespace android::net
diff --git a/tests/fuzzer/resolv_getaddrinfo_fuzzer.cpp b/tests/fuzzer/resolv_getaddrinfo_fuzzer.cpp
index 8ff2a910..a80c3358 100644
--- a/tests/fuzzer/resolv_getaddrinfo_fuzzer.cpp
+++ b/tests/fuzzer/resolv_getaddrinfo_fuzzer.cpp
@@ -39,11 +39,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
dot.setDelayQueriesTimeout(1000);
FuzzedDataProvider fdp(data, size);
- // Chooses doh or dot.
- std::string flag = fdp.PickValueInArray({"0", "1"});
- ScopedSystemProperties sp(kDohFlag, flag);
- android::net::Experiments::getInstance()->update();
-
auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
// Chooses private DNS or not.
if (fdp.ConsumeBool()) parcel.tlsServers = {};
diff --git a/tests/fuzzer/resolv_gethostbyaddr_fuzzer.cpp b/tests/fuzzer/resolv_gethostbyaddr_fuzzer.cpp
index 9230c9b9..63ad46bc 100644
--- a/tests/fuzzer/resolv_gethostbyaddr_fuzzer.cpp
+++ b/tests/fuzzer/resolv_gethostbyaddr_fuzzer.cpp
@@ -28,11 +28,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
[[maybe_unused]] static bool initialized = DoInit();
FuzzedDataProvider fdp(data, size);
- // Chooses doh or dot.
- std::string flag = fdp.PickValueInArray({"0", "1"});
- ScopedSystemProperties sp(kDohFlag, flag);
- android::net::Experiments::getInstance()->update();
-
auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
// Chooses private DNS or not.
if (fdp.ConsumeBool()) parcel.tlsServers = {};
diff --git a/tests/fuzzer/resolv_gethostbyname_fuzzer.cpp b/tests/fuzzer/resolv_gethostbyname_fuzzer.cpp
index de30b1e7..d05eba5c 100644
--- a/tests/fuzzer/resolv_gethostbyname_fuzzer.cpp
+++ b/tests/fuzzer/resolv_gethostbyname_fuzzer.cpp
@@ -29,11 +29,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
[[maybe_unused]] static const bool initialized = DoInit();
FuzzedDataProvider fdp(data, size);
- // Chooses doh or dot.
- std::string flag = fdp.PickValueInArray({"0", "1"});
- ScopedSystemProperties sp(kDohFlag, flag);
- android::net::Experiments::getInstance()->update();
-
auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
// Chooses private DNS or not.
if (fdp.ConsumeBool()) parcel.tlsServers = {};
diff --git a/tests/resolv_cache_unit_test.cpp b/tests/resolv_cache_unit_test.cpp
index 154c6d66..49ccab87 100644
--- a/tests/resolv_cache_unit_test.cpp
+++ b/tests/resolv_cache_unit_test.cpp
@@ -50,6 +50,7 @@ constexpr int DNS_PORT = 53;
// Constant values sync'd from res_cache.cpp
constexpr int DNS_HEADER_SIZE = 12;
constexpr int MAX_ENTRIES_DEFAULT = 64 * 2 * 5;
+constexpr int MAX_ENTRIES_LOWER_BOUND = 0;
constexpr int MAX_ENTRIES_UPPER_BOUND = 100 * 1000;
namespace {
@@ -627,8 +628,11 @@ class ResolvCacheParameterizedTest : public ResolvCacheTest,
public testing::WithParamInterface<int> {};
INSTANTIATE_TEST_SUITE_P(MaxCacheEntries, ResolvCacheParameterizedTest,
- testing::Values(0, MAX_ENTRIES_UPPER_BOUND + 1),
+ testing::Values(MAX_ENTRIES_LOWER_BOUND - 1, MAX_ENTRIES_UPPER_BOUND + 1),
[](const testing::TestParamInfo<int>& info) {
+ if (info.param < 0) { // '-' is an invalid character in test name
+ return "negative_" + std::to_string(abs(info.param));
+ }
return std::to_string(info.param);
});
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 71f3536d..e4f26fad 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -876,7 +876,7 @@ TEST_F(ResolverTest, GetAddrInfoV4_deferred_resp) {
});
// ensuring t1 and t2 handler functions are processed in order
- usleep(100 * 1000);
+ EXPECT_TRUE(PollForCondition([&]() { return GetNumQueries(dns1, host_name_deferred); }));
std::thread t2([&, this]() {
ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder()
.setDnsServers(servers_for_t2)
diff --git a/tests/resolv_private_dns_test.cpp b/tests/resolv_private_dns_test.cpp
index 2f5603c9..569b74af 100644
--- a/tests/resolv_private_dns_test.cpp
+++ b/tests/resolv_private_dns_test.cpp
@@ -273,7 +273,6 @@ class BasePrivateDnsTest : public BaseTest {
protected:
void SetUp() override {
- mDohScopedProp = std::make_unique<ScopedSystemProperties>(kDohFlag, "1");
mDohQueryTimeoutScopedProp =
std::make_unique<ScopedSystemProperties>(kDohQueryTimeoutFlag, "1000");
unsigned int expectedProbeTimeout = kExpectedDohValidationTimeWhenTimeout.count();
@@ -295,7 +294,6 @@ class BasePrivateDnsTest : public BaseTest {
void TearDown() override {
DumpResolverService();
- mDohScopedProp.reset();
BaseTest::TearDown();
}
@@ -342,8 +340,7 @@ class BasePrivateDnsTest : public BaseTest {
test::DNSResponder doh_backend{"127.0.1.3", kDnsPortString};
test::DNSResponder dot_backend{"127.0.2.3", kDnsPortString};
- // Used to enable DoH during the tests and set up a shorter timeout.
- std::unique_ptr<ScopedSystemProperties> mDohScopedProp;
+ // Used to set up a shorter timeout.
std::unique_ptr<ScopedSystemProperties> mDohQueryTimeoutScopedProp;
std::unique_ptr<ScopedSystemProperties> mDohProbeTimeoutScopedProp;
};
@@ -850,11 +847,7 @@ TEST_F(PrivateDnsDohTest, ExcessDnsRequests) {
ASSERT_TRUE(dot_ipv6.startServer());
ASSERT_TRUE(doh_ipv6.startServer());
- // It might already take several seconds before we are here. Add a ScopedSystemProperties
- // to ensure the doh flag is 1 before creating a new network.
- ScopedSystemProperties sp1(kDohFlag, "1");
mDnsClient.SetupOemNetwork(TEST_NETID_2);
-
parcel.netId = TEST_NETID_2;
parcel.servers = {listen_ipv6_addr};
parcel.tlsServers = {listen_ipv6_addr};
@@ -872,9 +865,6 @@ TEST_F(PrivateDnsDohTest, ExcessDnsRequests) {
// Expect two queries: one for DoH probe and the other one for kQueryHostname.
EXPECT_EQ(doh_ipv6.queries(), 2);
- // Add a ScopedSystemProperties here as well since DnsResolver will update its cached flags
- // when the networks is removed.
- ScopedSystemProperties sp2(kDohFlag, "1");
mDnsClient.TearDownOemNetwork(TEST_NETID_2);
// The DnsResolver will reconnect to the DoH server for the query that gets blocked at
@@ -1063,12 +1053,6 @@ TEST_F(PrivateDnsDohTest, SessionResumption) {
for (const auto& flag : {"0", "1"}) {
SCOPED_TRACE(fmt::format("flag: {}", flag));
ScopedSystemProperties sp(kDohSessionResumptionFlag, flag);
-
- // Each loop takes around 3 seconds, if the system property "doh" is reset in the middle
- // of the first loop, this test will fail when running the second loop because DnsResolver
- // updates its "doh" flag when resetNetwork() is called. Therefore, add another
- // ScopedSystemProperties for "doh" to make the test more robust.
- ScopedSystemProperties sp2(kDohFlag, "1");
resetNetwork();
ASSERT_TRUE(doh.stopServer());
@@ -1108,11 +1092,6 @@ TEST_F(PrivateDnsDohTest, TestEarlyDataFlag) {
SCOPED_TRACE(fmt::format("flag: {}", flag));
ScopedSystemProperties sp1(kDohSessionResumptionFlag, flag);
ScopedSystemProperties sp2(kDohEarlyDataFlag, flag);
-
- // As each loop takes around 2 seconds, it's possible the device_config flags are reset
- // in the middle of the test. Add another ScopedSystemProperties for "doh" to make the
- // test more robust.
- ScopedSystemProperties sp3(kDohFlag, "1");
resetNetwork();
ASSERT_TRUE(doh.stopServer());
diff --git a/tests/resolv_test_utils.h b/tests/resolv_test_utils.h
index a09bde29..1febb520 100644
--- a/tests/resolv_test_utils.h
+++ b/tests/resolv_test_utils.h
@@ -140,7 +140,6 @@ constexpr char kDotPortString[] = "853";
const std::string kFlagPrefix("persist.device_config.netd_native.");
const std::string kDohEarlyDataFlag(kFlagPrefix + "doh_early_data");
-const std::string kDohFlag(kFlagPrefix + "doh");
const std::string kDohIdleTimeoutFlag(kFlagPrefix + "doh_idle_timeout_ms");
const std::string kDohProbeTimeoutFlag(kFlagPrefix + "doh_probe_timeout_ms");
const std::string kDohQueryTimeoutFlag(kFlagPrefix + "doh_query_timeout_ms");
@@ -163,6 +162,10 @@ const std::string kSkip4aQueryOnV6LinklocalAddrFlag(kFlagPrefix +
"skip_4a_query_on_v6_linklocal_addr");
const std::string kSortNameserversFlag(kFlagPrefix + "sort_nameservers");
+const std::string kPersistNetPrefix("persist.net.");
+
+const std::string kQueryLogSize(kPersistNetPrefix + "dns_query_log_size");
+
static constexpr char kLocalHost[] = "localhost";
static constexpr char kLocalHostAddr[] = "127.0.0.1";
static constexpr char kIp6LocalHost[] = "ip6-localhost";
diff --git a/util.h b/util.h
index a7750fc4..fd9e2d40 100644
--- a/util.h
+++ b/util.h
@@ -62,12 +62,7 @@ inline bool isDebuggable() {
return android::base::GetBoolProperty("ro.debuggable", false);
}
-inline bool isDoHEnabled() {
- static bool isAtLeastT = android::modules::sdklevel::IsAtLeastT();
- return android::net::Experiments::getInstance()->getFlag("doh", isAtLeastT ? 1 : 0);
-}
-
inline bool isAtLeastU() {
const static bool isAtLeastU = android::modules::sdklevel::IsAtLeastU();
return isAtLeastU;
-} \ No newline at end of file
+}