[DRUEL03] Implement unsolicited private DNS validation event
Implement unsolicited private DNS validation event that will
report to the listener when process a private DNS validation
and get result.
Bug: 173485754
Test: atest resolv_integration_test resolv_unit_test\
resolv_stress_test resolv_stats_test_utils_test
Change-Id: I2b90f4e5ad298e06efbb07c8bcc56808df46fdd0
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 8f3b836..c8b070a 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -30,6 +30,8 @@
#include "netdutils/BackoffSequence.h"
#include "util.h"
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
using android::base::StringPrintf;
using android::netdutils::setThreadName;
using std::chrono::milliseconds;
@@ -222,6 +224,35 @@
validate_thread.detach();
}
+void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const DnsTlsServer& server,
+ unsigned netId, bool success) {
+ LOG(DEBUG) << "Sending validation " << (success ? "success" : "failure") << " event on netId "
+ << netId << " for " << server.toIpString() << " with hostname {" << server.name
+ << "}";
+ // Send a validation event to NetdEventListenerService.
+ const auto& listeners = ResolverEventReporter::getInstance().getListeners();
+ if (listeners.empty()) {
+ LOG(ERROR)
+ << "Validation event not sent since no INetdEventListener receiver is available.";
+ }
+ for (const auto& it : listeners) {
+ it->onPrivateDnsValidationEvent(netId, server.toIpString(), server.name, success);
+ }
+
+ // Send a validation event to unsolicited event listeners.
+ const auto& unsolEventListeners = ResolverEventReporter::getInstance().getUnsolEventListeners();
+ const PrivateDnsValidationEventParcel validationEvent = {
+ .netId = static_cast<int32_t>(netId),
+ .ipAddress = server.toIpString(),
+ .hostname = server.name,
+ .validation = success ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS
+ : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE,
+ };
+ for (const auto& it : unsolEventListeners) {
+ it->onPrivateDnsValidationEvent(validationEvent);
+ }
+}
+
bool PrivateDnsConfiguration::recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId,
bool success) {
constexpr bool NEEDS_REEVALUATION = true;
@@ -268,19 +299,8 @@
reevaluationStatus = DONT_REEVALUATE;
}
- // Send a validation event to NetdEventListenerService.
- const auto& listeners = ResolverEventReporter::getInstance().getListeners();
- if (listeners.size() != 0) {
- for (const auto& it : listeners) {
- it->onPrivateDnsValidationEvent(netId, server.toIpString(), server.name, success);
- }
- LOG(DEBUG) << "Sent validation " << (success ? "success" : "failure") << " event on netId "
- << netId << " for " << server.toIpString() << " with hostname {" << server.name
- << "}";
- } else {
- LOG(ERROR)
- << "Validation event not sent since no INetdEventListener receiver is available.";
- }
+ // Send private dns validation result to listeners.
+ sendPrivateDnsValidationEvent(server, netId, success);
if (success) {
updateServerState(identity, Validation::success, netId);
diff --git a/PrivateDnsConfiguration.h b/PrivateDnsConfiguration.h
index 07c6f2e..04f6a47 100644
--- a/PrivateDnsConfiguration.h
+++ b/PrivateDnsConfiguration.h
@@ -103,6 +103,9 @@
bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success)
EXCLUDES(mPrivateDnsLock);
+ void sendPrivateDnsValidationEvent(const DnsTlsServer& server, unsigned netId, bool success)
+ REQUIRES(mPrivateDnsLock);
+
// Decide if a validation for |server| is needed. Note that servers that have failed
// multiple validation attempts but for which there is still a validating
// thread running are marked as being Validation::in_process.
diff --git a/tests/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
index 50c7151..19d0a0c 100644
--- a/tests/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -56,6 +56,7 @@
using android::base::unique_fd;
using android::net::ResolverStats;
using android::net::metrics::TestOnDnsEvent;
+using android::net::resolv::aidl::UnsolicitedEventListener;
using android::netdutils::Stopwatch;
// TODO: make this dynamic and stop depending on implementation details.
@@ -302,17 +303,16 @@
}
TEST_F(DnsResolverBinderTest, RegisterUnsolicitedEventListener_DuplicateSubscription) {
- class FakeListener : public android::net::resolv::aidl::UnsolicitedEventListener {};
-
// Expect to subscribe successfully.
- std::shared_ptr<FakeListener> fakeListener = ndk::SharedRefBase::make<FakeListener>();
- ::ndk::ScopedAStatus status = mDnsResolver->registerUnsolicitedEventListener(fakeListener);
+ std::shared_ptr<UnsolicitedEventListener> listener =
+ ndk::SharedRefBase::make<UnsolicitedEventListener>(TEST_NETID);
+ ::ndk::ScopedAStatus status = mDnsResolver->registerUnsolicitedEventListener(listener);
ASSERT_TRUE(status.isOk()) << status.getMessage();
mExpectedLogData.push_back(
{"registerUnsolicitedEventListener()", "registerUnsolicitedEventListener.*"});
// Expect to subscribe failed with registered listener instance.
- status = mDnsResolver->registerUnsolicitedEventListener(fakeListener);
+ status = mDnsResolver->registerUnsolicitedEventListener(listener);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(EEXIST, status.getServiceSpecificError());
mExpectedLogData.push_back(
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index a31de04..e83fe63 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -72,6 +72,7 @@
#include "tests/dns_responder/dns_tls_frontend.h"
#include "tests/resolv_test_utils.h"
#include "tests/tun_forwarder.h"
+#include "tests/unsolicited_listener/unsolicited_event_listener.h"
// Valid VPN netId range is 100 ~ 65535
constexpr int TEST_VPN_NETID = 65502;
@@ -95,6 +96,8 @@
using aidl::android::net::INetd;
using aidl::android::net::ResolverParamsParcel;
using aidl::android::net::metrics::INetdEventListener;
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
using android::base::Error;
using android::base::ParseInt;
using android::base::Result;
@@ -103,6 +106,7 @@
using android::net::ResolverStats;
using android::net::TunForwarder;
using android::net::metrics::DnsMetricsListener;
+using android::net::resolv::aidl::UnsolicitedEventListener;
using android::netdutils::enableSockopt;
using android::netdutils::makeSlice;
using android::netdutils::ResponseCode;
@@ -205,6 +209,12 @@
TEST_NETID /*monitor specific network*/);
ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
+ // Subscribe the unsolicited event listener for verifying unsolicited event contents.
+ sUnsolicitedEventListener = ndk::SharedRefBase::make<UnsolicitedEventListener>(
+ TEST_NETID /*monitor specific network*/);
+ ASSERT_TRUE(
+ resolvService->registerUnsolicitedEventListener(sUnsolicitedEventListener).isOk());
+
// Start the binder thread pool for listening DNS metrics events and receiving death
// recipient.
ABinderProcess_startThreadPool();
@@ -215,6 +225,7 @@
void SetUp() {
mDnsClient.SetUp();
sDnsMetricsListener->reset();
+ sUnsolicitedEventListener->reset();
}
void TearDown() {
@@ -252,11 +263,16 @@
}
bool WaitForPrivateDnsValidation(std::string serverAddr, bool validated) {
- return sDnsMetricsListener->waitForPrivateDnsValidation(serverAddr, validated);
+ return sDnsMetricsListener->waitForPrivateDnsValidation(serverAddr, validated) &&
+ sUnsolicitedEventListener->waitForPrivateDnsValidation(
+ serverAddr,
+ validated ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS
+ : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE);
}
bool hasUncaughtPrivateDnsValidation(const std::string& serverAddr) {
- return sDnsMetricsListener->findValidationRecord(serverAddr);
+ return sDnsMetricsListener->findValidationRecord(serverAddr) &&
+ sUnsolicitedEventListener->findValidationRecord(serverAddr);
}
void ExpectDnsEvent(int32_t eventType, int32_t returnCode, const std::string& hostname,
@@ -368,6 +384,9 @@
static std::shared_ptr<DnsMetricsListener>
sDnsMetricsListener; // Initialized in SetUpTestSuite.
+ inline static std::shared_ptr<UnsolicitedEventListener>
+ sUnsolicitedEventListener; // Initialized in SetUpTestSuite.
+
// Use a shared static death recipient to monitor the service death. The static death
// recipient could monitor the death not only during the test but also between tests.
static AIBinder_DeathRecipient* sResolvDeathRecipient; // Initialized in SetUpTestSuite.
diff --git a/tests/unsolicited_listener/unsolicited_event_listener.cpp b/tests/unsolicited_listener/unsolicited_event_listener.cpp
index 5200311..f5ebb0b 100644
--- a/tests/unsolicited_listener/unsolicited_event_listener.cpp
+++ b/tests/unsolicited_listener/unsolicited_event_listener.cpp
@@ -16,24 +16,65 @@
#include "unsolicited_event_listener.h"
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/format.h>
+
namespace android::net::resolv::aidl {
+using ::aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
+using android::base::ScopedLockAssertion;
+using std::chrono::milliseconds;
+
+constexpr milliseconds kEventTimeoutMs{5000};
+
::ndk::ScopedAStatus UnsolicitedEventListener::onDnsHealthEvent(
const ::aidl::android::net::resolv::aidl::DnsHealthEventParcel&) {
// default no-op
return ::ndk::ScopedAStatus::ok();
-};
+}
::ndk::ScopedAStatus UnsolicitedEventListener::onNat64PrefixEvent(
const ::aidl::android::net::resolv::aidl::Nat64PrefixEventParcel&) {
// default no-op
return ::ndk::ScopedAStatus::ok();
-};
+}
::ndk::ScopedAStatus UnsolicitedEventListener::onPrivateDnsValidationEvent(
- const ::aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel&) {
- // default no-op
+ const PrivateDnsValidationEventParcel& event) {
+ {
+ std::lock_guard lock(mMutex);
+ // keep updating the server to have latest validation status.
+ mValidationRecords.insert_or_assign({event.netId, event.ipAddress}, event.validation);
+ }
+ mCv.notify_one();
return ::ndk::ScopedAStatus::ok();
-};
+}
+
+bool UnsolicitedEventListener::waitForPrivateDnsValidation(const std::string& serverAddr,
+ int validation) {
+ 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}, validation)) return true;
+ } while (mCv.wait_until(lock, now + kEventTimeoutMs) != std::cv_status::timeout);
+
+ // Timeout.
+ return false;
+}
+
+bool UnsolicitedEventListener::findAndRemoveValidationRecord(const ServerKey& key, int value) {
+ auto it = mValidationRecords.find(key);
+ if (it != mValidationRecords.end() && it->second == value) {
+ mValidationRecords.erase(it);
+ return true;
+ }
+ return false;
+}
} // namespace android::net::resolv::aidl
diff --git a/tests/unsolicited_listener/unsolicited_event_listener.h b/tests/unsolicited_listener/unsolicited_event_listener.h
index 170f183..5615f24 100644
--- a/tests/unsolicited_listener/unsolicited_event_listener.h
+++ b/tests/unsolicited_listener/unsolicited_event_listener.h
@@ -16,24 +16,60 @@
#pragma once
+#include <condition_variable>
+#include <map>
+#include <queue>
#include <string>
+#include <utility>
#include <aidl/android/net/resolv/aidl/BnDnsResolverUnsolicitedEventListener.h>
+#include <android-base/thread_annotations.h>
namespace android::net::resolv::aidl {
class UnsolicitedEventListener
: public ::aidl::android::net::resolv::aidl::BnDnsResolverUnsolicitedEventListener {
public:
- UnsolicitedEventListener() = default;
+ UnsolicitedEventListener() = delete;
+ UnsolicitedEventListener(int32_t netId) : mNetId(netId){};
~UnsolicitedEventListener() = default;
- virtual ::ndk::ScopedAStatus onDnsHealthEvent(
+ ::ndk::ScopedAStatus onDnsHealthEvent(
const ::aidl::android::net::resolv::aidl::DnsHealthEventParcel&) override;
- virtual ::ndk::ScopedAStatus onNat64PrefixEvent(
+ ::ndk::ScopedAStatus onNat64PrefixEvent(
const ::aidl::android::net::resolv::aidl::Nat64PrefixEventParcel&) override;
- virtual ::ndk::ScopedAStatus onPrivateDnsValidationEvent(
+ ::ndk::ScopedAStatus onPrivateDnsValidationEvent(
const ::aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel&) override;
+
+ // Wait for the expected private DNS validation result until timeout.
+ bool waitForPrivateDnsValidation(const std::string& serverAddr, int validation);
+
+ // Return true if a validation result for |serverAddr| is found; otherwise, return false.
+ bool findValidationRecord(const std::string& serverAddr) const EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ return mValidationRecords.find({mNetId, serverAddr}) != mValidationRecords.end();
+ }
+
+ void reset() EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ mValidationRecords.clear();
+ }
+
+ private:
+ typedef std::pair<int32_t, std::string> ServerKey;
+
+ // Search mValidationRecords. Return true if |key| exists and its value is equal to
+ // |value|, and then remove it; otherwise, return false.
+ bool findAndRemoveValidationRecord(const ServerKey& key, int value) REQUIRES(mMutex);
+
+ // Monitor the event which was fired on specific network id.
+ const int32_t mNetId;
+
+ // Used to store the data from onPrivateDnsValidationEvent.
+ std::map<ServerKey, int> mValidationRecords GUARDED_BY(mMutex);
+
+ mutable std::mutex mMutex;
+ std::condition_variable mCv;
};
} // namespace android::net::resolv::aidl