aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.bp9
-rw-r--r--tests/dns_metrics_listener/dns_metrics_listener.cpp67
-rw-r--r--tests/dns_metrics_listener/dns_metrics_listener.h9
-rw-r--r--tests/dns_responder/dns_responder.cpp3
-rw-r--r--tests/dns_responder/dns_responder_client_ndk.cpp18
-rw-r--r--tests/dns_responder/dns_responder_client_ndk.h14
-rw-r--r--tests/dnsresolver_binder_test.cpp119
-rw-r--r--tests/doh_ffi_test.cpp2
-rw-r--r--tests/resolv_cache_unit_test.cpp69
-rw-r--r--tests/resolv_integration_test.cpp185
-rw-r--r--tests/resolv_private_dns_test.cpp202
-rw-r--r--tests/resolv_stats_test_utils.cpp3
-rw-r--r--tests/resolv_stats_test_utils.h4
-rw-r--r--tests/resolv_stats_test_utils_test.cpp4
-rw-r--r--tests/resolv_test_utils.cpp4
-rw-r--r--tests/resolv_test_utils.h52
-rw-r--r--tests/resolv_unit_test.cpp4
-rw-r--r--tests/unsolicited_listener/unsolicited_event_listener.cpp64
18 files changed, 490 insertions, 342 deletions
diff --git a/tests/Android.bp b/tests/Android.bp
index 9d8f1e7f..b1266037 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -192,21 +192,24 @@ cc_test {
],
static_libs: [
"dnsresolver_aidl_interface-lateststable-ndk",
+ "libc++fs",
"libconnectivity_native_test_utils",
"libcrypto_static",
+ "libcutils",
+ "libdoh_frontend_ffi",
"libgmock",
+ "libip_checksum",
"libmodules-utils-build",
"libnetd_test_dnsresponder_ndk",
"libnetd_test_metrics_listener",
"libnetd_test_resolv_utils",
"libnetdutils",
+ "libnettestutils",
"libssl",
- "libcutils",
+ "libutils",
"netd_aidl_interface-lateststable-ndk",
"netd_event_listener_interface-lateststable-ndk",
- "libip_checksum",
"resolv_unsolicited_listener",
- "libdoh_frontend_ffi",
],
// This test talks to the DnsResolver module over a binary protocol on a socket, so keep it as
// multilib setting is worth because we might be able to get some coverage for the case where
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.cpp b/tests/dns_metrics_listener/dns_metrics_listener.cpp
index 1715118d..c1d4883d 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/dns_metrics_listener.cpp
@@ -18,17 +18,13 @@
#include <thread>
-#include <android-base/chrono_utils.h>
#include <android-base/format.h>
-namespace android {
-namespace net {
-namespace metrics {
+namespace android::net::metrics {
using android::base::ScopedLockAssertion;
using std::chrono::milliseconds;
-constexpr milliseconds kRetryIntervalMs{20};
constexpr milliseconds kEventTimeoutMs{5000};
bool DnsMetricsListener::DnsEvent::operator==(const DnsMetricsListener::DnsEvent& o) const {
@@ -49,6 +45,7 @@ std::ostream& operator<<(std::ostream& os, const DnsMetricsListener::DnsEvent& d
std::lock_guard lock(mMutex);
mUnexpectedNat64PrefixUpdates++;
if (netId == mNetId) mNat64Prefix = added ? prefixString : "";
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
@@ -60,7 +57,7 @@ std::ostream& operator<<(std::ostream& os, const DnsMetricsListener::DnsEvent& d
// keep updating the server to have latest validation status.
mValidationRecords.insert_or_assign({netId, ipAddress}, validated);
}
- mCv.notify_one();
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
@@ -74,39 +71,32 @@ std::ostream& operator<<(std::ostream& os, const DnsMetricsListener::DnsEvent& d
mDnsEventRecords.push(
{netId, eventType, returnCode, hostname, ipAddresses, ipAddressesCount});
}
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
bool DnsMetricsListener::waitForNat64Prefix(ExpectNat64PrefixStatus status, milliseconds timeout) {
- android::base::Timer t;
- while (t.duration() < timeout) {
- {
- std::lock_guard lock(mMutex);
- if ((status == EXPECT_FOUND && !mNat64Prefix.empty()) ||
- (status == EXPECT_NOT_FOUND && mNat64Prefix.empty())) {
- mUnexpectedNat64PrefixUpdates--;
- return true;
- }
- }
- std::this_thread::sleep_for(kRetryIntervalMs);
+ std::unique_lock lock(mMutex);
+ ScopedLockAssertion assume_lock(mMutex);
+
+ if (mCv.wait_for(lock, timeout, [&]() REQUIRES(mMutex) {
+ return (status == EXPECT_FOUND && !mNat64Prefix.empty()) ||
+ (status == EXPECT_NOT_FOUND && mNat64Prefix.empty());
+ })) {
+ mUnexpectedNat64PrefixUpdates--;
+ return true;
}
+
+ // Timeout.
return false;
}
bool DnsMetricsListener::waitForPrivateDnsValidation(const std::string& serverAddr,
const bool validated) {
- const auto now = std::chrono::steady_clock::now();
-
std::unique_lock lock(mMutex);
- ScopedLockAssertion assume_lock(mMutex);
-
- // onPrivateDnsValidationEvent() might already be invoked. Search for the record first.
- do {
- if (findAndRemoveValidationRecord({mNetId, serverAddr}, validated)) return true;
- } while (mCv.wait_until(lock, now + kEventTimeoutMs) != std::cv_status::timeout);
-
- // Timeout.
- return false;
+ return mCv.wait_for(lock, kEventTimeoutMs, [&]() REQUIRES(mMutex) {
+ return findAndRemoveValidationRecord({mNetId, serverAddr}, validated);
+ });
}
bool DnsMetricsListener::findAndRemoveValidationRecord(const ServerKey& key, const bool value) {
@@ -119,24 +109,17 @@ bool DnsMetricsListener::findAndRemoveValidationRecord(const ServerKey& key, con
}
std::optional<DnsMetricsListener::DnsEvent> DnsMetricsListener::popDnsEvent() {
- // Wait until the queue is not empty or timeout.
- android::base::Timer t;
- while (t.duration() < milliseconds{1000}) {
- {
- std::lock_guard lock(mMutex);
- if (!mDnsEventRecords.empty()) break;
- }
- std::this_thread::sleep_for(kRetryIntervalMs);
- }
+ std::unique_lock lock(mMutex);
+ ScopedLockAssertion assume_lock(mMutex);
- std::lock_guard lock(mMutex);
- if (mDnsEventRecords.empty()) return std::nullopt;
+ if (!mCv.wait_for(lock, kEventTimeoutMs,
+ [&]() REQUIRES(mMutex) { return !mDnsEventRecords.empty(); })) {
+ return std::nullopt;
+ }
auto ret = mDnsEventRecords.front();
mDnsEventRecords.pop();
return ret;
}
-} // namespace metrics
-} // namespace net
-} // namespace android
+} // namespace android::net::metrics
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.h b/tests/dns_metrics_listener/dns_metrics_listener.h
index ff714664..e34662b2 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.h
+++ b/tests/dns_metrics_listener/dns_metrics_listener.h
@@ -30,11 +30,8 @@ enum ExpectNat64PrefixStatus : bool {
EXPECT_NOT_FOUND,
};
-namespace android {
-namespace net {
-namespace metrics {
+namespace android::net::metrics {
-// TODO: Perhaps use condition variable but continually polling.
// TODO: Perhaps create a queue to monitor the event changes. That improves the unit test which can
// verify the event count, the event change order, and so on.
class DnsMetricsListener : public BaseMetricsListener {
@@ -131,6 +128,4 @@ class DnsMetricsListener : public BaseMetricsListener {
std::condition_variable mCv;
};
-} // namespace metrics
-} // namespace net
-} // namespace android
+} // namespace android::net::metrics
diff --git a/tests/dns_responder/dns_responder.cpp b/tests/dns_responder/dns_responder.cpp
index c4551ebf..75a2c599 100644
--- a/tests/dns_responder/dns_responder.cpp
+++ b/tests/dns_responder/dns_responder.cpp
@@ -730,7 +730,8 @@ void DNSResponder::requestHandler() {
bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, int protocol, char* response,
size_t* response_len) const {
- LOG(DEBUG) << "request: '" << bytesToHexStr({reinterpret_cast<const uint8_t*>(buffer), len})
+ LOG(DEBUG) << "request: '"
+ << bytesToHexStr(std::span(reinterpret_cast<const uint8_t*>(buffer), len))
<< "', on " << dnsproto2str(protocol);
const char* buffer_end = buffer + len;
DNSHeader header;
diff --git a/tests/dns_responder/dns_responder_client_ndk.cpp b/tests/dns_responder/dns_responder_client_ndk.cpp
index ee360dc2..b69ce183 100644
--- a/tests/dns_responder/dns_responder_client_ndk.cpp
+++ b/tests/dns_responder/dns_responder_client_ndk.cpp
@@ -30,6 +30,7 @@ using aidl::android::net::IDnsResolver;
using aidl::android::net::INetd;
using aidl::android::net::ResolverOptionsParcel;
using aidl::android::net::ResolverParamsParcel;
+using aidl::android::net::resolv::aidl::DohParamsParcel;
using android::base::Error;
using android::base::Result;
using android::net::ResolverStats;
@@ -51,6 +52,7 @@ ResolverParams::Builder::Builder() {
mParcel.tlsServers = {kDefaultServer};
mParcel.caCertificate = kCaCert;
mParcel.resolverOptions = ResolverOptionsParcel{}; // optional, must be explicitly set.
+ mParcel.dohParams = std::nullopt;
}
void DnsResponderClient::SetupMappings(unsigned numHosts, const std::vector<std::string>& domains,
@@ -116,22 +118,6 @@ Result<ResolverInfo> DnsResponderClient::getResolverInfo() {
}
bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
- const std::vector<std::string>& domains,
- std::vector<int> params) {
- params.resize(IDnsResolver::RESOLVER_PARAMS_COUNT);
- std::array<int, IDnsResolver::RESOLVER_PARAMS_COUNT> arr;
- std::copy_n(params.begin(), arr.size(), arr.begin());
- const auto resolverParams = ResolverParams::Builder()
- .setDomains(domains)
- .setDnsServers(servers)
- .setDotServers({})
- .setParams(arr)
- .build();
- const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
- return rv.isOk();
-}
-
-bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
const std::vector<std::string>& domains) {
const auto resolverParams = ResolverParams::Builder()
.setDomains(domains)
diff --git a/tests/dns_responder/dns_responder_client_ndk.h b/tests/dns_responder/dns_responder_client_ndk.h
index 1b4ba35d..0713a7ca 100644
--- a/tests/dns_responder/dns_responder_client_ndk.h
+++ b/tests/dns_responder/dns_responder_client_ndk.h
@@ -91,6 +91,15 @@ class ResolverParams {
mParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
return *this;
}
+ constexpr Builder& setMetered(const bool metered) {
+ mParcel.meteredNetwork = metered;
+ return *this;
+ }
+ constexpr Builder& setDohParams(
+ const aidl::android::net::resolv::aidl::DohParamsParcel& dohParams) {
+ mParcel.dohParams = dohParams;
+ return *this;
+ }
aidl::android::net::ResolverParamsParcel build() { return mParcel; }
private:
@@ -114,11 +123,6 @@ class DnsResponderClient {
static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
std::vector<Mapping>* mappings);
- // For dns_benchmark built from tm-mainline-prod.
- // TODO: Remove it when possible.
- bool SetResolversForNetwork(const std::vector<std::string>& servers,
- const std::vector<std::string>& domains, std::vector<int> params);
-
// Sets up DnsResolver with given DNS servers. This is used to set up for private DNS off mode.
bool SetResolversForNetwork(const std::vector<std::string>& servers = {kDefaultServer},
const std::vector<std::string>& domains = {kDefaultSearchDomain});
diff --git a/tests/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
index dc30f71f..799e16a6 100644
--- a/tests/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -37,6 +37,7 @@
#include <gtest/gtest.h>
#include <netdutils/NetNativeTestBase.h>
#include <netdutils/Stopwatch.h>
+#include <nettestutils/DumpService.h>
#include <util.h>
#include "dns_metrics_listener/base_metrics_listener.h"
@@ -52,7 +53,9 @@ using aidl::android::net::ResolverHostsParcel;
using aidl::android::net::ResolverOptionsParcel;
using aidl::android::net::ResolverParamsParcel;
using aidl::android::net::metrics::INetdEventListener;
+using aidl::android::net::resolv::aidl::DohParamsParcel;
using android::base::ReadFdToString;
+using android::base::StringReplace;
using android::base::unique_fd;
using android::net::ResolverStats;
using android::net::metrics::TestOnDnsEvent;
@@ -63,37 +66,6 @@ using android::netdutils::Stopwatch;
// Sync from TEST_NETID in dns_responder_client.cpp as resolv_integration_test.cpp does.
constexpr int TEST_NETID = 30;
-namespace {
-
-std::vector<std::string> dumpService(ndk::SpAIBinder binder) {
- unique_fd localFd, remoteFd;
- bool success = Pipe(&localFd, &remoteFd);
- EXPECT_TRUE(success) << "Failed to open pipe for dumping: " << strerror(errno);
- if (!success) return {};
-
- // dump() blocks until another thread has consumed all its output.
- std::thread dumpThread = std::thread([binder, remoteFd{std::move(remoteFd)}]() {
- EXPECT_EQ(STATUS_OK, AIBinder_dump(binder.get(), remoteFd, nullptr, 0));
- });
-
- std::string dumpContent;
-
- EXPECT_TRUE(ReadFdToString(localFd.get(), &dumpContent))
- << "Error during dump: " << strerror(errno);
- dumpThread.join();
-
- std::stringstream dumpStream(std::move(dumpContent));
- std::vector<std::string> lines;
- std::string line;
- while (std::getline(dumpStream, line)) {
- lines.push_back(std::move(line));
- }
-
- return lines;
-}
-
-} // namespace
-
class DnsResolverBinderTest : public NetNativeTestBase {
public:
DnsResolverBinderTest() {
@@ -118,7 +90,10 @@ class DnsResolverBinderTest : public NetNativeTestBase {
// This could happen when the test isn't running as root, or if netd isn't running.
assert(nullptr != netdBinder.get());
// Send the service dump request to netd.
- std::vector<std::string> lines = dumpService(netdBinder);
+ std::vector<std::string> lines;
+ const android::status_t ret =
+ dumpService(netdBinder, /*args=*/nullptr, /*num_args=*/0, lines);
+ ASSERT_EQ(android::OK, ret) << "Error dumping service: " << android::statusToString(ret);
// Basic regexp to match dump output lines. Matches the beginning and end of the line, and
// puts the output of the command itself into the first match group.
@@ -142,7 +117,6 @@ class DnsResolverBinderTest : public NetNativeTestBase {
// information. To keep it working on Q/R/..., remove what has been
// added for now. TODO(b/266248339)
std::string output = match[1].str();
- using android::base::StringReplace;
output = StringReplace(output, "(null)", "", /*all=*/true);
output = StringReplace(output, "<unimplemented>", "", /*all=*/true);
output = StringReplace(output, "<interface>", "", /*all=*/true);
@@ -198,45 +172,12 @@ class DnsResolverBinderTest : public NetNativeTestBase {
LogData withoutPacel;
};
- std::string toString(const std::vector<ResolverHostsParcel>& parms) {
- std::string o;
- const size_t size = parms.size();
- for (size_t i = 0; i < size; ++i) {
- o.append(fmt::format("ResolverHostsParcel{{ipAddr: {}, hostName: {}}}", parms[i].ipAddr,
- parms[i].hostName));
- if (i + 1 < size) o.append(", ");
- }
- return o;
- }
-
- std::string toString(const std::optional<ResolverOptionsParcel>& parms) {
- if (!parms.has_value()) return "(null)";
- return fmt::format("ResolverOptionsParcel{{hosts: [{}], tcMode: {}, enforceDnsUid: {}}}",
- toString(parms->hosts), parms->tcMode, parms->enforceDnsUid);
- }
-
- std::string toString(const ResolverParamsParcel& parms) {
- return fmt::format(
- "ResolverParamsParcel{{netId: {}, sampleValiditySeconds: {}, successThreshold: {}, "
- "minSamples: {}, "
- "maxSamples: {}, baseTimeoutMsec: {}, retryCount: {}, "
- "servers: [{}], domains: [{}], "
- "tlsName: {}, tlsServers: [{}], "
- "tlsFingerprints: [{}], "
- "caCertificate: {}, tlsConnectTimeoutMs: {}, "
- "resolverOptions: {}, transportTypes: [{}]}}",
- parms.netId, parms.sampleValiditySeconds, parms.successThreshold, parms.minSamples,
- parms.maxSamples, parms.baseTimeoutMsec, parms.retryCount,
- fmt::join(parms.servers, ", "), fmt::join(parms.domains, ", "), parms.tlsName,
- fmt::join(parms.tlsServers, ", "), fmt::join(parms.tlsFingerprints, ", "),
- android::base::StringReplace(parms.caCertificate, "\n", "\\n", true),
- parms.tlsConnectTimeoutMs, toString(parms.resolverOptions),
- fmt::join(parms.transportTypes, ", "));
- }
-
PossibleLogData toSetResolverConfigurationLogData(const ResolverParamsParcel& parms,
int returnCode = 0) {
- std::string outputWithParcel = "setResolverConfiguration(" + toString(parms) + ")";
+ // Replace "\n" with "\\n" in parms.caCertificate.
+ std::string outputWithParcel =
+ fmt::format("setResolverConfiguration({})",
+ StringReplace(parms.toString(), "\n", "\\n", /*all=*/true));
std::string hintRegexWithParcel = fmt::format("setResolverConfiguration.*{}", parms.netId);
std::string outputWithoutParcel = "setResolverConfiguration()";
@@ -498,6 +439,40 @@ TEST_F(DnsResolverBinderTest, SetResolverConfiguration_TransportTypes_Default) {
EXPECT_THAT(str, HasSubstr("UNKNOWN"));
}
+TEST_F(DnsResolverBinderTest, SetResolverConfiguration_DohParams) {
+ const auto paramsWithoutDohParams = ResolverParams::Builder().build();
+ ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(paramsWithoutDohParams);
+ EXPECT_TRUE(status.isOk()) << status.getMessage();
+ mExpectedLogDataWithPacel.push_back(toSetResolverConfigurationLogData(paramsWithoutDohParams));
+
+ const DohParamsParcel dohParams = {
+ .name = "doh.google",
+ .ips = {"1.2.3.4", "2001:db8::2"},
+ .dohpath = "/dns-query{?dns}",
+ .port = 443,
+ };
+ const auto paramsWithDohParams = ResolverParams::Builder().setDohParams(dohParams).build();
+ status = mDnsResolver->setResolverConfiguration(paramsWithDohParams);
+ EXPECT_TRUE(status.isOk()) << status.getMessage();
+ mExpectedLogDataWithPacel.push_back(toSetResolverConfigurationLogData(paramsWithDohParams));
+}
+
+class MeteredNetworkParameterizedTest : public DnsResolverBinderTest,
+ public testing::WithParamInterface<bool> {};
+
+INSTANTIATE_TEST_SUITE_P(SetResolverConfigurationTest, MeteredNetworkParameterizedTest,
+ testing::Bool(), [](const testing::TestParamInfo<bool>& info) {
+ return info.param ? "Metered" : "NotMetered";
+ });
+
+TEST_P(MeteredNetworkParameterizedTest, MeteredTest) {
+ const auto resolverParams = ResolverParams::Builder().setMetered(GetParam()).build();
+ ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
+ EXPECT_TRUE(status.isOk()) << status.getMessage();
+
+ mExpectedLogDataWithPacel.push_back(toSetResolverConfigurationLogData(resolverParams));
+}
+
TEST_F(DnsResolverBinderTest, GetResolverInfo) {
std::vector<std::string> servers = {"127.0.0.1", "127.0.0.2"};
std::vector<std::string> domains = {"example.com"};
@@ -640,9 +615,9 @@ TEST_F(DnsResolverBinderTest, SetResolverOptions) {
options.enforceDnsUid = true;
EXPECT_TRUE(mDnsResolver->setResolverOptions(TEST_NETID, options).isOk());
mExpectedLogData.push_back(
- {"setResolverOptions(30, " + toString(options) + ")", "setResolverOptions.*30"});
+ {"setResolverOptions(30, " + options.toString() + ")", "setResolverOptions.*30"});
EXPECT_EQ(ENONET, mDnsResolver->setResolverOptions(-1, options).getServiceSpecificError());
- mExpectedLogData.push_back({"setResolverOptions(-1, " + toString(options) +
+ mExpectedLogData.push_back({"setResolverOptions(-1, " + options.toString() +
") -> ServiceSpecificException(64, \"Machine is not on the "
"network\")",
"setResolverOptions.*-1.*64"});
diff --git a/tests/doh_ffi_test.cpp b/tests/doh_ffi_test.cpp
index 0e51402a..6d5b0492 100644
--- a/tests/doh_ffi_test.cpp
+++ b/tests/doh_ffi_test.cpp
@@ -73,7 +73,7 @@ bool haveIpv6() {
class DoHFFITest : public NetNativeTestBase {
public:
- static void SetUpTestSuite() { doh_init_logger(DOH_LOG_LEVEL_DEBUG); }
+ static void SetUpTestSuite() { doh_init_logger(DOH_LOG_LEVEL_TRACE); }
};
TEST_F(DoHFFITest, SmokeTest) {
diff --git a/tests/resolv_cache_unit_test.cpp b/tests/resolv_cache_unit_test.cpp
index defd2da6..ef9ecb30 100644
--- a/tests/resolv_cache_unit_test.cpp
+++ b/tests/resolv_cache_unit_test.cpp
@@ -66,6 +66,7 @@ struct SetupParams {
res_params params;
aidl::android::net::ResolverOptionsParcel resolverOptions;
std::vector<int32_t> transportTypes;
+ bool metered;
};
struct CacheStats {
@@ -206,7 +207,7 @@ class ResolvCacheTest : public NetNativeTestBase {
int cacheSetupResolver(uint32_t netId, const SetupParams& setup) {
return resolv_set_nameservers(netId, setup.servers, setup.domains, setup.params,
- setup.resolverOptions, setup.transportTypes);
+ setup.resolverOptions, setup.transportTypes, setup.metered);
}
void cacheAddStats(uint32_t netId, int revision_id, const IPSockAddr& ipsa,
@@ -928,6 +929,72 @@ TEST_F(ResolvCacheTest, GetResolverStats) {
}
}
+TEST_F(ResolvCacheTest, IsEnforceDnsUidEnabled) {
+ const SetupParams unenforcedDnsUidCfg = {
+ .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
+ .domains = {"domain1.com", "domain2.com"},
+ .params = kParams,
+ };
+ // Network #1
+ EXPECT_EQ(0, cacheCreate(TEST_NETID));
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, unenforcedDnsUidCfg));
+ EXPECT_FALSE(resolv_is_enforceDnsUid_enabled_network(TEST_NETID));
+
+ // Network #2
+ EXPECT_EQ(0, cacheCreate(TEST_NETID + 1));
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID + 1, unenforcedDnsUidCfg));
+ EXPECT_FALSE(resolv_is_enforceDnsUid_enabled_network(TEST_NETID + 1));
+
+ // Change the enforceDnsUid setting on network #1
+ const SetupParams enforcedDnsUidCfg = {
+ .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
+ .domains = {"domain1.com", "domain2.com"},
+ .params = kParams,
+ .resolverOptions = {.enforceDnsUid = true},
+ };
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, enforcedDnsUidCfg));
+ EXPECT_TRUE(resolv_is_enforceDnsUid_enabled_network(TEST_NETID));
+
+ // Network #2 is unaffected
+ EXPECT_FALSE(resolv_is_enforceDnsUid_enabled_network(TEST_NETID + 1));
+
+ // Returns false on non-existent network
+ EXPECT_FALSE(resolv_is_enforceDnsUid_enabled_network(TEST_NETID + 2));
+}
+
+TEST_F(ResolvCacheTest, IsNetworkMetered) {
+ const SetupParams defaultCfg = {
+ .servers = {"127.0.0.1"},
+ .domains = {"domain1.com"},
+ .params = kParams,
+ };
+ // Network #1
+ EXPECT_EQ(0, cacheCreate(TEST_NETID));
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, defaultCfg));
+ EXPECT_FALSE(resolv_is_metered_network(TEST_NETID));
+
+ // Network #2
+ EXPECT_EQ(0, cacheCreate(TEST_NETID + 1));
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID + 1, defaultCfg));
+ EXPECT_FALSE(resolv_is_metered_network(TEST_NETID + 1));
+
+ // Change the metered setting on network #1
+ const SetupParams meteredCfg = {
+ .servers = {"127.0.0.1"},
+ .domains = {"domain1.com"},
+ .params = kParams,
+ .metered = true,
+ };
+ EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, meteredCfg));
+ EXPECT_TRUE(resolv_is_metered_network(TEST_NETID));
+
+ // Network #2 is unaffected
+ EXPECT_FALSE(resolv_is_metered_network(TEST_NETID + 1));
+
+ // Returns false on non-existent network
+ EXPECT_FALSE(resolv_is_metered_network(TEST_NETID + 2));
+}
+
namespace {
constexpr int EAI_OK = 0;
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index ff62da45..ef2bf1e5 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -127,6 +127,8 @@ using android::netdutils::ScopedAddrinfo;
using android::netdutils::Stopwatch;
using android::netdutils::toHex;
+namespace fs = std::filesystem;
+
namespace {
std::pair<ScopedAddrinfo, int> safe_getaddrinfo_time_taken(const char* node, const char* service,
@@ -860,6 +862,7 @@ TEST_F(ResolverTest, GetAddrInfoV4_deferred_resp) {
addrinfo hints = {.ai_family = AF_INET};
const std::array<int, IDnsResolver::RESOLVER_PARAMS_COUNT> params = {300, 25, 8, 8, 5000, 0};
bool t3_task_done = false;
+ bool t2_sv_setup_done = false;
dns1.setDeferredResp(true);
std::thread t1([&, this]() {
@@ -884,6 +887,7 @@ TEST_F(ResolverTest, GetAddrInfoV4_deferred_resp) {
.setDotServers({})
.setParams(params)
.build()));
+ t2_sv_setup_done = true;
ScopedAddrinfo result = safe_getaddrinfo(host_name_deferred, nullptr, &hints);
EXPECT_TRUE(t3_task_done);
EXPECT_EQ(0U, GetNumQueries(dns2, host_name_deferred));
@@ -895,7 +899,7 @@ TEST_F(ResolverTest, GetAddrInfoV4_deferred_resp) {
});
// ensuring t2 and t3 handler functions are processed in order
- usleep(100 * 1000);
+ EXPECT_TRUE(PollForCondition([&]() { return t2_sv_setup_done; }));
std::thread t3([&, this]() {
ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder()
.setDnsServers(servers_for_t3)
@@ -4503,9 +4507,9 @@ TEST_F(ResolverTest, GetAddrinfo_BlockDnsQueryWithUidRule) {
const char* hname;
const int expectedErrorCode;
} kTestData[] = {
- {host_name, EAI_NODATA},
+ {host_name, (isAtLeastT() && fs::exists(DNS_HELPER)) ? EAI_FAIL : EAI_NODATA},
// To test the query with search domain.
- {"howdy", EAI_AGAIN},
+ {"howdy", (isAtLeastT() && fs::exists(DNS_HELPER)) ? EAI_FAIL : EAI_AGAIN},
};
INetd* netdService = mDnsClient.netdService();
@@ -4823,77 +4827,76 @@ TEST_F(ResolverTest, ConnectTlsServerTimeout_ConcurrentQueries) {
}
}
+// Tests that the DoT query timeout is configurable via the feature flag "dot_query_timeout_ms".
+// The test DoT server is configured to postpone DNS queries for DOT_SERVER_UNRESPONSIVE_TIME_MS
+// (2s). If the feature flag is set to a positive value smaller than
+// DOT_SERVER_UNRESPONSIVE_TIME_MS, DoT queries should timeout.
TEST_F(ResolverTest, QueryTlsServerTimeout) {
- constexpr uint32_t cacheFlag = ANDROID_RESOLV_NO_CACHE_LOOKUP;
- constexpr int INFINITE_QUERY_TIMEOUT = -1;
- constexpr int DOT_SERVER_UNRESPONSIVE_TIME_MS = 5000;
+ constexpr int DOT_SERVER_UNRESPONSIVE_TIME_MS = 2000;
+ constexpr int TIMING_TOLERANCE_MS = 200;
constexpr char hostname1[] = "query1.example.com.";
- constexpr char hostname2[] = "query2.example.com.";
const std::vector<DnsRecord> records = {
{hostname1, ns_type::ns_t_a, "1.2.3.4"},
- {hostname2, ns_type::ns_t_a, "1.2.3.5"},
};
- for (const int queryTimeoutMs : {INFINITE_QUERY_TIMEOUT, 1000}) {
- for (const std::string_view dnsMode : {"OPPORTUNISTIC", "STRICT"}) {
- SCOPED_TRACE(fmt::format("testConfig: [{}] [{}]", dnsMode, queryTimeoutMs));
-
- const std::string addr = getUniqueIPv4Address();
- test::DNSResponder dns(addr);
- StartDns(dns, records);
- test::DnsTlsFrontend tls(addr, "853", addr, "53");
- ASSERT_TRUE(tls.startServer());
+ static const struct TestConfig {
+ std::string dnsMode;
+ int queryTimeoutMs;
+ int expectResultTimedOut;
+ int expectedTimeTakenMs;
+ } testConfigs[] = {
+ // clang-format off
+ {"OPPORTUNISTIC", -1, false, DOT_SERVER_UNRESPONSIVE_TIME_MS},
+ {"OPPORTUNISTIC", 1000, false, 1000},
+ {"STRICT", -1, false, DOT_SERVER_UNRESPONSIVE_TIME_MS},
+ // `expectResultTimedOut` is true in the following testcase because in strict mode
+ // DnsResolver doesn't try Do53 servers after the DoT query is timed out.
+ {"STRICT", 1000, true, 1000},
+ // clang-format on
+ };
+ for (const auto& config : testConfigs) {
+ SCOPED_TRACE(fmt::format("testConfig: [{}] [{}]", config.dnsMode, config.queryTimeoutMs));
- ScopedSystemProperties sp(kDotQueryTimeoutMsFlag, std::to_string(queryTimeoutMs));
+ const std::string addr = getUniqueIPv4Address();
+ test::DNSResponder dns(addr);
+ StartDns(dns, records);
+ test::DnsTlsFrontend tls(addr, "853", addr, "53");
+ ASSERT_TRUE(tls.startServer());
- // Don't skip unusable DoT servers and disable revalidation for this test.
- ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, "-1");
- ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1");
- resetNetwork();
+ ScopedSystemProperties sp(kDotQueryTimeoutMsFlag, std::to_string(config.queryTimeoutMs));
- auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
- parcel.servers = {addr};
- parcel.tlsServers = {addr};
- if (dnsMode == "STRICT") parcel.tlsName = kDefaultPrivateDnsHostName;
+ // Don't skip unusable DoT servers and disable revalidation for this test.
+ ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, "-1");
+ ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1");
+ resetNetwork();
- ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
- EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
- EXPECT_TRUE(tls.waitForQueries(1));
- tls.clearQueries();
+ auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ parcel.servers = {addr};
+ parcel.tlsServers = {addr};
+ if (config.dnsMode == "STRICT") parcel.tlsName = kDefaultPrivateDnsHostName;
- // Set the DoT server to be unresponsive to DNS queries until either it receives
- // 2 queries or 5s later.
- tls.setDelayQueries(2);
- tls.setDelayQueriesTimeout(DOT_SERVER_UNRESPONSIVE_TIME_MS);
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+ EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
+ EXPECT_TRUE(tls.waitForQueries(1));
+ tls.clearQueries();
- // First query.
- Stopwatch s;
- int fd = resNetworkQuery(TEST_NETID, hostname1, ns_c_in, ns_t_a, cacheFlag);
- if (dnsMode == "STRICT" && queryTimeoutMs != INFINITE_QUERY_TIMEOUT) {
- expectAnswersNotValid(fd, -ETIMEDOUT);
- } else {
- expectAnswersValid(fd, AF_INET, "1.2.3.4");
- }
+ // Set the DoT server to be unresponsive to DNS queries for
+ // `DOT_SERVER_UNRESPONSIVE_TIME_MS` ms.
+ tls.setDelayQueries(999);
+ tls.setDelayQueriesTimeout(DOT_SERVER_UNRESPONSIVE_TIME_MS);
- // Besides checking the result of the query, check how much time the
- // resolver processed the query.
- int timeTakenMs = s.getTimeAndResetUs() / 1000;
- const int expectedTimeTakenMs = (queryTimeoutMs == INFINITE_QUERY_TIMEOUT)
- ? DOT_SERVER_UNRESPONSIVE_TIME_MS
- : queryTimeoutMs;
- EXPECT_GE(timeTakenMs, expectedTimeTakenMs);
- EXPECT_LE(timeTakenMs, expectedTimeTakenMs + 1000);
-
- // Second query.
- tls.setDelayQueries(1);
- fd = resNetworkQuery(TEST_NETID, hostname2, ns_c_in, ns_t_a, cacheFlag);
- expectAnswersValid(fd, AF_INET, "1.2.3.5");
-
- // Also check how much time the resolver processed the query.
- timeTakenMs = s.timeTakenUs() / 1000;
- EXPECT_LE(timeTakenMs, 500);
- EXPECT_TRUE(tls.waitForQueries(2));
+ // Send a DNS query, and then check the result and the response time.
+ Stopwatch s;
+ int fd = resNetworkQuery(TEST_NETID, hostname1, ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+ if (config.expectResultTimedOut) {
+ expectAnswersNotValid(fd, -ETIMEDOUT);
+ } else {
+ expectAnswersValid(fd, AF_INET, "1.2.3.4");
}
+ const int timeTakenMs = s.getTimeAndResetUs() / 1000;
+ EXPECT_NEAR(config.expectedTimeTakenMs, timeTakenMs, TIMING_TOLERANCE_MS);
+ EXPECT_TRUE(tls.waitForQueries(1));
}
}
@@ -5778,8 +5781,9 @@ TEST_F(ResolverTest, RepeatedSetup_KeepChangingPrivateDnsServers) {
for (const auto& serverState : {WORKING, UNSUPPORTED, UNRESPONSIVE}) {
int testIndex = 0;
for (const auto& config : testConfigs) {
- SCOPED_TRACE(fmt::format("serverState:{} testIndex:{} testConfig:[{}]", serverState,
- testIndex++, config.asTestName()));
+ SCOPED_TRACE(fmt::format("serverState:{} testIndex:{} testConfig:[{}]",
+ static_cast<int>(serverState), testIndex++,
+ config.asTestName()));
auto& tls = (config.tlsServer == addr1) ? tls1 : tls2;
if (serverState == UNSUPPORTED && tls.running()) ASSERT_TRUE(tls.stopServer());
@@ -7427,7 +7431,7 @@ TEST_F(ResolverMultinetworkTest, GetAddrInfo_AI_ADDRCONFIG) {
ConnectivityType::V4V6,
};
for (const auto& type : allTypes) {
- SCOPED_TRACE(fmt::format("ConnectivityType: {}", type));
+ SCOPED_TRACE(fmt::format("ConnectivityType: {}", static_cast<int>(type)));
// Create a network.
ScopedPhysicalNetwork network = CreateScopedPhysicalNetwork(type);
@@ -7559,7 +7563,7 @@ TEST_F(ResolverMultinetworkTest, DnsWithVpn) {
{ConnectivityType::V4V6, {ipv6_addr, ipv4_addr}},
};
for (const auto& [type, result] : testPairs) {
- SCOPED_TRACE(fmt::format("ConnectivityType: {}", type));
+ SCOPED_TRACE(fmt::format("ConnectivityType: {}", static_cast<int>(type)));
// Create a network.
ScopedPhysicalNetwork underlyingNetwork = CreateScopedPhysicalNetwork(type, "Underlying");
@@ -7679,7 +7683,7 @@ TEST_F(ResolverMultinetworkTest, PerAppDefaultNetwork) {
{ConnectivityType::V4V6, {ipv6_addr, ipv4_addr}},
};
for (const auto& [ipVersion, expectedDnsReply] : testPairs) {
- SCOPED_TRACE(fmt::format("ConnectivityType: {}", ipVersion));
+ SCOPED_TRACE(fmt::format("ConnectivityType: {}", static_cast<int>(ipVersion)));
// Create networks.
ScopedPhysicalNetwork sysDefaultNetwork =
@@ -7809,55 +7813,6 @@ TEST_F(ResolverMultinetworkTest, IPv6LinkLocalWithDefaultRoute) {
EXPECT_EQ(GetNumQueriesForType(*dnsPair->dnsServer, ns_type::ns_t_aaaa, host_name), 1U);
}
-// Test if the "do not send AAAA query when IPv6 address is link-local with a default route" feature
-// can be toggled by flag.
-TEST_F(ResolverMultinetworkTest, IPv6LinkLocalWithDefaultRouteFlag) {
- // Kernel 4.4 does not provide an IPv6 link-local address when an interface is added to a
- // network. Skip it because v6 link-local address is a prerequisite for this test.
- SKIP_IF_KERNEL_VERSION_LOWER_THAN(4, 9, 0);
-
- constexpr char host_name[] = "ohayou.example.com.";
- const struct TestConfig {
- std::string flagValue;
- std::vector<std::string> ips;
- unsigned numOfQuadAQuery;
- } TestConfigs[]{{"0", {"192.0.2.0", "2001:db8:cafe:d00d::31"}, 1U}, {"1", {"192.0.2.0"}, 0U}};
-
- for (const auto& config : TestConfigs) {
- SCOPED_TRACE(fmt::format("flagValue = {}, numOfQuadAQuery = {}", config.flagValue,
- config.numOfQuadAQuery));
-
- ScopedSystemProperties sp1(kSkip4aQueryOnV6LinklocalAddrFlag, config.flagValue);
- ScopedPhysicalNetwork network = CreateScopedPhysicalNetwork(ConnectivityType::V4);
- ASSERT_RESULT_OK(network.init());
-
- // Add IPv6 default route
- ASSERT_TRUE(mDnsClient.netdService()
- ->networkAddRoute(network.netId(), network.ifname(), "::/0", "")
- .isOk());
-
- // Ensuring that routing is applied. This is required for mainline test (b/257404586).
- usleep(1000 * 1000);
-
- const Result<DnsServerPair> dnsPair = network.addIpv4Dns();
- ASSERT_RESULT_OK(dnsPair);
- StartDns(*dnsPair->dnsServer, {{host_name, ns_type::ns_t_a, "192.0.2.0"},
- {host_name, ns_type::ns_t_aaaa, "2001:db8:cafe:d00d::31"}});
-
- ASSERT_TRUE(network.setDnsConfiguration());
- ASSERT_TRUE(network.startTunForwarder());
-
- auto result = android_getaddrinfofornet_wrapper(host_name, network.netId());
- ASSERT_RESULT_OK(result);
- ScopedAddrinfo ai_results(std::move(result.value()));
- std::vector<std::string> result_strs = ToStrings(ai_results);
- EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.ips));
- EXPECT_EQ(GetNumQueriesForType(*dnsPair->dnsServer, ns_type::ns_t_a, host_name), 1U);
- EXPECT_EQ(GetNumQueriesForType(*dnsPair->dnsServer, ns_type::ns_t_aaaa, host_name),
- config.numOfQuadAQuery);
- }
-}
-
// v6 mdns is expected to be sent when the IPv6 address is a link-local with a default route.
TEST_F(ResolverMultinetworkTest, MdnsIPv6LinkLocalWithDefaultRoute) {
// Kernel 4.4 does not provide an IPv6 link-local address when an interface is added to a
@@ -7973,7 +7928,7 @@ TEST_F(ResolverMultinetworkTest, UidAllowedNetworks) {
{ConnectivityType::V4V6, {ipv6_addr, ipv4_addr}},
};
for (const auto& [ipVersion, expectedDnsReply] : testPairs) {
- SCOPED_TRACE(fmt::format("ConnectivityType: {}", ipVersion));
+ SCOPED_TRACE(fmt::format("ConnectivityType: {}", static_cast<int>(ipVersion)));
// Create networks.
ScopedPhysicalNetwork sysDefaultNetwork =
diff --git a/tests/resolv_private_dns_test.cpp b/tests/resolv_private_dns_test.cpp
index 59a70ffa..cef39b40 100644
--- a/tests/resolv_private_dns_test.cpp
+++ b/tests/resolv_private_dns_test.cpp
@@ -29,6 +29,7 @@
#include <netdutils/InternetAddresses.h>
#include <netdutils/NetNativeTestBase.h>
#include <netdutils/Stopwatch.h>
+#include <nettestutils/DumpService.h>
#include "doh_frontend.h"
#include "tests/dns_responder/dns_responder.h"
@@ -52,6 +53,7 @@ using android::netdutils::ScopedAddrinfo;
using android::netdutils::Stopwatch;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;
+using ::testing::AnyOf;
constexpr int MAXPACKET = (8 * 1024);
@@ -60,33 +62,6 @@ constexpr int kDohIdleDefaultTimeoutMs = 55000;
namespace {
-std::vector<std::string> dumpService(ndk::SpAIBinder binder) {
- unique_fd localFd, remoteFd;
- bool success = Pipe(&localFd, &remoteFd);
- EXPECT_TRUE(success) << "Failed to open pipe for dumping: " << strerror(errno);
- if (!success) return {};
-
- // dump() blocks until another thread has consumed all its output.
- std::thread dumpThread = std::thread([binder, remoteFd{std::move(remoteFd)}]() {
- EXPECT_EQ(STATUS_OK, AIBinder_dump(binder.get(), remoteFd, nullptr, 0));
- });
-
- std::string dumpContent;
-
- EXPECT_TRUE(ReadFdToString(localFd.get(), &dumpContent))
- << "Error during dump: " << strerror(errno);
- dumpThread.join();
-
- std::stringstream dumpStream(std::move(dumpContent));
- std::vector<std::string> lines;
- std::string line;
- while (std::getline(dumpStream, line)) {
- lines.push_back(std::move(line));
- }
-
- return lines;
-}
-
int getAsyncResponse(int fd, int* rcode, uint8_t* buf, int bufLen) {
struct pollfd wait_fd[1];
wait_fd[0].fd = fd;
@@ -240,9 +215,13 @@ class BaseTest : public NetNativeTestBase {
}
bool expectLog(const std::string& ipAddrOrNoData, const std::string& port) {
- ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver"));
- assert(nullptr != resolvBinder.get());
- std::vector<std::string> lines = dumpService(resolvBinder);
+ std::vector<std::string> lines;
+ const android::status_t ret =
+ dumpService(sResolvBinder, /*args=*/nullptr, /*num_args=*/0, lines);
+ if (ret != android::OK) {
+ ADD_FAILURE() << "Error dumping service: " << android::statusToString(ret);
+ return false;
+ }
const std::string expectedLog =
port.empty() ? ipAddrOrNoData
@@ -344,6 +323,28 @@ class BasePrivateDnsTest : public BaseTest {
EXPECT_EQ(mDnsClient.resolvService()->dump(fd, querylogCmd, std::size(querylogCmd)), 0);
}
+ void expectQueriesAreBlocked() {
+ // getaddrinfo should fail
+ const addrinfo hints = {.ai_socktype = SOCK_DGRAM};
+ EXPECT_FALSE(safe_getaddrinfo(kQueryHostname, nullptr, &hints));
+
+ // gethostbyname should fail
+ EXPECT_FALSE(gethostbyname(kQueryHostname));
+
+ // gethostbyaddr should fail
+ in6_addr v6addr;
+ inet_pton(AF_INET6, "2001:db8::102:304", &v6addr);
+ EXPECT_FALSE(gethostbyaddr(&v6addr, sizeof(v6addr), AF_INET6));
+
+ // resNetworkQuery should fail
+ int fd = resNetworkQuery(TEST_NETID, kQueryHostname, ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd != -1);
+
+ uint8_t buf[MAXPACKET] = {};
+ int rcode;
+ EXPECT_EQ(-ECONNREFUSED, getAsyncResponse(fd, &rcode, buf, MAXPACKET));
+ }
+
static constexpr milliseconds kExpectedDohValidationTimeWhenTimeout{1000};
static constexpr milliseconds kExpectedDohValidationTimeWhenServerUnreachable{1000};
static constexpr char kQueryHostname[] = "TransportParameterizedTest.example.com.";
@@ -527,6 +528,123 @@ TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) {
}
}
+TEST_P(TransportParameterizedTest, BlockDnsQuery) {
+ SKIP_IF_BEFORE_T;
+ SKIP_IF_DEPENDENT_LIB_DOES_NOT_EXIST(DNS_HELPER);
+
+ constexpr char ptr_name[] = "v4v6.example.com.";
+ // PTR record for IPv6 address 2001:db8::102:304
+ constexpr char ptr_addr_v6[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.";
+ const DnsRecord r = {ptr_addr_v6, ns_type::ns_t_ptr, ptr_name};
+ dns.addMapping(r.host_name, r.type, r.addr);
+ dot_backend.addMapping(r.host_name, r.type, r.addr);
+ doh_backend.addMapping(r.host_name, r.type, r.addr);
+
+ auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr));
+ if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr));
+
+ // This waiting time is expected to avoid that the DoH validation event interferes other tests.
+ if (!testParamHasDoh()) waitForDohValidationFailed();
+
+ // Have the test independent of the number of sent queries in private DNS validation, because
+ // the DnsResolver can send either 1 or 2 queries in DoT validation.
+ if (testParamHasDoh()) {
+ doh.clearQueries();
+ }
+ if (testParamHasDot()) {
+ EXPECT_TRUE(dot.waitForQueries(1));
+ dot.clearQueries();
+ }
+ dns.clearQueries();
+
+ for (const bool testDataSaver : {false, true}) {
+ SCOPED_TRACE(fmt::format("test {}", testDataSaver ? "data saver" : "UID firewall rules"));
+ if (testDataSaver) {
+ // Data Saver applies on metered networks only.
+ parcel.meteredNetwork = true;
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ // Block network access by enabling data saver.
+ ScopedSetDataSaverByBPF scopedSetDataSaverByBPF(true);
+ ScopedChangeUID scopedChangeUID(TEST_UID);
+ expectQueriesAreBlocked();
+ } else {
+ // Block network access by setting UID firewall rules.
+ ScopeBlockedUIDRule scopeBlockUidRule(mDnsClient.netdService(), TEST_UID);
+ expectQueriesAreBlocked();
+ }
+ expectQueries(0 /* dns */, 0 /* dot */, 0 /* doh */);
+ }
+}
+
+// Verify whether the DNS fail-fast feature can be turned off by flag.
+TEST_P(TransportParameterizedTest, BlockDnsQuery_FlaggedOff) {
+ SKIP_IF_BEFORE_T;
+ SKIP_IF_DEPENDENT_LIB_DOES_NOT_EXIST(DNS_HELPER);
+
+ constexpr char ptr_name[] = "v4v6.example.com.";
+ // PTR record for IPv6 address 2001:db8::102:304
+ constexpr char ptr_addr_v6[] =
+ "4.0.3.0.2.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.";
+ const DnsRecord r = {ptr_addr_v6, ns_type::ns_t_ptr, ptr_name};
+ dns.addMapping(r.host_name, r.type, r.addr);
+ dot_backend.addMapping(r.host_name, r.type, r.addr);
+ doh_backend.addMapping(r.host_name, r.type, r.addr);
+
+ ScopedSystemProperties sp1(kFailFastOnUidNetworkBlockingFlag, "0");
+ resetNetwork();
+
+ auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr));
+ if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr));
+
+ // This waiting time is expected to avoid that the DoH validation event interferes other tests.
+ if (!testParamHasDoh()) waitForDohValidationFailed();
+
+ // Have the test independent of the number of sent queries in private DNS validation, because
+ // the DnsResolver can send either 1 or 2 queries in DoT validation.
+ if (testParamHasDoh()) {
+ doh.clearQueries();
+ }
+ if (testParamHasDot()) {
+ EXPECT_TRUE(dot.waitForQueries(1));
+ dot.clearQueries();
+ }
+ dns.clearQueries();
+
+ for (const bool testDataSaver : {false, true}) {
+ SCOPED_TRACE(fmt::format("test {}", testDataSaver ? "data saver" : "UID firewall rules"));
+ if (testDataSaver) {
+ // Data Saver applies on metered networks only.
+ parcel.meteredNetwork = true;
+ ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+ // Block network access by enabling data saver.
+ ScopedSetDataSaverByBPF scopedSetDataSaverByBPF(true);
+ ScopedChangeUID scopedChangeUID(TEST_UID);
+ EXPECT_NO_FAILURE(sendQueryAndCheckResult());
+ } else {
+ // Block network access by setting UID firewall rules.
+ ScopeBlockedUIDRule scopeBlockUidRule(mDnsClient.netdService(), TEST_UID);
+ EXPECT_NO_FAILURE(sendQueryAndCheckResult());
+ }
+
+ if (testParamHasDoh()) {
+ EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 0 /* dot */, 2 /* doh */));
+ dot.clearQueries();
+ } else {
+ EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 2 /* dot */, 0 /* doh */));
+ doh.clearQueries();
+ }
+ }
+}
+
class PrivateDnsDohTest : public BasePrivateDnsTest {
protected:
void SetUp() override {
@@ -804,9 +922,19 @@ TEST_F(PrivateDnsDohTest, TemporaryConnectionStalled) {
TEST_F(PrivateDnsDohTest, ExcessDnsRequests) {
const int total_queries = 70;
- // The number is from MAX_BUFFERED_COMMANDS + 2 (one that will be queued in
- // connection mpsc channel; the other one that will get blocked at dispatcher sending channel).
- const int timeout_queries = 52;
+ // In most cases, the number of timed-out DoH queries is MAX_BUFFERED_COMMANDS + 2 (one that
+ // will be queued in connection's mpsc::channel; the other one that will get blocked at
+ // dispatcher's mpsc::channel), as shown below:
+ //
+ // dispatcher's mpsc::channel -----> network's mpsc:channel -----> connection's mpsc::channel
+ // (expect 1 query queued here) (size: MAX_BUFFERED_COMMANDS) (expect 1 query queued here)
+ //
+ // However, it's still possible that the (MAX_BUFFERED_COMMANDS + 2)th query is sent to the DoH
+ // engine before the DoH engine moves a query to connection's mpsc::channel. In that case,
+ // the (MAX_BUFFERED_COMMANDS + 2)th query will be fallback'ed to DoT immediately rather than
+ // be waiting until DoH timeout, which result in only (MAX_BUFFERED_COMMANDS + 1) timed-out
+ // DoH queries.
+ const int doh_timeout_queries = 52;
// If early data flag is enabled, DnsResolver doesn't wait for the connection established.
// It will send DNS queries along with 0-RTT rather than queue them in connection mpsc channel.
@@ -829,7 +957,7 @@ TEST_F(PrivateDnsDohTest, ExcessDnsRequests) {
dns.clearQueries();
// Set the DoT server not to close the connection until it receives enough queries or timeout.
- dot.setDelayQueries(total_queries - timeout_queries);
+ dot.setDelayQueries(total_queries - doh_timeout_queries);
dot.setDelayQueriesTimeout(200);
// Set the server blocking, wait for the connection closed, and send some DNS requests.
@@ -847,8 +975,10 @@ TEST_F(PrivateDnsDohTest, ExcessDnsRequests) {
// There are some queries that fall back to DoT rather than UDP since the DoH client rejects
// any new DNS requests when the capacity is full.
- EXPECT_NO_FAILURE(expectQueries(timeout_queries /* dns */,
- total_queries - timeout_queries /* dot */, 0 /* doh */));
+ EXPECT_THAT(dns.queries().size(), AnyOf(doh_timeout_queries, doh_timeout_queries - 1));
+ EXPECT_THAT(dot.queries(), AnyOf(total_queries - doh_timeout_queries,
+ total_queries - doh_timeout_queries + 1));
+ EXPECT_EQ(doh.queries(), 0);
// Set up another network and send a DNS query. Expect that this network is unaffected.
constexpr int TEST_NETID_2 = 31;
diff --git a/tests/resolv_stats_test_utils.cpp b/tests/resolv_stats_test_utils.cpp
index 492080b8..1704922c 100644
--- a/tests/resolv_stats_test_utils.cpp
+++ b/tests/resolv_stats_test_utils.cpp
@@ -107,6 +107,8 @@ NetworkDnsEventReported fromNetworkDnsEventReportedStr(const std::string& str) {
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);
+ } else if (protoField[1] == "uid" && ParseInt(protoField[2], &value)) {
+ event.set_uid(value);
}
}
// Parsing each field of the proto DnsQueryEvent
@@ -169,6 +171,7 @@ void PrintTo(const NetworkDnsEventReported& event, std::ostream* os) {
*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 << " uid: " << event.uid() << "\n";
*os << "}";
}
diff --git a/tests/resolv_stats_test_utils.h b/tests/resolv_stats_test_utils.h
index 90fe511a..24e46858 100644
--- a/tests/resolv_stats_test_utils.h
+++ b/tests/resolv_stats_test_utils.h
@@ -120,7 +120,9 @@ MATCHER_P(NetworkDnsEventEq, other, "") {
*/
::testing::Property("dns_query_events",
&android::net::NetworkDnsEventReported::dns_query_events,
- DnsQueryEventsEq(other.dns_query_events()))),
+ DnsQueryEventsEq(other.dns_query_events())),
+ ::testing::Property("uid", &android::net::NetworkDnsEventReported::uid,
+ ::testing::Eq(other.uid()))),
arg, result_listener);
}
diff --git a/tests/resolv_stats_test_utils_test.cpp b/tests/resolv_stats_test_utils_test.cpp
index 3b30e086..af67796c 100644
--- a/tests/resolv_stats_test_utils_test.cpp
+++ b/tests/resolv_stats_test_utils_test.cpp
@@ -58,7 +58,8 @@ TEST_F(ResolvStatsUtilsTest, NetworkDnsEventEq) {
latency_micros: 0,
}
]
- }
+ },
+ uid: 1000,
})Event";
// TODO: Add integration test to verify Level 1 fields of NetworkDnsEventReported.
@@ -83,6 +84,7 @@ TEST_F(ResolvStatsUtilsTest, NetworkDnsEventEq) {
dnsQueryEvent2->set_dns_server_index(1);
dnsQueryEvent2->set_connected(0);
dnsQueryEvent2->set_latency_micros(5);
+ event1.set_uid(1000);
EXPECT_THAT(event1, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event2)));
}
diff --git a/tests/resolv_test_utils.cpp b/tests/resolv_test_utils.cpp
index 17c6c1db..4b09b213 100644
--- a/tests/resolv_test_utils.cpp
+++ b/tests/resolv_test_utils.cpp
@@ -229,3 +229,7 @@ void RemoveMdnsRoute() {
};
EXPECT_EQ(0, ForkAndRun(args_v6));
}
+
+bool is64bitAbi() {
+ return android::base::GetProperty("ro.product.cpu.abi", "").find("64") != std::string::npos;
+}
diff --git a/tests/resolv_test_utils.h b/tests/resolv_test_utils.h
index a94c6234..e3f744ce 100644
--- a/tests/resolv_test_utils.h
+++ b/tests/resolv_test_utils.h
@@ -20,6 +20,7 @@
#include <arpa/nameser.h>
#include <netdb.h>
+#include <filesystem>
#include <functional>
#include <string>
#include <vector>
@@ -32,6 +33,7 @@
#include <netdutils/InternetAddresses.h>
#include "dns_responder/dns_responder.h"
+#include "util.h"
class ScopeBlockedUIDRule {
using INetd = aidl::android::net::INetd;
@@ -80,6 +82,33 @@ class ScopeBlockedUIDRule {
const uid_t mSavedUid;
};
+// Supported from T+ only.
+class ScopedSetDataSaverByBPF {
+ public:
+ ScopedSetDataSaverByBPF(bool wanted) {
+ if (android::modules::sdklevel::IsAtLeastT()) {
+ mFw = Firewall::getInstance();
+ // Backup current setting.
+ const Result<bool> current = mFw->getDataSaverSetting();
+ EXPECT_RESULT_OK(current);
+ if (wanted != current.value()) {
+ mSavedDataSaverSetting = current;
+ EXPECT_RESULT_OK(mFw->setDataSaver(wanted));
+ }
+ }
+ };
+ ~ScopedSetDataSaverByBPF() {
+ // Restore the setting.
+ if (mSavedDataSaverSetting.has_value()) {
+ EXPECT_RESULT_OK(mFw->setDataSaver(mSavedDataSaverSetting.value()));
+ }
+ }
+
+ private:
+ Firewall* mFw;
+ Result<bool> mSavedDataSaverSetting;
+};
+
class ScopedChangeUID {
public:
ScopedChangeUID(uid_t testUid) : mTestUid(testUid), mSavedUid(getuid()) {
@@ -155,12 +184,12 @@ const std::string kDotXportUnusableThresholdFlag(kFlagPrefix + "dot_xport_unusab
const std::string kDotValidationLatencyFactorFlag(kFlagPrefix + "dot_validation_latency_factor");
const std::string kDotValidationLatencyOffsetMsFlag(kFlagPrefix +
"dot_validation_latency_offset_ms");
+const std::string kFailFastOnUidNetworkBlockingFlag(kFlagPrefix +
+ "fail_fast_on_uid_network_blocking");
const std::string kKeepListeningUdpFlag(kFlagPrefix + "keep_listening_udp");
const std::string kParallelLookupSleepTimeFlag(kFlagPrefix + "parallel_lookup_sleep_time");
const std::string kRetransIntervalFlag(kFlagPrefix + "retransmission_time_interval");
const std::string kRetryCountFlag(kFlagPrefix + "retry_count");
-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.");
@@ -402,3 +431,22 @@ android::netdutils::ScopedAddrinfo safe_getaddrinfo(const char* node, const char
void SetMdnsRoute();
void RemoveMdnsRoute();
+
+#define SKIP_IF_BEFORE_T \
+ do { \
+ if (!isAtLeastT()) { \
+ GTEST_SKIP() << "Skipping test because SDK version is less than T."; \
+ } \
+ } while (0)
+
+bool is64bitAbi();
+
+static const std::string DNS_HELPER =
+ is64bitAbi() ? "/apex/com.android.tethering/lib64/libcom.android.tethering.dns_helper.so"
+ : "/apex/com.android.tethering/lib/libcom.android.tethering.dns_helper.so";
+
+#define SKIP_IF_DEPENDENT_LIB_DOES_NOT_EXIST(libPath) \
+ do { \
+ if (!std::filesystem::exists(libPath)) \
+ GTEST_SKIP() << "Required " << (libPath) << " not found."; \
+ } while (0)
diff --git a/tests/resolv_unit_test.cpp b/tests/resolv_unit_test.cpp
index d7471b0b..af4cbf4e 100644
--- a/tests/resolv_unit_test.cpp
+++ b/tests/resolv_unit_test.cpp
@@ -638,7 +638,7 @@ TEST_F(ResolvGetAddrInfoTest, ServerResponseError) {
};
for (const auto& config : testConfigs) {
- SCOPED_TRACE(fmt::format("rcode: {}", config.rcode));
+ SCOPED_TRACE(fmt::format("rcode: {}", static_cast<int>(config.rcode)));
test::DNSResponder dns(config.rcode);
dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
@@ -1538,7 +1538,7 @@ TEST_F(GetHostByNameForNetContextTest, ServerResponseError) {
};
for (const auto& config : testConfigs) {
- SCOPED_TRACE(fmt::format("rcode: {}", config.rcode));
+ SCOPED_TRACE(fmt::format("rcode: {}", static_cast<int>(config.rcode)));
test::DNSResponder dns(config.rcode);
dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
diff --git a/tests/unsolicited_listener/unsolicited_event_listener.cpp b/tests/unsolicited_listener/unsolicited_event_listener.cpp
index 20edae13..b337496f 100644
--- a/tests/unsolicited_listener/unsolicited_event_listener.cpp
+++ b/tests/unsolicited_listener/unsolicited_event_listener.cpp
@@ -33,11 +33,11 @@ using android::base::ScopedLockAssertion;
using std::chrono::milliseconds;
constexpr milliseconds kEventTimeoutMs{5000};
-constexpr milliseconds kRetryIntervalMs{20};
::ndk::ScopedAStatus UnsolicitedEventListener::onDnsHealthEvent(const DnsHealthEventParcel& event) {
std::lock_guard lock(mMutex);
if (event.netId == mNetId) mDnsHealthResultRecords.push(event.healthResult);
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
@@ -51,6 +51,7 @@ constexpr milliseconds kRetryIntervalMs{20};
? event.prefixAddress
: "";
}
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
@@ -62,24 +63,16 @@ constexpr milliseconds kRetryIntervalMs{20};
mValidationRecords.insert_or_assign({event.netId, event.ipAddress, event.protocol},
event.validation);
}
- mCv.notify_one();
+ mCv.notify_all();
return ::ndk::ScopedAStatus::ok();
}
bool UnsolicitedEventListener::waitForPrivateDnsValidation(const std::string& serverAddr,
int validation, int protocol) {
- const auto now = std::chrono::steady_clock::now();
-
std::unique_lock lock(mMutex);
- ScopedLockAssertion assume_lock(mMutex);
-
- // onPrivateDnsValidationEvent() might already be invoked. Search for the record first.
- do {
- if (findAndRemoveValidationRecord({mNetId, serverAddr, protocol}, validation)) return true;
- } while (mCv.wait_until(lock, now + kEventTimeoutMs) != std::cv_status::timeout);
-
- // Timeout.
- return false;
+ return mCv.wait_for(lock, kEventTimeoutMs, [&]() REQUIRES(mMutex) {
+ return findAndRemoveValidationRecord({mNetId, serverAddr, protocol}, validation);
+ });
}
bool UnsolicitedEventListener::findAndRemoveValidationRecord(const ServerKey& key, int value) {
@@ -92,36 +85,33 @@ bool UnsolicitedEventListener::findAndRemoveValidationRecord(const ServerKey& ke
}
bool UnsolicitedEventListener::waitForNat64Prefix(int operation, const milliseconds& timeout) {
- android::base::Timer t;
- while (t.duration() < timeout) {
- {
- std::lock_guard lock(mMutex);
- if ((operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED &&
- !mNat64PrefixAddress.empty()) ||
- (operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_REMOVED &&
- mNat64PrefixAddress.empty())) {
- mUnexpectedNat64PrefixUpdates--;
- return true;
- }
- }
- std::this_thread::sleep_for(kRetryIntervalMs);
+ const auto now = std::chrono::steady_clock::now();
+
+ std::unique_lock lock(mMutex);
+ ScopedLockAssertion assume_lock(mMutex);
+
+ if (mCv.wait_for(lock, timeout, [&]() REQUIRES(mMutex) {
+ return (operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED &&
+ !mNat64PrefixAddress.empty()) ||
+ (operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_REMOVED &&
+ mNat64PrefixAddress.empty());
+ })) {
+ mUnexpectedNat64PrefixUpdates--;
+ return true;
}
+
+ // Timeout.
return false;
}
Result<int> UnsolicitedEventListener::popDnsHealthResult() {
- // Wait until the queue is not empty or timeout.
- android::base::Timer t;
- while (t.duration() < milliseconds{1000}) {
- {
- std::lock_guard lock(mMutex);
- if (!mDnsHealthResultRecords.empty()) break;
- }
- std::this_thread::sleep_for(kRetryIntervalMs);
- }
+ std::unique_lock lock(mMutex);
+ ScopedLockAssertion assume_lock(mMutex);
- std::lock_guard lock(mMutex);
- if (mDnsHealthResultRecords.empty()) return Error() << "Dns health result record is empty";
+ if (!mCv.wait_for(lock, kEventTimeoutMs,
+ [&]() REQUIRES(mMutex) { return !mDnsHealthResultRecords.empty(); })) {
+ return Error() << "Dns health result record is empty";
+ }
auto ret = mDnsHealthResultRecords.front();
mDnsHealthResultRecords.pop();