diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Android.bp | 9 | ||||
-rw-r--r-- | tests/dns_metrics_listener/dns_metrics_listener.cpp | 67 | ||||
-rw-r--r-- | tests/dns_metrics_listener/dns_metrics_listener.h | 9 | ||||
-rw-r--r-- | tests/dns_responder/dns_responder.cpp | 3 | ||||
-rw-r--r-- | tests/dns_responder/dns_responder_client_ndk.cpp | 18 | ||||
-rw-r--r-- | tests/dns_responder/dns_responder_client_ndk.h | 14 | ||||
-rw-r--r-- | tests/dnsresolver_binder_test.cpp | 119 | ||||
-rw-r--r-- | tests/doh_ffi_test.cpp | 2 | ||||
-rw-r--r-- | tests/resolv_cache_unit_test.cpp | 69 | ||||
-rw-r--r-- | tests/resolv_integration_test.cpp | 185 | ||||
-rw-r--r-- | tests/resolv_private_dns_test.cpp | 202 | ||||
-rw-r--r-- | tests/resolv_stats_test_utils.cpp | 3 | ||||
-rw-r--r-- | tests/resolv_stats_test_utils.h | 4 | ||||
-rw-r--r-- | tests/resolv_stats_test_utils_test.cpp | 4 | ||||
-rw-r--r-- | tests/resolv_test_utils.cpp | 4 | ||||
-rw-r--r-- | tests/resolv_test_utils.h | 52 | ||||
-rw-r--r-- | tests/resolv_unit_test.cpp | 4 | ||||
-rw-r--r-- | tests/unsolicited_listener/unsolicited_event_listener.cpp | 64 |
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(); |