Merge PrivateDnsConfiguration and qhook into libnetd_resolv
PrivateDnsConfiguration handles the operations which adds, validates,
and removes private DNS servers. It will be hidden inside the library
after this change.
PrivateDnsConfiguration and DnsTlsDispatcher will be constructed along
with netd starts. Their instances are now moved to the library. Netd
can use public functions to update private DNS servers. In addition,
qhook() is no longer needed for TLS query.
This change comprises:
[1] Provide APIs for netd to add and delete private DNS servers as
well as get status.
[2] Provide a way for netd to register callback which will be invoked
whenever private DNS servers validation finishes. This is used for
onPrivateDnsValidationEvent().
[3] Remove qhook in android_net_context, since DnsTls* have been moved
to libnetd_resolv library. Also, qhook and rhook are removed in the
library.
[4] The visibility of DnsTls* symbols are hidden, while they have been
visible for a while.
Bug: 113628807
Test: as follows
- built, flashed, booted
- system/netd/tests/runtests.sh
- DNS-over-TLS in live network passed
Change-Id: I235004e4019d88d0d162d7ebd452148cd14cfd39
diff --git a/server/ResolverController.cpp b/server/ResolverController.cpp
index a299120..0b326b0 100644
--- a/server/ResolverController.cpp
+++ b/server/ResolverController.cpp
@@ -38,6 +38,7 @@
#include <android/net/INetd.h>
#include <android/net/metrics/INetdEventListener.h>
+#include "Controllers.h"
#include "DumpWriter.h"
#include "EventReporter.h"
#include "Fwmark.h"
@@ -45,18 +46,13 @@
#include "Permission.h"
#include "ResolverController.h"
#include "ResolverStats.h"
-#include "netd_resolv/DnsTlsServer.h"
-#include "netd_resolv/DnsTlsTransport.h"
#include "netd_resolv/params.h"
#include "netd_resolv/resolv.h"
#include "netd_resolv/stats.h"
-#include "netdutils/BackoffSequence.h"
namespace android {
-
-using netdutils::BackoffSequence;
-
namespace net {
+
namespace {
std::string addrToString(const sockaddr_storage* addr) {
@@ -66,324 +62,50 @@
return std::string(out);
}
-bool parseServer(const char* server, sockaddr_storage* parsed) {
- addrinfo hints = {
- .ai_family = AF_UNSPEC,
- .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV
- };
- addrinfo* res;
-
- int err = getaddrinfo(server, "853", &hints, &res);
- if (err != 0) {
- ALOGW("Failed to parse server address (%s): %s", server, gai_strerror(err));
- return false;
- }
-
- memcpy(parsed, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
- return true;
-}
-
const char* getPrivateDnsModeString(PrivateDnsMode mode) {
switch (mode) {
- case PrivateDnsMode::OFF: return "OFF";
- case PrivateDnsMode::OPPORTUNISTIC: return "OPPORTUNISTIC";
- case PrivateDnsMode::STRICT: return "STRICT";
+ case PrivateDnsMode::OFF:
+ return "OFF";
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return "OPPORTUNISTIC";
+ case PrivateDnsMode::STRICT:
+ return "STRICT";
}
}
-constexpr const char* validationStatusToString(ResolverController::Validation value) {
+constexpr const char* validationStatusToString(Validation value) {
switch (value) {
- case ResolverController::Validation::in_process: return "in_process";
- case ResolverController::Validation::success: return "success";
- case ResolverController::Validation::fail: return "fail";
- case ResolverController::Validation::unknown_server: return "unknown_server";
- case ResolverController::Validation::unknown_netid: return "unknown_netid";
- default: return "unknown_status";
+ case Validation::in_process:
+ return "in_process";
+ case Validation::success:
+ return "success";
+ case Validation::fail:
+ return "fail";
+ case Validation::unknown_server:
+ return "unknown_server";
+ case Validation::unknown_netid:
+ return "unknown_netid";
+ default:
+ return "unknown_status";
}
}
-
-class PrivateDnsConfiguration {
- public:
- typedef ResolverController::PrivateDnsStatus PrivateDnsStatus;
- typedef ResolverController::Validation Validation;
- typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
-
- int set(uint32_t netId, uint32_t mark, const std::vector<std::string>& servers,
- const std::string& name, const std::set<std::vector<uint8_t>>& fingerprints) {
+void onPrivateDnsValidation(unsigned netId, const char* server, const char* hostname,
+ bool success) {
+ // Send a validation event to NetdEventListenerService.
+ const auto netdEventListener = net::gCtls->eventReporter.getNetdEventListener();
+ if (netdEventListener != nullptr) {
+ netdEventListener->onPrivateDnsValidationEvent(netId, android::String16(server),
+ android::String16(hostname), success);
if (DBG) {
- ALOGD("PrivateDnsConfiguration::set(%u, 0x%x, %zu, %s, %zu)", netId, mark,
- servers.size(), name.c_str(), fingerprints.size());
+ ALOGD("Sending validation %s event on netId %u for %s with hostname %s",
+ success ? "success" : "failure", netId, server, hostname);
}
- const bool explicitlyConfigured = !name.empty() || !fingerprints.empty();
-
- // Parse the list of servers that has been passed in
- std::set<DnsTlsServer> tlsServers;
- for (size_t i = 0; i < servers.size(); ++i) {
- sockaddr_storage parsed;
- if (!parseServer(servers[i].c_str(), &parsed)) {
- return -EINVAL;
- }
- DnsTlsServer server(parsed);
- server.name = name;
- server.fingerprints = fingerprints;
- tlsServers.insert(server);
- }
-
- std::lock_guard guard(mPrivateDnsLock);
- if (explicitlyConfigured) {
- mPrivateDnsModes[netId] = PrivateDnsMode::STRICT;
- } else if (!tlsServers.empty()) {
- mPrivateDnsModes[netId] = PrivateDnsMode::OPPORTUNISTIC;
- } else {
- mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
- mPrivateDnsTransports.erase(netId);
- return 0;
- }
-
- // Create the tracker if it was not present
- auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- // No TLS tracker yet for this netId.
- bool added;
- std::tie(netPair, added) = mPrivateDnsTransports.emplace(netId, PrivateDnsTracker());
- if (!added) {
- ALOGE("Memory error while recording private DNS for netId %d", netId);
- return -ENOMEM;
- }
- }
- auto& tracker = netPair->second;
-
- // Remove any servers from the tracker that are not in |servers| exactly.
- for (auto it = tracker.begin(); it != tracker.end();) {
- if (tlsServers.count(it->first) == 0) {
- it = tracker.erase(it);
- } else {
- ++it;
- }
- }
-
- // Add any new or changed servers to the tracker, and initiate async checks for them.
- for (const auto& server : tlsServers) {
- if (needsValidation(tracker, server)) {
- validatePrivateDnsProvider(server, tracker, netId, mark);
- }
- }
- return 0;
+ } else {
+ ALOGE("Validation event not sent since NetdEventListenerService is unavailable.");
}
-
- PrivateDnsStatus getStatus(unsigned netId) {
- PrivateDnsStatus status{PrivateDnsMode::OFF, {}};
-
- // This mutex is on the critical path of every DNS lookup.
- //
- // If the overhead of mutex acquisition proves too high, we could reduce
- // it by maintaining an atomic_int32_t counter of TLS-enabled netids, or
- // by using an RWLock.
- std::lock_guard guard(mPrivateDnsLock);
-
- const auto mode = mPrivateDnsModes.find(netId);
- if (mode == mPrivateDnsModes.end()) return status;
- status.mode = mode->second;
-
- const auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair != mPrivateDnsTransports.end()) {
- for (const auto& serverPair : netPair->second) {
- if (serverPair.second == Validation::success) {
- status.validatedServers.push_back(serverPair.first);
- }
- }
- }
-
- return status;
- }
-
- void clear(unsigned netId) {
- if (DBG) {
- ALOGD("PrivateDnsConfiguration::clear(%u)", netId);
- }
- std::lock_guard guard(mPrivateDnsLock);
- mPrivateDnsModes.erase(netId);
- mPrivateDnsTransports.erase(netId);
- }
-
- void dump(DumpWriter& dw, unsigned netId) {
- std::lock_guard guard(mPrivateDnsLock);
-
- const auto& mode = mPrivateDnsModes.find(netId);
- dw.println("Private DNS mode: %s", getPrivateDnsModeString(
- (mode != mPrivateDnsModes.end()) ? mode->second : PrivateDnsMode::OFF));
- const auto& netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- dw.println("No Private DNS servers configured");
- } else {
- const auto& tracker = netPair->second;
- dw.println("Private DNS configuration (%zu entries)", tracker.size());
- dw.incIndent();
- for (const auto& kv : tracker) {
- const auto& server = kv.first;
- const auto status = kv.second;
- dw.println("%s name{%s} status{%s}",
- addrToString(&(server.ss)).c_str(),
- server.name.c_str(),
- validationStatusToString(status));
- }
- dw.decIndent();
- }
- }
-
- private:
- void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
- unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock) {
- if (DBG) {
- ALOGD("validatePrivateDnsProvider(%s, %u)", addrToString(&(server.ss)).c_str(), netId);
- }
-
- tracker[server] = Validation::in_process;
- if (DBG) {
- ALOGD("Server %s marked as in_process. Tracker now has size %zu",
- addrToString(&(server.ss)).c_str(), tracker.size());
- }
- // Note that capturing |server| and |netId| in this lambda create copies.
- std::thread validate_thread([this, server, netId, mark] {
- // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
- //
- // Start with a 1 minute delay and backoff to once per hour.
- //
- // Assumptions:
- // [1] Each TLS validation is ~10KB of certs+handshake+payload.
- // [2] Network typically provision clients with <=4 nameservers.
- // [3] Average month has 30 days.
- //
- // Each validation pass in a given hour is ~1.2MB of data. And 24
- // such validation passes per day is about ~30MB per month, in the
- // worst case. Otherwise, this will cost ~600 SYNs per month
- // (6 SYNs per ip, 4 ips per validation pass, 24 passes per day).
- auto backoff = BackoffSequence<>::Builder()
- .withInitialRetransmissionTime(std::chrono::seconds(60))
- .withMaximumRetransmissionTime(std::chrono::seconds(3600))
- .build();
-
- while (true) {
- // ::validate() is a blocking call that performs network operations.
- // It can take milliseconds to minutes, up to the SYN retry limit.
- const bool success = DnsTlsTransport::validate(server, netId, mark);
- if (DBG) {
- ALOGD("validateDnsTlsServer returned %d for %s", success,
- addrToString(&(server.ss)).c_str());
- }
-
- const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success);
- if (!needs_reeval) {
- break;
- }
-
- if (backoff.hasNextTimeout()) {
- std::this_thread::sleep_for(backoff.getNextTimeout());
- } else {
- break;
- }
- }
- });
- validate_thread.detach();
- }
-
- bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success) {
- constexpr bool NEEDS_REEVALUATION = true;
- constexpr bool DONT_REEVALUATE = false;
-
- std::lock_guard guard(mPrivateDnsLock);
-
- auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- ALOGW("netId %u was erased during private DNS validation", netId);
- return DONT_REEVALUATE;
- }
-
- const auto mode = mPrivateDnsModes.find(netId);
- if (mode == mPrivateDnsModes.end()) {
- ALOGW("netId %u has no private DNS validation mode", netId);
- return DONT_REEVALUATE;
- }
- const bool modeDoesReevaluation = (mode->second == PrivateDnsMode::STRICT);
-
- bool reevaluationStatus =
- (success || !modeDoesReevaluation) ? DONT_REEVALUATE : NEEDS_REEVALUATION;
-
- auto& tracker = netPair->second;
- auto serverPair = tracker.find(server);
- if (serverPair == tracker.end()) {
- ALOGW("Server %s was removed during private DNS validation",
- addrToString(&(server.ss)).c_str());
- success = false;
- reevaluationStatus = DONT_REEVALUATE;
- } else if (!(serverPair->first == server)) {
- // TODO: It doesn't seem correct to overwrite the tracker entry for
- // |server| down below in this circumstance... Fix this.
- ALOGW("Server %s was changed during private DNS validation",
- addrToString(&(server.ss)).c_str());
- success = false;
- reevaluationStatus = DONT_REEVALUATE;
- }
-
- // Send a validation event to NetdEventListenerService.
- if (mNetdEventListener == nullptr) {
- mNetdEventListener = mEventReporter.getNetdEventListener();
- }
- if (mNetdEventListener != nullptr) {
- const String16 ipLiteral(addrToString(&(server.ss)).c_str());
- const String16 hostname(server.name.empty() ? "" : server.name.c_str());
- mNetdEventListener->onPrivateDnsValidationEvent(netId, ipLiteral, hostname, success);
- if (DBG) {
- ALOGD("Sending validation %s event on netId %u for %s with hostname %s",
- success ? "success" : "failure", netId,
- addrToString(&(server.ss)).c_str(), server.name.c_str());
- }
- } else {
- ALOGE("Validation event not sent since NetdEventListenerService is unavailable.");
- }
-
- if (success) {
- tracker[server] = Validation::success;
- if (DBG) {
- ALOGD("Validation succeeded for %s! Tracker now has %zu entries.",
- addrToString(&(server.ss)).c_str(), tracker.size());
- }
- } else {
- // Validation failure is expected if a user is on a captive portal.
- // TODO: Trigger a second validation attempt after captive portal login
- // succeeds.
- tracker[server] = (reevaluationStatus == NEEDS_REEVALUATION) ? Validation::in_process
- : Validation::fail;
- if (DBG) {
- ALOGD("Validation failed for %s!", addrToString(&(server.ss)).c_str());
- }
- }
-
- return reevaluationStatus;
- }
-
- // Start validation for newly added servers as well as any servers that have
- // landed in Validation::fail state. Note that servers that have failed
- // multiple validation attempts but for which there is still a validating
- // thread running are marked as being Validation::in_process.
- static bool needsValidation(const PrivateDnsTracker& tracker, const DnsTlsServer& server) {
- const auto& iter = tracker.find(server);
- return (iter == tracker.end()) || (iter->second == Validation::fail);
- }
-
- EventReporter mEventReporter;
-
- std::mutex mPrivateDnsLock;
- std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock);
- // Structure for tracking the validation status of servers on a specific netId.
- // Using the AddressComparator ensures at most one entry per IP address.
- std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
- android::sp<android::net::metrics::INetdEventListener>
- mNetdEventListener GUARDED_BY(mPrivateDnsLock);
-} sPrivateDnsConfiguration;
+}
bool allIPv6Only(const std::vector<std::string>& servers) {
for (const auto& server : servers) {
@@ -402,18 +124,13 @@
return -resolv_set_nameservers_for_net(netId, servers, numservers, searchDomains, params);
}
-ResolverController::PrivateDnsStatus
-ResolverController::getPrivateDnsStatus(unsigned netId) const {
- return sPrivateDnsConfiguration.getStatus(netId);
-}
-
int ResolverController::clearDnsServers(unsigned netId) {
resolv_set_nameservers_for_net(netId, nullptr, 0, "", nullptr);
if (DBG) {
ALOGD("clearDnsServers netId = %u\n", netId);
}
mDns64Configuration.stopPrefixDiscovery(netId);
- sPrivateDnsConfiguration.clear(netId);
+ resolv_delete_private_dns_for_net(netId);
return 0;
}
@@ -512,6 +229,20 @@
return -EINVAL;
}
+ std::vector<const char*> server_ptrs;
+ size_t count = std::min<size_t>(MAXNS, tlsServers.size());
+ server_ptrs.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ server_ptrs.push_back(tlsServers[i].data());
+ }
+
+ std::vector<const uint8_t*> fingerprint_ptrs;
+ count = tlsFingerprints.size();
+ fingerprint_ptrs.reserve(count);
+ for (const auto& fp : tlsFingerprints) {
+ fingerprint_ptrs.push_back(fp.data());
+ }
+
// At private DNS validation time, we only know the netId, so we have to guess/compute the
// corresponding socket mark.
Fwmark fwmark;
@@ -520,16 +251,19 @@
fwmark.protectedFromVpn = true;
fwmark.permission = PERMISSION_SYSTEM;
- const int err = sPrivateDnsConfiguration.set(netId, fwmark.intValue, tlsServers, tlsName,
- tlsFingerprints);
+ const int err = resolv_set_private_dns_for_net(
+ netId, fwmark.intValue, server_ptrs.data(), server_ptrs.size(), tlsName.c_str(),
+ fingerprint_ptrs.data(), fingerprint_ptrs.size());
if (err != 0) {
return err;
}
+ resolv_register_private_dns_callback(&onPrivateDnsValidation);
// Convert network-assigned server list to bionic's format.
- auto server_count = std::min<size_t>(MAXNS, servers.size());
- std::vector<const char*> server_ptrs;
- for (size_t i = 0 ; i < server_count ; ++i) {
+ server_ptrs.clear();
+ count = std::min<size_t>(MAXNS, servers.size());
+ server_ptrs.reserve(count);
+ for (size_t i = 0; i < count; ++i) {
server_ptrs.push_back(servers[i].c_str());
}
@@ -653,7 +387,24 @@
}
mDns64Configuration.dump(dw, netId);
- sPrivateDnsConfiguration.dump(dw, netId);
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ resolv_get_private_dns_status_for_net(netId, &privateDnsStatus);
+ dw.println("Private DNS mode: %s",
+ getPrivateDnsModeString(static_cast<PrivateDnsMode>(privateDnsStatus.mode)));
+ if (!privateDnsStatus.numServers) {
+ dw.println("No Private DNS servers configured");
+ } else {
+ dw.println("Private DNS configuration (%u entries)", privateDnsStatus.numServers);
+ dw.incIndent();
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ dw.println("%s name{%s} status{%s}",
+ addrToString(&(privateDnsStatus.serverStatus[i].ss)).c_str(),
+ privateDnsStatus.serverStatus[i].hostname,
+ validationStatusToString(static_cast<Validation>(
+ privateDnsStatus.serverStatus[i].validation)));
+ }
+ dw.decIndent();
+ }
}
dw.decIndent();
}