Loading Android.bp +7 −3 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ cc_library { "libbase", "libcutils", "libnetdutils", "libdoh_ffi", "libprotobuf-cpp-lite", "libstatslog_resolv", "libstatspush_compat", Loading Loading @@ -331,7 +332,8 @@ rust_ffi_static { rlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche", Loading Loading @@ -366,7 +368,8 @@ rust_test { rustlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", Loading @@ -387,7 +390,8 @@ rust_ffi_static { rlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", Loading DnsResolver.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ DnsResolver::DnsResolver() { auto& dnsTlsDispatcher = DnsTlsDispatcher::getInstance(); auto& privateDnsConfiguration = PrivateDnsConfiguration::getInstance(); privateDnsConfiguration.setObserver(&dnsTlsDispatcher); if (isDoHEnabled()) privateDnsConfiguration.initDoh(); } bool DnsResolver::start() { Loading PrivateDnsConfiguration.cpp +138 −3 Original line number Diff line number Diff line Loading @@ -21,18 +21,22 @@ #include <android-base/format.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <netdutils/Slice.h> #include <netdutils/ThreadUtil.h> #include <sys/socket.h> #include "DnsTlsTransport.h" #include "ResolverEventReporter.h" #include "doh.h" #include "netd_resolv/resolv.h" #include "resolv_private.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 android::netdutils::Slice; using std::chrono::milliseconds; namespace android { Loading Loading @@ -238,9 +242,9 @@ void PrivateDnsConfiguration::startValidation(const ServerIdentity& identity, un } void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) { unsigned netId, bool success) const { LOG(DEBUG) << "Sending validation " << (success ? "success" : "failure") << " event on netId " << netId << " for " << identity.sockaddr.ip().toString() << " with hostname {" << netId << " for " << identity.sockaddr.toString() << " with hostname {" << identity.provider << "}"; // Send a validation event to NetdEventListenerService. const auto& listeners = ResolverEventReporter::getInstance().getListeners(); Loading Loading @@ -313,7 +317,9 @@ bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& i } // Send private dns validation result to listeners. if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } if (success) { updateServerState(identity, Validation::success, netId); Loading Loading @@ -411,5 +417,134 @@ void PrivateDnsConfiguration::dump(netdutils::DumpWriter& dw) const { dw.blankline(); } void PrivateDnsConfiguration::initDoh() { std::lock_guard guard(mPrivateDnsLock); initDohLocked(); } void PrivateDnsConfiguration::initDohLocked() { if (mDohDispatcher != nullptr) return; mDohDispatcher = doh_dispatcher_new( [](uint32_t net_id, bool success, const char* ip_addr, const char* host) { android::net::PrivateDnsConfiguration::getInstance().onDohStatusUpdate( net_id, success, ip_addr, host); }); } int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) { if (servers.empty()) return 0; LOG(DEBUG) << "PrivateDnsConfiguration::setDoh(" << netId << ", 0x" << std::hex << mark << std::dec << ", " << servers.size() << ", " << name << ")"; std::lock_guard guard(mPrivateDnsLock); initDohLocked(); // TODO: 1. Improve how to choose the server // TODO: 2. Support multiple servers for (const auto& entry : mAvailableDoHProviders) { const auto& doh = entry.getDohIdentity(servers, name); if (!doh.ok()) continue; auto it = mDohTracker.find(netId); // Skip if the same server already exists and its status == success. if (it != mDohTracker.end() && it->second == doh.value() && it->second.status == Validation::success) { return 0; } const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value()); const auto& dohId = dohIt->second; RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, 443), name}, dohId.status); mPrivateDnsLog.push(std::move(record)); return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(), dohId.ipAddr.c_str(), mark, caCert.c_str(), 3000); } LOG(INFO) << __func__ << "No suitable DoH server found"; return 0; } void PrivateDnsConfiguration::clearDoh(unsigned netId) { LOG(DEBUG) << "PrivateDnsConfiguration::clearDoh (" << netId << ")"; std::lock_guard guard(mPrivateDnsLock); if (mDohDispatcher != nullptr) doh_net_delete(mDohDispatcher, netId); mDohTracker.erase(netId); } ssize_t PrivateDnsConfiguration::dohQuery(unsigned netId, const Slice query, const Slice answer, uint64_t timeoutMs) { { std::lock_guard guard(mPrivateDnsLock); // It's safe because mDohDispatcher won't be deleted after initializing. if (mDohDispatcher == nullptr) return RESULT_CAN_NOT_SEND; } return doh_query(mDohDispatcher, netId, query.base(), query.size(), answer.base(), answer.size(), timeoutMs); } void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, const char* ipAddr, const char* host) { LOG(INFO) << __func__ << netId << ", " << success << ", " << ipAddr << ", " << host; std::lock_guard guard(mPrivateDnsLock); // Update the server status. auto it = mDohTracker.find(netId); if (it == mDohTracker.end() || (it->second.ipAddr != ipAddr && it->second.host != host)) { LOG(WARNING) << __func__ << "obsolete event"; return; } Validation status = success ? Validation::success : Validation::fail; it->second.status = status; // Send the events to registered listeners. ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, 443), host}; if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } // Add log. RecordEntry record(netId, identity, status); mPrivateDnsLog.push(std::move(record)); } bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity identity, bool success) const { // If the result is success or DoH is not enable, no concern to report the events. if (success || !isDoHEnabled()) return true; // If the result is failure, check another transport's status to determine if we should report // the event. switch (identity.sockaddr.port()) { // DoH case 443: { auto netPair = mPrivateDnsTransports.find(netId); if (netPair == mPrivateDnsTransports.end()) return true; for (const auto& [id, server] : netPair->second) { if ((identity.sockaddr.ip() == id.sockaddr.ip()) && (identity.sockaddr.port() != id.sockaddr.port()) && (server->validationState() == Validation::success)) { LOG(DEBUG) << __func__ << "skip reporting DoH validation failure event, server addr: " + identity.sockaddr.ip().toString(); return false; } } break; } // DoT case 853: { auto it = mDohTracker.find(netId); if (it == mDohTracker.end()) return true; if (it->second == identity && it->second.status == Validation::success) { LOG(DEBUG) << __func__ << "skip reporting DoT validation failure event, server addr: " + identity.sockaddr.ip().toString(); return false; } break; } } return true; } } // namespace net } // namespace android PrivateDnsConfiguration.h +81 −2 Original line number Diff line number Diff line Loading @@ -16,20 +16,25 @@ #pragma once #include <array> #include <list> #include <map> #include <mutex> #include <vector> #include <android-base/format.h> #include <android-base/logging.h> #include <android-base/result.h> #include <android-base/thread_annotations.h> #include <netdutils/BackoffSequence.h> #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> #include <netdutils/Slice.h> #include "DnsTlsServer.h" #include "LockedQueue.h" #include "PrivateDnsValidationObserver.h" #include "doh.h" namespace android { namespace net { Loading Loading @@ -61,6 +66,8 @@ class PrivateDnsConfiguration { explicit ServerIdentity(const IPrivateDnsServer& server) : sockaddr(server.addr()), provider(server.provider()) {} ServerIdentity(const netdutils::IPSockAddr& addr, const std::string& host) : sockaddr(addr), provider(host) {} bool operator<(const ServerIdentity& other) const { return std::tie(sockaddr, provider) < std::tie(other.sockaddr, other.provider); Loading @@ -79,10 +86,20 @@ class PrivateDnsConfiguration { int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock); void initDoh() EXCLUDES(mPrivateDnsLock); int setDoh(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock); PrivateDnsStatus getStatus(unsigned netId) const EXCLUDES(mPrivateDnsLock); void clear(unsigned netId) EXCLUDES(mPrivateDnsLock); void clearDoh(unsigned netId) EXCLUDES(mPrivateDnsLock); ssize_t dohQuery(unsigned netId, const netdutils::Slice query, const netdutils::Slice answer, uint64_t timeoutMs) EXCLUDES(mPrivateDnsLock); // Request the server to be revalidated on a connection tagged with |mark|. // Returns a Result to indicate if the request is accepted. base::Result<void> requestValidation(unsigned netId, const ServerIdentity& identity, Loading @@ -92,6 +109,9 @@ class PrivateDnsConfiguration { void dump(netdutils::DumpWriter& dw) const; void onDohStatusUpdate(uint32_t netId, bool success, const char* ipAddr, const char* host) EXCLUDES(mPrivateDnsLock); private: typedef std::map<ServerIdentity, std::unique_ptr<IPrivateDnsServer>> PrivateDnsTracker; Loading @@ -105,8 +125,8 @@ class PrivateDnsConfiguration { bool recordPrivateDnsValidation(const ServerIdentity& identity, unsigned netId, bool success, bool isRevalidation) EXCLUDES(mPrivateDnsLock); void sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) REQUIRES(mPrivateDnsLock); void sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) const 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 Loading @@ -123,6 +143,8 @@ class PrivateDnsConfiguration { base::Result<IPrivateDnsServer*> getPrivateDnsLocked(const ServerIdentity& identity, unsigned netId) REQUIRES(mPrivateDnsLock); void initDohLocked() REQUIRES(mPrivateDnsLock); mutable std::mutex mPrivateDnsLock; std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock); Loading @@ -135,9 +157,14 @@ class PrivateDnsConfiguration { void notifyValidationStateUpdate(const netdutils::IPSockAddr& sockaddr, Validation validation, uint32_t netId) const REQUIRES(mPrivateDnsLock); bool needReportEvent(uint32_t netId, ServerIdentity identity, bool success) const REQUIRES(mPrivateDnsLock); // TODO: fix the reentrancy problem. PrivateDnsValidationObserver* mObserver GUARDED_BY(mPrivateDnsLock); DohDispatcher* mDohDispatcher; friend class PrivateDnsConfigurationTest; // It's not const because PrivateDnsConfigurationTest needs to override it. Loading @@ -147,6 +174,58 @@ class PrivateDnsConfiguration { .withInitialRetransmissionTime(std::chrono::seconds(60)) .withMaximumRetransmissionTime(std::chrono::seconds(3600)); struct DohIdentity { std::string httpsTemplate; std::string ipAddr; std::string host; Validation status; bool operator<(const DohIdentity& other) const { return std::tie(ipAddr, host) < std::tie(other.ipAddr, other.host); } bool operator==(const DohIdentity& other) const { return std::tie(ipAddr, host) == std::tie(other.ipAddr, other.host); } bool operator<(const ServerIdentity& other) const { std::string otherIp = other.sockaddr.ip().toString(); return std::tie(ipAddr, host) < std::tie(otherIp, other.provider); } bool operator==(const ServerIdentity& other) const { std::string otherIp = other.sockaddr.ip().toString(); return std::tie(ipAddr, host) == std::tie(otherIp, other.provider); } }; struct DohProviderEntry { std::string provider; std::set<std::string> ips; std::string host; std::string httpsTemplate; base::Result<DohIdentity> getDohIdentity(const std::vector<std::string>& ips, const std::string& host) const { if (!host.empty() && this->host != host) return Errorf("host {} not matched", host); for (const auto& ip : ips) { if (this->ips.find(ip) == this->ips.end()) continue; LOG(INFO) << fmt::format("getDohIdentity: {} {}", ip, host); // Only pick the first one for now. return DohIdentity{httpsTemplate, ip, host, Validation::in_process}; } return Errorf("server not matched"); }; }; // TODO: Move below DoH relevant stuff into Rust implementation. std::map<unsigned, DohIdentity> mDohTracker GUARDED_BY(mPrivateDnsLock); std::array<DohProviderEntry, 2> mAvailableDoHProviders = {{ {"Google", {"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"}, "dns.google", "https://dns.google/dns-query"}, {"Cloudflare", {"2606:4700::6810:f8f9", "2606:4700::6810:f9f9", "104.16.248.249", "104.16.249.249"}, "cloudflare-dns.com", "https://cloudflare-dns.com/dns-query"}, }}; struct RecordEntry { RecordEntry(uint32_t netId, const ServerIdentity& identity, Validation state) : netId(netId), serverIdentity(identity), state(state) {} Loading ResolverController.cpp +14 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include "ResolverStats.h" #include "resolv_cache.h" #include "stats.h" #include "util.h" using aidl::android::net::ResolverParamsParcel; using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; Loading Loading @@ -169,6 +170,7 @@ void ResolverController::destroyNetworkCache(unsigned netId) { resolv_delete_cache_for_net(netId); mDns64Configuration.stopPrefixDiscovery(netId); PrivateDnsConfiguration::getInstance().clear(netId); if (isDoHEnabled()) PrivateDnsConfiguration::getInstance().clearDoh(netId); // Don't get this instance in PrivateDnsConfiguration. It's probe to deadlock. DnsTlsDispatcher::getInstance().forceCleanup(netId); Loading Loading @@ -206,8 +208,8 @@ int ResolverController::setResolverConfiguration(const ResolverParamsParcel& res // through a different network. For example, on a VPN with no DNS servers (Do53), if the VPN // applies to UID 0, dns_mark is assigned for default network rathan the VPN. (note that it's // possible that a VPN doesn't have any DNS servers but DoT servers in DNS strict mode) const int err = PrivateDnsConfiguration::getInstance().set( resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, int err = PrivateDnsConfiguration::getInstance().set(resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, resolverParams.caCertificate); if (err != 0) { Loading @@ -225,6 +227,15 @@ int ResolverController::setResolverConfiguration(const ResolverParamsParcel& res return err; } if (isDoHEnabled()) err = PrivateDnsConfiguration::getInstance().setDoh( resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, resolverParams.caCertificate); if (err != 0) { return err; } res_params res_params = {}; res_params.sample_validity = resolverParams.sampleValiditySeconds; res_params.success_threshold = resolverParams.successThreshold; Loading Loading
Android.bp +7 −3 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ cc_library { "libbase", "libcutils", "libnetdutils", "libdoh_ffi", "libprotobuf-cpp-lite", "libstatslog_resolv", "libstatspush_compat", Loading Loading @@ -331,7 +332,8 @@ rust_ffi_static { rlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche", Loading Loading @@ -366,7 +368,8 @@ rust_test { rustlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", Loading @@ -387,7 +390,8 @@ rust_ffi_static { rlibs: [ "libandroid_logger", "libanyhow", "liblazy_static", "libbase64_rust", "libfutures", "liblibc", "liblog_rust", "libquiche_static", Loading
DnsResolver.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ DnsResolver::DnsResolver() { auto& dnsTlsDispatcher = DnsTlsDispatcher::getInstance(); auto& privateDnsConfiguration = PrivateDnsConfiguration::getInstance(); privateDnsConfiguration.setObserver(&dnsTlsDispatcher); if (isDoHEnabled()) privateDnsConfiguration.initDoh(); } bool DnsResolver::start() { Loading
PrivateDnsConfiguration.cpp +138 −3 Original line number Diff line number Diff line Loading @@ -21,18 +21,22 @@ #include <android-base/format.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <netdutils/Slice.h> #include <netdutils/ThreadUtil.h> #include <sys/socket.h> #include "DnsTlsTransport.h" #include "ResolverEventReporter.h" #include "doh.h" #include "netd_resolv/resolv.h" #include "resolv_private.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 android::netdutils::Slice; using std::chrono::milliseconds; namespace android { Loading Loading @@ -238,9 +242,9 @@ void PrivateDnsConfiguration::startValidation(const ServerIdentity& identity, un } void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) { unsigned netId, bool success) const { LOG(DEBUG) << "Sending validation " << (success ? "success" : "failure") << " event on netId " << netId << " for " << identity.sockaddr.ip().toString() << " with hostname {" << netId << " for " << identity.sockaddr.toString() << " with hostname {" << identity.provider << "}"; // Send a validation event to NetdEventListenerService. const auto& listeners = ResolverEventReporter::getInstance().getListeners(); Loading Loading @@ -313,7 +317,9 @@ bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& i } // Send private dns validation result to listeners. if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } if (success) { updateServerState(identity, Validation::success, netId); Loading Loading @@ -411,5 +417,134 @@ void PrivateDnsConfiguration::dump(netdutils::DumpWriter& dw) const { dw.blankline(); } void PrivateDnsConfiguration::initDoh() { std::lock_guard guard(mPrivateDnsLock); initDohLocked(); } void PrivateDnsConfiguration::initDohLocked() { if (mDohDispatcher != nullptr) return; mDohDispatcher = doh_dispatcher_new( [](uint32_t net_id, bool success, const char* ip_addr, const char* host) { android::net::PrivateDnsConfiguration::getInstance().onDohStatusUpdate( net_id, success, ip_addr, host); }); } int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) { if (servers.empty()) return 0; LOG(DEBUG) << "PrivateDnsConfiguration::setDoh(" << netId << ", 0x" << std::hex << mark << std::dec << ", " << servers.size() << ", " << name << ")"; std::lock_guard guard(mPrivateDnsLock); initDohLocked(); // TODO: 1. Improve how to choose the server // TODO: 2. Support multiple servers for (const auto& entry : mAvailableDoHProviders) { const auto& doh = entry.getDohIdentity(servers, name); if (!doh.ok()) continue; auto it = mDohTracker.find(netId); // Skip if the same server already exists and its status == success. if (it != mDohTracker.end() && it->second == doh.value() && it->second.status == Validation::success) { return 0; } const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value()); const auto& dohId = dohIt->second; RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, 443), name}, dohId.status); mPrivateDnsLog.push(std::move(record)); return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(), dohId.ipAddr.c_str(), mark, caCert.c_str(), 3000); } LOG(INFO) << __func__ << "No suitable DoH server found"; return 0; } void PrivateDnsConfiguration::clearDoh(unsigned netId) { LOG(DEBUG) << "PrivateDnsConfiguration::clearDoh (" << netId << ")"; std::lock_guard guard(mPrivateDnsLock); if (mDohDispatcher != nullptr) doh_net_delete(mDohDispatcher, netId); mDohTracker.erase(netId); } ssize_t PrivateDnsConfiguration::dohQuery(unsigned netId, const Slice query, const Slice answer, uint64_t timeoutMs) { { std::lock_guard guard(mPrivateDnsLock); // It's safe because mDohDispatcher won't be deleted after initializing. if (mDohDispatcher == nullptr) return RESULT_CAN_NOT_SEND; } return doh_query(mDohDispatcher, netId, query.base(), query.size(), answer.base(), answer.size(), timeoutMs); } void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, const char* ipAddr, const char* host) { LOG(INFO) << __func__ << netId << ", " << success << ", " << ipAddr << ", " << host; std::lock_guard guard(mPrivateDnsLock); // Update the server status. auto it = mDohTracker.find(netId); if (it == mDohTracker.end() || (it->second.ipAddr != ipAddr && it->second.host != host)) { LOG(WARNING) << __func__ << "obsolete event"; return; } Validation status = success ? Validation::success : Validation::fail; it->second.status = status; // Send the events to registered listeners. ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, 443), host}; if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } // Add log. RecordEntry record(netId, identity, status); mPrivateDnsLog.push(std::move(record)); } bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity identity, bool success) const { // If the result is success or DoH is not enable, no concern to report the events. if (success || !isDoHEnabled()) return true; // If the result is failure, check another transport's status to determine if we should report // the event. switch (identity.sockaddr.port()) { // DoH case 443: { auto netPair = mPrivateDnsTransports.find(netId); if (netPair == mPrivateDnsTransports.end()) return true; for (const auto& [id, server] : netPair->second) { if ((identity.sockaddr.ip() == id.sockaddr.ip()) && (identity.sockaddr.port() != id.sockaddr.port()) && (server->validationState() == Validation::success)) { LOG(DEBUG) << __func__ << "skip reporting DoH validation failure event, server addr: " + identity.sockaddr.ip().toString(); return false; } } break; } // DoT case 853: { auto it = mDohTracker.find(netId); if (it == mDohTracker.end()) return true; if (it->second == identity && it->second.status == Validation::success) { LOG(DEBUG) << __func__ << "skip reporting DoT validation failure event, server addr: " + identity.sockaddr.ip().toString(); return false; } break; } } return true; } } // namespace net } // namespace android
PrivateDnsConfiguration.h +81 −2 Original line number Diff line number Diff line Loading @@ -16,20 +16,25 @@ #pragma once #include <array> #include <list> #include <map> #include <mutex> #include <vector> #include <android-base/format.h> #include <android-base/logging.h> #include <android-base/result.h> #include <android-base/thread_annotations.h> #include <netdutils/BackoffSequence.h> #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> #include <netdutils/Slice.h> #include "DnsTlsServer.h" #include "LockedQueue.h" #include "PrivateDnsValidationObserver.h" #include "doh.h" namespace android { namespace net { Loading Loading @@ -61,6 +66,8 @@ class PrivateDnsConfiguration { explicit ServerIdentity(const IPrivateDnsServer& server) : sockaddr(server.addr()), provider(server.provider()) {} ServerIdentity(const netdutils::IPSockAddr& addr, const std::string& host) : sockaddr(addr), provider(host) {} bool operator<(const ServerIdentity& other) const { return std::tie(sockaddr, provider) < std::tie(other.sockaddr, other.provider); Loading @@ -79,10 +86,20 @@ class PrivateDnsConfiguration { int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock); void initDoh() EXCLUDES(mPrivateDnsLock); int setDoh(int32_t netId, uint32_t mark, const std::vector<std::string>& servers, const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock); PrivateDnsStatus getStatus(unsigned netId) const EXCLUDES(mPrivateDnsLock); void clear(unsigned netId) EXCLUDES(mPrivateDnsLock); void clearDoh(unsigned netId) EXCLUDES(mPrivateDnsLock); ssize_t dohQuery(unsigned netId, const netdutils::Slice query, const netdutils::Slice answer, uint64_t timeoutMs) EXCLUDES(mPrivateDnsLock); // Request the server to be revalidated on a connection tagged with |mark|. // Returns a Result to indicate if the request is accepted. base::Result<void> requestValidation(unsigned netId, const ServerIdentity& identity, Loading @@ -92,6 +109,9 @@ class PrivateDnsConfiguration { void dump(netdutils::DumpWriter& dw) const; void onDohStatusUpdate(uint32_t netId, bool success, const char* ipAddr, const char* host) EXCLUDES(mPrivateDnsLock); private: typedef std::map<ServerIdentity, std::unique_ptr<IPrivateDnsServer>> PrivateDnsTracker; Loading @@ -105,8 +125,8 @@ class PrivateDnsConfiguration { bool recordPrivateDnsValidation(const ServerIdentity& identity, unsigned netId, bool success, bool isRevalidation) EXCLUDES(mPrivateDnsLock); void sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) REQUIRES(mPrivateDnsLock); void sendPrivateDnsValidationEvent(const ServerIdentity& identity, unsigned netId, bool success) const 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 Loading @@ -123,6 +143,8 @@ class PrivateDnsConfiguration { base::Result<IPrivateDnsServer*> getPrivateDnsLocked(const ServerIdentity& identity, unsigned netId) REQUIRES(mPrivateDnsLock); void initDohLocked() REQUIRES(mPrivateDnsLock); mutable std::mutex mPrivateDnsLock; std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock); Loading @@ -135,9 +157,14 @@ class PrivateDnsConfiguration { void notifyValidationStateUpdate(const netdutils::IPSockAddr& sockaddr, Validation validation, uint32_t netId) const REQUIRES(mPrivateDnsLock); bool needReportEvent(uint32_t netId, ServerIdentity identity, bool success) const REQUIRES(mPrivateDnsLock); // TODO: fix the reentrancy problem. PrivateDnsValidationObserver* mObserver GUARDED_BY(mPrivateDnsLock); DohDispatcher* mDohDispatcher; friend class PrivateDnsConfigurationTest; // It's not const because PrivateDnsConfigurationTest needs to override it. Loading @@ -147,6 +174,58 @@ class PrivateDnsConfiguration { .withInitialRetransmissionTime(std::chrono::seconds(60)) .withMaximumRetransmissionTime(std::chrono::seconds(3600)); struct DohIdentity { std::string httpsTemplate; std::string ipAddr; std::string host; Validation status; bool operator<(const DohIdentity& other) const { return std::tie(ipAddr, host) < std::tie(other.ipAddr, other.host); } bool operator==(const DohIdentity& other) const { return std::tie(ipAddr, host) == std::tie(other.ipAddr, other.host); } bool operator<(const ServerIdentity& other) const { std::string otherIp = other.sockaddr.ip().toString(); return std::tie(ipAddr, host) < std::tie(otherIp, other.provider); } bool operator==(const ServerIdentity& other) const { std::string otherIp = other.sockaddr.ip().toString(); return std::tie(ipAddr, host) == std::tie(otherIp, other.provider); } }; struct DohProviderEntry { std::string provider; std::set<std::string> ips; std::string host; std::string httpsTemplate; base::Result<DohIdentity> getDohIdentity(const std::vector<std::string>& ips, const std::string& host) const { if (!host.empty() && this->host != host) return Errorf("host {} not matched", host); for (const auto& ip : ips) { if (this->ips.find(ip) == this->ips.end()) continue; LOG(INFO) << fmt::format("getDohIdentity: {} {}", ip, host); // Only pick the first one for now. return DohIdentity{httpsTemplate, ip, host, Validation::in_process}; } return Errorf("server not matched"); }; }; // TODO: Move below DoH relevant stuff into Rust implementation. std::map<unsigned, DohIdentity> mDohTracker GUARDED_BY(mPrivateDnsLock); std::array<DohProviderEntry, 2> mAvailableDoHProviders = {{ {"Google", {"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"}, "dns.google", "https://dns.google/dns-query"}, {"Cloudflare", {"2606:4700::6810:f8f9", "2606:4700::6810:f9f9", "104.16.248.249", "104.16.249.249"}, "cloudflare-dns.com", "https://cloudflare-dns.com/dns-query"}, }}; struct RecordEntry { RecordEntry(uint32_t netId, const ServerIdentity& identity, Validation state) : netId(netId), serverIdentity(identity), state(state) {} Loading
ResolverController.cpp +14 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include "ResolverStats.h" #include "resolv_cache.h" #include "stats.h" #include "util.h" using aidl::android::net::ResolverParamsParcel; using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; Loading Loading @@ -169,6 +170,7 @@ void ResolverController::destroyNetworkCache(unsigned netId) { resolv_delete_cache_for_net(netId); mDns64Configuration.stopPrefixDiscovery(netId); PrivateDnsConfiguration::getInstance().clear(netId); if (isDoHEnabled()) PrivateDnsConfiguration::getInstance().clearDoh(netId); // Don't get this instance in PrivateDnsConfiguration. It's probe to deadlock. DnsTlsDispatcher::getInstance().forceCleanup(netId); Loading Loading @@ -206,8 +208,8 @@ int ResolverController::setResolverConfiguration(const ResolverParamsParcel& res // through a different network. For example, on a VPN with no DNS servers (Do53), if the VPN // applies to UID 0, dns_mark is assigned for default network rathan the VPN. (note that it's // possible that a VPN doesn't have any DNS servers but DoT servers in DNS strict mode) const int err = PrivateDnsConfiguration::getInstance().set( resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, int err = PrivateDnsConfiguration::getInstance().set(resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, resolverParams.caCertificate); if (err != 0) { Loading @@ -225,6 +227,15 @@ int ResolverController::setResolverConfiguration(const ResolverParamsParcel& res return err; } if (isDoHEnabled()) err = PrivateDnsConfiguration::getInstance().setDoh( resolverParams.netId, netcontext.app_mark, tlsServers, resolverParams.tlsName, resolverParams.caCertificate); if (err != 0) { return err; } res_params res_params = {}; res_params.sample_validity = resolverParams.sampleValiditySeconds; res_params.success_threshold = resolverParams.successThreshold; Loading