diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:35:39 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:35:39 +0000 |
commit | befe95e2b78d326d270d3ad7825beda6a1f47d08 (patch) | |
tree | 888eee2a961d218d5bc787ea4077697fc8ab300b | |
parent | 8be05f6c1eb5c6ddfa50ebde58ec8036f3e74ffe (diff) | |
parent | 9c4b2dccac4247418f70e07e37aa02054bded1e9 (diff) | |
download | DnsResolver-befe95e2b78d326d270d3ad7825beda6a1f47d08.tar.gz |
Snap for 11224086 from 9c4b2dccac4247418f70e07e37aa02054bded1e9 to mainline-tzdata5-release
Change-Id: I4bf41d0cdaa55ab2317dfdf424907a16495a23fa
79 files changed, 1694 insertions, 644 deletions
@@ -53,7 +53,7 @@ cc_library_headers { ], } -dnsresolver_aidl_interface_lateststable_version = "V11" +dnsresolver_aidl_interface_lateststable_version = "V13" cc_library_static { name: "dnsresolver_aidl_interface-lateststable-ndk", @@ -96,22 +96,67 @@ aidl_interface { min_sdk_version: "30", }, }, - versions: [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - ], + dumpapi: { no_license: true, }, + versions_with_info: [ + { + version: "1", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "2", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "3", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "4", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "5", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "6", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "7", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "8", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "9", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "10", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "11", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "12", + imports: ["netd_event_listener_interface-V1"], + }, + { + version: "13", + imports: ["netd_event_listener_interface-V1"], + }, + + ], + frozen: true, + } cc_defaults { @@ -154,7 +199,7 @@ cc_defaults { // after the build process. host_required: [ "net-tests-utils-host-common", - ] + ], } cc_defaults { @@ -176,7 +221,7 @@ cc_defaults { // after the build process. host_required: [ "net-tests-utils-host-common", - ] + ], } cc_library { @@ -229,8 +274,8 @@ cc_library { "libmodules-utils-build", "libprotobuf-cpp-lite", "libstatslog_resolv", - "libstatspush_compat", "libsysutils", + "libutils", "netd_event_listener_interface-lateststable-ndk", "server_configurable_flags", "stats_proto", @@ -251,6 +296,9 @@ cc_library { "libssl", "libstatssocket", ], + runtime_libs: [ + "libcom.android.tethering.dns_helper", + ], header_libs: [ "libnetdbinder_utils_headers", ], @@ -292,7 +340,7 @@ genrule { name: "statslog_resolv.h", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_resolv.h --module resolv" + - " --namespace android,net,stats --minApiLevel 29", + " --namespace android,net,stats --minApiLevel 30", out: [ "statslog_resolv.h", ], @@ -302,7 +350,7 @@ genrule { name: "statslog_resolv.cpp", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv" + - " --namespace android,net,stats --importHeader statslog_resolv.h --minApiLevel 29", + " --namespace android,net,stats --importHeader statslog_resolv.h --minApiLevel 30", out: [ "statslog_resolv.cpp", ], @@ -316,10 +364,9 @@ cc_library_static { export_generated_headers: ["statslog_resolv.h"], static_libs: [ "libcutils", - "libstatspush_compat", ], - header_libs: [ - "libgtest_prod_headers", // Used by libstatspush_compat + shared_libs: [ + "libstatssocket", ], apex_available: ["com.android.resolv"], min_sdk_version: "30", diff --git a/Dns64Configuration.cpp b/Dns64Configuration.cpp index a1fe8717..fc1428db 100644 --- a/Dns64Configuration.cpp +++ b/Dns64Configuration.cpp @@ -24,6 +24,7 @@ #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> #include <netdutils/ThreadUtil.h> +#include <utils/StrongPointer.h> #include <thread> #include <utility> @@ -36,6 +37,7 @@ namespace android { +using android::sp; using netdutils::DumpWriter; using netdutils::IPAddress; using netdutils::IPPrefix; @@ -61,8 +63,9 @@ void Dns64Configuration::startPrefixDiscovery(unsigned netId) { // Emplace a copy of |cfg| in the map. mDns64Configs.emplace(std::make_pair(netId, cfg)); + const sp<Dns64Configuration> thiz = sp<Dns64Configuration>::fromExisting(this); // Note that capturing |cfg| in this lambda creates a copy. - std::thread discovery_thread([this, cfg, netId] { + std::thread discovery_thread([thiz, cfg, netId] { setThreadName(fmt::format("Nat64Pfx_{}", netId)); // Make a mutable copy rather than mark the whole lambda mutable. @@ -75,28 +78,28 @@ void Dns64Configuration::startPrefixDiscovery(unsigned netId) { .build(); while (true) { - if (!this->shouldContinueDiscovery(evalCfg)) break; + if (!thiz->shouldContinueDiscovery(evalCfg)) break; android_net_context netcontext{}; - mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext); + thiz->mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext); // Prefix discovery must bypass private DNS because in strict mode // the server generally won't know the NAT64 prefix. netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS; if (doRfc7050PrefixDiscovery(netcontext, &evalCfg)) { - this->recordDns64Config(evalCfg); + thiz->recordDns64Config(evalCfg); break; } - if (!this->shouldContinueDiscovery(evalCfg)) break; + if (!thiz->shouldContinueDiscovery(evalCfg)) break; if (!backoff.hasNextTimeout()) break; { - std::unique_lock<std::mutex> cvGuard(mMutex); + std::unique_lock<std::mutex> cvGuard(thiz->mMutex); // TODO: Consider some chrono math, combined with wait_until() // perhaps, to prevent early re-resolves from the removal of // other netids with IPv6-only nameservers. - mCv.wait_for(cvGuard, backoff.getNextTimeout()); + thiz->mCv.wait_for(cvGuard, backoff.getNextTimeout()); } } }); diff --git a/Dns64Configuration.h b/Dns64Configuration.h index 387de785..4170e32d 100644 --- a/Dns64Configuration.h +++ b/Dns64Configuration.h @@ -20,12 +20,14 @@ #include <netinet/in.h> #include <condition_variable> #include <cstdlib> +#include <functional> #include <mutex> #include <unordered_map> #include <android-base/thread_annotations.h> #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> +#include <utils/RefBase.h> struct android_net_context; @@ -47,7 +49,7 @@ namespace net { * Thread-safety: All public methods in this class MUST be thread-safe. * (In other words: this class handles all its locking privately.) */ -class Dns64Configuration { +class Dns64Configuration : virtual public RefBase { public: // Simple data struct for passing back packet NAT64 prefix event information to the // Dns64PrefixCallback callback. diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp index 0de74939..e70ddb40 100644 --- a/DnsProxyListener.cpp +++ b/DnsProxyListener.cpp @@ -18,6 +18,7 @@ #include <arpa/inet.h> #include <dirent.h> +#include <dlfcn.h> #include <linux/if.h> #include <math.h> #include <net/if.h> @@ -70,20 +71,19 @@ using std::span; namespace android { +using netdutils::MAX_QUERIES_IN_TOTAL; +using netdutils::MAX_QUERIES_PER_UID; using netdutils::ResponseCode; using netdutils::Stopwatch; namespace net { namespace { -// Limits the number of outstanding DNS queries by client UID. -constexpr int MAX_QUERIES_PER_UID = 256; - android::netdutils::OperationLimiter<uid_t> queryLimiter(MAX_QUERIES_PER_UID); bool startQueryLimiter(uid_t uid) { - const int globalLimit = - android::net::Experiments::getInstance()->getFlag("max_queries_global", INT_MAX); + const int globalLimit = android::net::Experiments::getInstance()->getFlag("max_queries_global", + MAX_QUERIES_IN_TOTAL); return queryLimiter.start(uid, globalLimit); } @@ -322,10 +322,12 @@ void maybeLogQuery(int eventType, const android_net_context& netContext, void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs, int returnCode, NetworkDnsEventReported& event, const std::string& query_name, - const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) { - uint32_t rate = - (query_name.ends_with(".local") && is_mdns_supported_network(netContext.dns_netid) && - android::net::Experiments::getInstance()->getFlag("mdns_resolution", 1)) + bool skipStats, const std::vector<std::string>& ip_addrs = {}, + int total_ip_addr_count = 0) { + int32_t rate = + skipStats ? 0 + : (query_name.ends_with(".local") && is_mdns_supported_network(netContext.dns_netid) && + android::net::Experiments::getInstance()->getFlag("mdns_resolution", 1)) ? getDnsEventSubsamplingRate(netContext.dns_netid, returnCode, true) : getDnsEventSubsamplingRate(netContext.dns_netid, returnCode, false); @@ -334,11 +336,12 @@ void reportDnsEvent(int eventType, const android_net_context& netContext, int la stats::BytesField dnsQueryBytesField{dnsQueryStats.c_str(), dnsQueryStats.size()}; event.set_return_code(static_cast<ReturnCode>(returnCode)); event.set_network_type(resolv_get_network_types_for_net(netContext.dns_netid)); - android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED, - event.event_type(), event.return_code(), - event.latency_micros(), event.hints_ai_flags(), - event.res_nsend_flags(), event.network_type(), - event.private_dns_modes(), dnsQueryBytesField, rate); + event.set_uid(netContext.uid); + android::net::stats::stats_write( + android::net::stats::NETWORK_DNS_EVENT_REPORTED, event.event_type(), + event.return_code(), event.latency_micros(), event.hints_ai_flags(), + event.res_nsend_flags(), event.network_type(), event.private_dns_modes(), + dnsQueryBytesField, rate, event.uid()); } maybeLogQuery(eventType, netContext, event, query_name, ip_addrs); @@ -662,6 +665,59 @@ std::string makeThreadName(unsigned netId, uint32_t uid) { return fmt::format("Dns_{}_{}", netId, multiuser_get_app_id(uid)); } +typedef int (*InitFn)(); +typedef int (*IsUidBlockedFn)(uid_t, bool); + +IsUidBlockedFn ADnsHelper_isUidNetworkingBlocked; + +IsUidBlockedFn resolveIsUidNetworkingBlockedFn() { + // Related BPF maps were mainlined from T. + if (!isAtLeastT()) return nullptr; + + // TODO: Check whether it is safe to shared link the .so without using dlopen when the carrier + // APEX module (tethering) is fully released. + void* handle = dlopen("libcom.android.tethering.dns_helper.so", RTLD_NOW | RTLD_LOCAL); + if (!handle) { + LOG(WARNING) << __func__ << ": " << dlerror(); + return nullptr; + } + + InitFn ADnsHelper_init = reinterpret_cast<InitFn>(dlsym(handle, "ADnsHelper_init")); + if (!ADnsHelper_init) { + LOG(ERROR) << __func__ << ": " << dlerror(); + abort(); + } + const int ret = (*ADnsHelper_init)(); + if (ret) { + LOG(ERROR) << __func__ << ": ADnsHelper_init failed " << strerror(-ret); + abort(); + } + + IsUidBlockedFn f = + reinterpret_cast<IsUidBlockedFn>(dlsym(handle, "ADnsHelper_isUidNetworkingBlocked")); + if (!f) { + LOG(ERROR) << __func__ << ": " << dlerror(); + abort(); + } + return f; +} + +bool isUidNetworkingBlocked(uid_t uid, unsigned netId) { + if (!ADnsHelper_isUidNetworkingBlocked) return false; + + // The enforceDnsUid is an OEM feature that sets DNS packet with AID_DNS instead of the + // application's UID. Its DNS packets are not subject to certain network restriction features. + if (resolv_is_enforceDnsUid_enabled_network(netId)) return false; + + // Feature flag that can disable the feature. + if (!android::net::Experiments::getInstance()->getFlag("fail_fast_on_uid_network_blocking", + 1)) { + return false; + } + + return (*ADnsHelper_isUidNetworkingBlocked)(uid, resolv_is_metered_network(netId)) == 1; +} + } // namespace DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) { @@ -679,6 +735,8 @@ DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) { mGetDnsNetIdCommand = std::make_unique<GetDnsNetIdCommand>(); registerCmd(mGetDnsNetIdCommand.get()); + + ADnsHelper_isUidNetworkingBlocked = resolveIsUidNetworkingBlockedFn(); } void DnsProxyListener::Handler::spawn() { @@ -819,21 +877,14 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf if (ipv6WantedButNoData) { // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis - const uid_t uid = mClient->getUid(); - if (startQueryLimiter(uid)) { - const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); - const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); - mHints->ai_family = AF_INET; - // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous - // DNS lookup is failed with error EAI_NODATA. - *rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, res, event); - endQueryLimiter(uid); - if (*rv) { - *rv = EAI_NODATA; // return original error code - return; - } - } else { - LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached"; + const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); + const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); + mHints->ai_family = AF_INET; + // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous + // DNS lookup is failed with error EAI_NODATA. + *rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, res, event); + if (*rv) { + *rv = EAI_NODATA; // return original error code return; } } @@ -861,11 +912,16 @@ void DnsProxyListener::GetAddrInfoHandler::run() { int32_t rv = 0; NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); - if (startQueryLimiter(uid)) { + const bool isUidBlocked = isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid); + if (isUidBlocked) { + LOG(INFO) << "GetAddrInfoHandler::run: network access blocked"; + rv = EAI_FAIL; + } else if (startQueryLimiter(uid)) { const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); if (evaluate_domain_name(mNetContext, host)) { rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, &result, &event); + doDns64Synthesis(&rv, &result, &event); } else { rv = EAI_SYSTEM; } @@ -878,7 +934,6 @@ void DnsProxyListener::GetAddrInfoHandler::run() { << ", max concurrent queries reached"; } - doDns64Synthesis(&rv, &result, &event); const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); event.set_latency_micros(latencyUs); event.set_event_type(EVENT_GETADDRINFO); @@ -906,7 +961,7 @@ void DnsProxyListener::GetAddrInfoHandler::run() { std::vector<std::string> ip_addrs; const int total_ip_addr_count = extractGetAddrInfoAnswers(result, &ip_addrs); reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, event, mHost, - ip_addrs, total_ip_addr_count); + isUidBlocked, ip_addrs, total_ip_addr_count); freeaddrinfo(result); } @@ -1056,8 +1111,8 @@ void DnsProxyListener::ResNSendHandler::run() { uint16_t original_query_id = 0; // TODO: Handle the case which is msg contains more than one query - if (!parseQuery({msg.data(), msgLen}, &original_query_id, &rr_type, &rr_name) || - !setQueryId({msg.data(), msgLen}, arc4random_uniform(65536))) { + if (!parseQuery(std::span(msg.data(), msgLen), &original_query_id, &rr_type, &rr_name) || + !setQueryId(std::span(msg.data(), msgLen), arc4random_uniform(65536))) { // If the query couldn't be parsed, block the request. LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid << ", invalid query"; sendBE32(mClient, -EINVAL); @@ -1070,11 +1125,16 @@ void DnsProxyListener::ResNSendHandler::run() { int ansLen = -1; NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); - if (startQueryLimiter(uid)) { + const bool isUidBlocked = isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid); + if (isUidBlocked) { + LOG(INFO) << "ResNSendHandler::run: network access blocked"; + ansLen = -ECONNREFUSED; + } else if (startQueryLimiter(uid)) { if (evaluate_domain_name(mNetContext, rr_name.c_str())) { - ansLen = resolv_res_nsend(&mNetContext, {msg.data(), msgLen}, ansBuf, &rcode, + ansLen = resolv_res_nsend(&mNetContext, std::span(msg.data(), msgLen), ansBuf, &rcode, static_cast<ResNsendFlags>(mFlags), &event); } else { + // TODO(b/307048182): It should return -errno. ansLen = -EAI_SYSTEM; } endQueryLimiter(uid); @@ -1097,7 +1157,7 @@ void DnsProxyListener::ResNSendHandler::run() { } if (rr_type == ns_t_a || rr_type == ns_t_aaaa) { reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs, - resNSendToAiError(ansLen, rcode), event, rr_name); + resNSendToAiError(ansLen, rcode), event, rr_name, isUidBlocked); } return; } @@ -1110,7 +1170,7 @@ void DnsProxyListener::ResNSendHandler::run() { } // Restore query id - if (!setQueryId({ansBuf.data(), ansLen}, original_query_id)) { + if (!setQueryId(std::span(ansBuf.data(), ansLen), original_query_id)) { LOG(WARNING) << "ResNSendHandler::run: resnsend: failed to restore query id"; return; } @@ -1125,10 +1185,10 @@ void DnsProxyListener::ResNSendHandler::run() { if (rr_type == ns_t_a || rr_type == ns_t_aaaa) { std::vector<std::string> ip_addrs; const int total_ip_addr_count = - extractResNsendAnswers({ansBuf.data(), ansLen}, rr_type, &ip_addrs); + extractResNsendAnswers(std::span(ansBuf.data(), ansLen), rr_type, &ip_addrs); reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs, - resNSendToAiError(ansLen, rcode), event, rr_name, ip_addrs, - total_ip_addr_count); + resNSendToAiError(ansLen, rcode), event, rr_name, /*skipStats=*/false, + ip_addrs, total_ip_addr_count); } } @@ -1245,17 +1305,10 @@ void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, hoste } // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis - const uid_t uid = mClient->getUid(); - if (startQueryLimiter(uid)) { - const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); - *rv = resolv_gethostbyname(name, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); - endQueryLimiter(uid); - if (*rv) { - *rv = EAI_NODATA; // return original error code - return; - } - } else { - LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached"; + const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); + *rv = resolv_gethostbyname(name, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); + if (*rv) { + *rv = EAI_NODATA; // return original error code return; } @@ -1277,11 +1330,16 @@ void DnsProxyListener::GetHostByNameHandler::run() { int32_t rv = 0; NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); - if (startQueryLimiter(uid)) { + const bool isUidBlocked = isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid); + if (isUidBlocked) { + LOG(INFO) << "GetHostByNameHandler::run: network access blocked"; + rv = EAI_FAIL; + } else if (startQueryLimiter(uid)) { const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); if (evaluate_domain_name(mNetContext, name)) { rv = resolv_gethostbyname(name, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); + doDns64Synthesis(&rv, &hbuf, tmpbuf, sizeof tmpbuf, &hp, &event); } else { rv = EAI_SYSTEM; } @@ -1292,7 +1350,6 @@ void DnsProxyListener::GetHostByNameHandler::run() { << ", max concurrent queries reached"; } - doDns64Synthesis(&rv, &hbuf, tmpbuf, sizeof tmpbuf, &hp, &event); const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); event.set_latency_micros(latencyUs); event.set_event_type(EVENT_GETHOSTBYNAME); @@ -1318,7 +1375,7 @@ void DnsProxyListener::GetHostByNameHandler::run() { std::vector<std::string> ip_addrs; const int total_ip_addr_count = extractGetHostByNameAnswers(hp, &ip_addrs); reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, event, - mName, ip_addrs, total_ip_addr_count); + mName, isUidBlocked, ip_addrs, total_ip_addr_count); } std::string DnsProxyListener::GetHostByNameHandler::threadName() { @@ -1407,27 +1464,21 @@ void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(hostent* hbuf, return; } - const uid_t uid = mClient->getUid(); - if (startQueryLimiter(uid)) { - // Remove NAT64 prefix and do reverse DNS query - struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]}; - resolv_gethostbyaddr(&v4addr, sizeof(v4addr), AF_INET, hbuf, buf, buflen, &mNetContext, hpp, - event); - endQueryLimiter(uid); - if (*hpp && (*hpp)->h_addr_list[0]) { - // Replace IPv4 address with original queried IPv6 address in place. The space has - // reserved by dns_gethtbyaddr() and netbsd_gethostent_r() in - // system/netd/resolv/gethnamaddr.cpp. - // Note that resolv_gethostbyaddr() returns only one entry in result. - char* addr = (*hpp)->h_addr_list[0]; - memcpy(addr, &v6addr, sizeof(v6addr)); - (*hpp)->h_addrtype = AF_INET6; - (*hpp)->h_length = sizeof(struct in6_addr); - } else { - LOG(ERROR) << __func__ << ": hpp or (*hpp)->h_addr_list[0] is null"; - } + // Remove NAT64 prefix and do reverse DNS query + struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]}; + resolv_gethostbyaddr(&v4addr, sizeof(v4addr), AF_INET, hbuf, buf, buflen, &mNetContext, hpp, + event); + if (*hpp && (*hpp)->h_addr_list[0]) { + // Replace IPv4 address with original queried IPv6 address in place. The space has + // reserved by dns_gethtbyaddr() and netbsd_gethostent_r() in + // system/netd/resolv/gethnamaddr.cpp. + // Note that resolv_gethostbyaddr() returns only one entry in result. + char* addr = (*hpp)->h_addr_list[0]; + memcpy(addr, &v6addr, sizeof(v6addr)); + (*hpp)->h_addrtype = AF_INET6; + (*hpp)->h_length = sizeof(struct in6_addr); } else { - LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached"; + LOG(ERROR) << __func__ << ": hpp or (*hpp)->h_addr_list[0] is null"; } } @@ -1442,7 +1493,12 @@ void DnsProxyListener::GetHostByAddrHandler::run() { int32_t rv = 0; NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); - if (startQueryLimiter(uid)) { + + const bool isUidBlocked = isUidNetworkingBlocked(mNetContext.uid, mNetContext.dns_netid); + if (isUidBlocked) { + LOG(INFO) << "GetHostByAddrHandler::run: network access blocked"; + rv = EAI_FAIL; + } else if (startQueryLimiter(uid)) { // From Android U, evaluate_domain_name() is not only for OEM customization, but also tells // DNS resolver whether the UID can send DNS on the specified network. The function needs // to be called even when there is no domain name to evaluate (GetHostByAddr). This is @@ -1454,6 +1510,7 @@ void DnsProxyListener::GetHostByAddrHandler::run() { } else { rv = resolv_gethostbyaddr(&mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); + doDns64ReverseLookup(&hbuf, tmpbuf, sizeof tmpbuf, &hp, &event); } endQueryLimiter(uid); } else { @@ -1462,7 +1519,6 @@ void DnsProxyListener::GetHostByAddrHandler::run() { << ", max concurrent queries reached"; } - doDns64ReverseLookup(&hbuf, tmpbuf, sizeof tmpbuf, &hp, &event); const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs()); event.set_latency_micros(latencyUs); event.set_event_type(EVENT_GETHOSTBYADDR); @@ -1485,7 +1541,7 @@ void DnsProxyListener::GetHostByAddrHandler::run() { } reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, event, - (hp && hp->h_name) ? hp->h_name : "null", {}, 0); + (hp && hp->h_name) ? hp->h_name : "null", isUidBlocked, {}, 0); } std::string DnsProxyListener::GetHostByAddrHandler::threadName() { diff --git a/DnsResolver.cpp b/DnsResolver.cpp index 5abfaea6..372252d6 100644 --- a/DnsResolver.cpp +++ b/DnsResolver.cpp @@ -68,7 +68,6 @@ bool verifyCallbacks() { DnsResolver* gDnsResolv = nullptr; ResolverNetdCallbacks gResNetdCallbacks; -netdutils::Log gDnsResolverLog("dnsResolver"); uint64_t gApiLevel = 0; DnsResolver* DnsResolver::getInstance() { diff --git a/DnsResolver.h b/DnsResolver.h index 9c2f3d8c..a3819f15 100644 --- a/DnsResolver.h +++ b/DnsResolver.h @@ -21,7 +21,6 @@ #include "DnsQueryLog.h" #include "ResolverController.h" #include "netd_resolv/resolv.h" -#include "netdutils/Log.h" namespace android { namespace net { @@ -48,7 +47,6 @@ class DnsResolver { extern DnsResolver* gDnsResolv; extern ResolverNetdCallbacks gResNetdCallbacks; -extern netdutils::Log gDnsResolverLog; extern uint64_t gApiLevel; } // namespace net diff --git a/DnsResolverService.cpp b/DnsResolverService.cpp index ee129514..2d7a0e5f 100644 --- a/DnsResolverService.cpp +++ b/DnsResolverService.cpp @@ -90,8 +90,6 @@ binder_status_t DnsResolverService::start() { ABinderProcess_startThreadPool(); - // TODO: register log callback if binder NDK backend support it. b/126501406 - return STATUS_OK; } @@ -199,19 +197,7 @@ binder_status_t DnsResolverService::dump(int fd, const char** args, uint32_t num return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(EX_SECURITY, err.c_str())); } - // TODO: Remove this log after AIDL gen_log supporting more types, b/129732660 - auto entry = - gDnsResolverLog.newEntry() - .prettyFunction(__PRETTY_FUNCTION__) - .args(resolverParams.netId, resolverParams.servers, resolverParams.domains, - resolverParams.sampleValiditySeconds, resolverParams.successThreshold, - resolverParams.minSamples, resolverParams.maxSamples, - resolverParams.baseTimeoutMsec, resolverParams.retryCount, - resolverParams.tlsName, resolverParams.tlsServers); - int res = gDnsResolv->resolverCtrl.setResolverConfiguration(resolverParams); - gResNetdCallbacks.log(entry.returns(res).withAutomaticDuration().toString().c_str()); - return statusFromErrcode(res); } diff --git a/DnsTlsDispatcher.cpp b/DnsTlsDispatcher.cpp index 452d28b8..6e0ced2a 100644 --- a/DnsTlsDispatcher.cpp +++ b/DnsTlsDispatcher.cpp @@ -71,7 +71,7 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList( if (!xport->usable()) { // Don't use this xport. It will be removed after timeout // (IDLE_TIMEOUT minutes). - LOG(DEBUG) << "Skip using DoT server " << tlsServer.toIpString() << " on " + LOG(DEBUG) << "Skip using DoT server " << tlsServer.toString() << " on " << netId; continue; } @@ -231,7 +231,7 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un // a new xport will be created. const auto result = PrivateDnsConfiguration::getInstance().requestDotValidation( netId, PrivateDnsConfiguration::ServerIdentity{server}, mark); - LOG(WARNING) << "Requested validation for " << server.toIpString() << " with mark 0x" + LOG(WARNING) << "Requested validation for " << server.toString() << " with mark 0x" << std::hex << mark << ", " << (result.ok() ? "succeeded" : "failed: " + result.error().message()); } @@ -327,7 +327,7 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& ret = new Transport(server, mark, netId, mFactory.get(), triggerThr, unusableThr, queryTimeout); LOG(INFO) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " << queryTimeout << "ms }" - << " for server { " << server.toIpString() << "/" << server.name << " }"; + << " for server " << server.toString(); mStore[key].reset(ret); diff --git a/DnsTlsServer.cpp b/DnsTlsServer.cpp index 89ea8417..76825317 100644 --- a/DnsTlsServer.cpp +++ b/DnsTlsServer.cpp @@ -18,6 +18,7 @@ #include <algorithm> +#include <android-base/format.h> #include <netdutils/InternetAddresses.h> namespace { @@ -130,5 +131,9 @@ std::string DnsTlsServer::toIpString() const { return netdutils::IPSockAddr::toIPSockAddr(ss).ip().toString(); } +std::string DnsTlsServer::toString() const { + return fmt::format("{{{}/{}}}", toIpString(), name); +} + } // namespace net } // namespace android diff --git a/DnsTlsServer.h b/DnsTlsServer.h index ef364942..9d2ac5e6 100644 --- a/DnsTlsServer.h +++ b/DnsTlsServer.h @@ -63,6 +63,7 @@ struct DnsTlsServer { bool wasExplicitlyConfigured() const; std::string toIpString() const; + std::string toString() const; std::string provider() const { return name; } netdutils::IPSockAddr addr() const { return netdutils::IPSockAddr::toIPSockAddr(ss); } diff --git a/DnsTlsSocket.cpp b/DnsTlsSocket.cpp index 9789aa5b..2d1eeb85 100644 --- a/DnsTlsSocket.cpp +++ b/DnsTlsSocket.cpp @@ -70,7 +70,7 @@ int waitForWriting(int fd, int timeoutMs = -1) { Status DnsTlsSocket::tcpConnect() { if (mServer.protocol != IPPROTO_TCP) return Status(EPROTONOSUPPORT); - LOG(DEBUG) << mMark << " connecting TCP socket"; + LOG(INFO) << fmt::format("Connecting to {} with mark 0x{:x}", mServer.toString(), mMark); mSslFd.reset(socket(mServer.ss.ss_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); if (mSslFd.get() == -1) { @@ -270,7 +270,7 @@ bssl::UniquePtr<SSL> DnsTlsSocket::sslConnect(int fd) { for (;;) { LOG(DEBUG) << " Calling SSL_connect with mark 0x" << std::hex << mMark; int ret = SSL_connect(ssl.get()); - LOG(DEBUG) << " SSL_connect returned " << ret << " with mark 0x" << std::hex << mMark; + LOG(INFO) << " SSL_connect returned " << ret << " with mark 0x" << std::hex << mMark; if (ret == 1) break; // SSL handshake complete; const int ssl_err = SSL_get_error(ssl.get(), ret); @@ -315,7 +315,7 @@ bssl::UniquePtr<SSL> DnsTlsSocket::sslConnectV2(int fd) { for (;;) { LOG(DEBUG) << " Calling SSL_connect with mark 0x" << std::hex << mMark; int ret = SSL_connect(ssl.get()); - LOG(DEBUG) << " SSL_connect returned " << ret << " with mark 0x" << std::hex << mMark; + LOG(INFO) << " SSL_connect returned " << ret << " with mark 0x" << std::hex << mMark; if (ret == 1) break; // SSL handshake complete; enum { SSLFD = 0, EVENTFD = 1 }; @@ -445,7 +445,7 @@ void DnsTlsSocket::loop() { break; } if (s < 0) { - PLOG(DEBUG) << "Poll failed"; + PLOG(WARNING) << "Poll failed"; break; } if (fds[SSLFD].revents & (POLLIN | POLLERR | POLLHUP)) { @@ -457,7 +457,7 @@ void DnsTlsSocket::loop() { // refactoring it to not get blocked in any case. do { if (!readResponse()) { - LOG(DEBUG) << "SSL remote close or read error."; + LOG(INFO) << "SSL remote close or read error."; readFailed = true; } } while (SSL_pending(mSsl.get()) > 0 && !readFailed); @@ -496,7 +496,7 @@ void DnsTlsSocket::loop() { q.pop_front(); } } - LOG(DEBUG) << "Disconnecting"; + LOG(INFO) << fmt::format("Disconnecting {}, mark 0x{:x}", mServer.toString(), mMark); sslDisconnect(); LOG(DEBUG) << "Calling onClosed"; mObserver->onClosed(); diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp index 172a1e36..de40aac1 100644 --- a/DnsTlsTransport.cpp +++ b/DnsTlsTransport.cpp @@ -113,7 +113,7 @@ base::Result<void> sendUdpQuery(netdutils::IPAddress ip, uint32_t mark, return ErrnoErrorf("connect failed"); } - if (send(fd, query.data(), query.size(), 0) != query.size()) { + if (send(fd, query.data(), query.size(), 0) != static_cast<ptrdiff_t>(query.size())) { return ErrnoErrorf("send failed"); } diff --git a/Experiments.h b/Experiments.h index a9845629..db58630c 100644 --- a/Experiments.h +++ b/Experiments.h @@ -17,6 +17,7 @@ #pragma once #include <climits> +#include <functional> #include <map> #include <mutex> #include <string> @@ -61,6 +62,7 @@ class Experiments { "dot_validation_latency_factor", "dot_validation_latency_offset_ms", "dot_xport_unusable_threshold", + "fail_fast_on_uid_network_blocking", "keep_listening_udp", "max_cache_entries", "max_queries_global", @@ -68,7 +70,6 @@ class Experiments { "parallel_lookup_sleep_time", "retransmission_time_interval", "retry_count", - "skip_4a_query_on_v6_linklocal_addr", "sort_nameservers", }; // This value is used in updateInternal as the default value if any flags can't be found. @@ -1,2 +1,3 @@ +# Bug component: 31808 set noparent -file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking +file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking diff --git a/OperationLimiter.h b/OperationLimiter.h index 24f4dc3c..335ec5a3 100644 --- a/OperationLimiter.h +++ b/OperationLimiter.h @@ -28,6 +28,11 @@ namespace android { namespace netdutils { +// Limits the number of outstanding DNS queries by client UID. +constexpr int MAX_QUERIES_PER_UID = 256; +// Limits the total number of outstanding DNS queries. +constexpr int MAX_QUERIES_IN_TOTAL = 2500; + // Tracks the number of operations in progress on behalf of a particular key or // ID, rejecting further attempts to start new operations after a configurable // limit has been reached. @@ -56,11 +61,11 @@ class OperationLimiter { // // Note: each successful start(key) must be matched by exactly one call to // finish(key). - bool start(KeyType key, int globalLimit = INT_MAX) EXCLUDES(mMutex) { + bool start(KeyType key, int globalLimit = MAX_QUERIES_IN_TOTAL) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); if (globalLimit < mLimitPerKey) { LOG(ERROR) << "Misconfiguration on max_queries_global " << globalLimit; - globalLimit = INT_MAX; + globalLimit = MAX_QUERIES_IN_TOTAL; } if (mGlobalCounter >= globalLimit) { // Oh, no! diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp index 013cd1a5..cb1a8987 100644 --- a/PrivateDnsConfiguration.cpp +++ b/PrivateDnsConfiguration.cpp @@ -365,11 +365,11 @@ void PrivateDnsConfiguration::startDotValidation(const ServerIdentity& identity, while (true) { // ::validate() is a blocking call that performs network operations. // It can take milliseconds to minutes, up to the SYN retry limit. - LOG(WARNING) << "Validating DnsTlsServer " << server.toIpString() << " with mark 0x" + LOG(WARNING) << "Validating DnsTlsServer " << server.toString() << " with mark 0x" << std::hex << server.validationMark(); const bool success = DnsTlsTransport::validate(server, server.validationMark()); LOG(WARNING) << "validateDnsTlsServer returned " << success << " for " - << server.toIpString(); + << server.toString(); const bool needs_reeval = this->recordDotValidation(identity, netId, success, isRevalidation); @@ -534,8 +534,8 @@ base::Result<DnsTlsServer*> PrivateDnsConfiguration::getDotServerLocked( auto iter = netPair->second.find(identity); if (iter == netPair->second.end()) { - return Errorf("Failed to get private DNS: server {{{}/{}}} not found", identity.sockaddr, - identity.provider); + return Errorf("Failed to get private DNS: server {{{}/{}}} not found", + identity.sockaddr.toString(), identity.provider); } return &iter->second; diff --git a/ResolverController.cpp b/ResolverController.cpp index 7fe01d48..757e3f74 100644 --- a/ResolverController.cpp +++ b/ResolverController.cpp @@ -37,9 +37,11 @@ #include "stats.h" #include "util.h" +using aidl::android::net::IDnsResolver; using aidl::android::net::ResolverParamsParcel; using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using aidl::android::net::resolv::aidl::Nat64PrefixEventParcel; +using android::net::ResolverStats; namespace android { @@ -79,8 +81,6 @@ void sendNat64PrefixEvent(const Dns64Configuration::Nat64PrefixInfo& args) { int getDnsInfo(unsigned netId, std::vector<std::string>* servers, std::vector<std::string>* domains, res_params* params, std::vector<android::net::ResolverStats>* stats, int* wait_for_pending_req_timeout_count) { - using aidl::android::net::IDnsResolver; - using android::net::ResolverStats; static_assert(ResolverStats::STATS_SUCCESSES == IDnsResolver::RESOLVER_STATS_SUCCESSES && ResolverStats::STATS_ERRORS == IDnsResolver::RESOLVER_STATS_ERRORS && ResolverStats::STATS_TIMEOUTS == IDnsResolver::RESOLVER_STATS_TIMEOUTS && @@ -155,11 +155,11 @@ int getDnsInfo(unsigned netId, std::vector<std::string>* servers, std::vector<st } // namespace ResolverController::ResolverController() - : mDns64Configuration( + : mDns64Configuration(android::sp<Dns64Configuration>::make( [](uint32_t netId, uint32_t uid, android_net_context* netcontext) { gResNetdCallbacks.get_network_context(netId, uid, netcontext); }, - std::bind(sendNat64PrefixEvent, std::placeholders::_1)) {} + std::bind(sendNat64PrefixEvent, std::placeholders::_1))) {} void ResolverController::destroyNetworkCache(unsigned netId) { LOG(VERBOSE) << __func__ << ": netId = " << netId; @@ -173,7 +173,7 @@ void ResolverController::destroyNetworkCache(unsigned netId) { event.network_type(), event.private_dns_modes(), bytesField); resolv_delete_cache_for_net(netId); - mDns64Configuration.stopPrefixDiscovery(netId); + mDns64Configuration->stopPrefixDiscovery(netId); privateDnsConfiguration.clear(netId); // Don't get this instance in PrivateDnsConfiguration. It's probe to deadlock. @@ -192,8 +192,6 @@ int ResolverController::flushNetworkCache(unsigned netId) { } int ResolverController::setResolverConfiguration(const ResolverParamsParcel& resolverParams) { - using aidl::android::net::IDnsResolver; - if (!has_named_cache(resolverParams.netId)) { return -ENOENT; } @@ -239,7 +237,8 @@ int ResolverController::setResolverConfiguration(const ResolverParamsParcel& res return resolv_set_nameservers(resolverParams.netId, resolverParams.servers, resolverParams.domains, res_params, - resolverParams.resolverOptions, resolverParams.transportTypes); + resolverParams.resolverOptions, resolverParams.transportTypes, + resolverParams.meteredNetwork); } int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers, @@ -247,8 +246,6 @@ int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* std::vector<std::string>* tlsServers, std::vector<int32_t>* params, std::vector<int32_t>* stats, int* wait_for_pending_req_timeout_count) { - using aidl::android::net::IDnsResolver; - using android::net::ResolverStats; res_params res_params; std::vector<ResolverStats> res_stats; int ret = getDnsInfo(netId, servers, domains, &res_params, &res_stats, @@ -276,16 +273,16 @@ int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* } void ResolverController::startPrefix64Discovery(int32_t netId) { - mDns64Configuration.startPrefixDiscovery(netId); + mDns64Configuration->startPrefixDiscovery(netId); } void ResolverController::stopPrefix64Discovery(int32_t netId) { - return mDns64Configuration.stopPrefixDiscovery(netId); + return mDns64Configuration->stopPrefixDiscovery(netId); } // TODO: use StatusOr<T> to wrap the result. int ResolverController::getPrefix64(unsigned netId, netdutils::IPPrefix* prefix) { - netdutils::IPPrefix p = mDns64Configuration.getPrefix64(netId); + netdutils::IPPrefix p = mDns64Configuration->getPrefix64(netId); if (p.family() != AF_INET6 || p.length() == 0) { return -ENOENT; } @@ -295,7 +292,6 @@ int ResolverController::getPrefix64(unsigned netId, netdutils::IPPrefix* prefix) void ResolverController::dump(DumpWriter& dw, unsigned netId) { // No lock needed since Bionic's resolver locks all accessed data structures internally. - using android::net::ResolverStats; std::vector<std::string> servers; std::vector<std::string> domains; res_params params = {}; @@ -352,7 +348,7 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { params.max_samples, params.base_timeout_msec, params.retry_count); } - mDns64Configuration.dump(dw, netId); + mDns64Configuration->dump(dw, netId); const auto privateDnsStatus = PrivateDnsConfiguration::getInstance().getStatus(netId); dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode)); if (privateDnsStatus.dotServersMap.size() == 0) { diff --git a/ResolverController.h b/ResolverController.h index 0af830ee..b74cff92 100644 --- a/ResolverController.h +++ b/ResolverController.h @@ -55,10 +55,10 @@ class ResolverController { // Set or clear a NAT64 prefix discovered by other sources (e.g., RA). int setPrefix64(unsigned netId, const netdutils::IPPrefix& prefix) { - return mDns64Configuration.setPrefix64(netId, prefix); + return mDns64Configuration->setPrefix64(netId, prefix); } - int clearPrefix64(unsigned netId) { return mDns64Configuration.clearPrefix64(netId); } + int clearPrefix64(unsigned netId) { return mDns64Configuration->clearPrefix64(netId); } // Return the current NAT64 prefix network, regardless of how it was discovered. int getPrefix64(unsigned netId, netdutils::IPPrefix* prefix); @@ -66,7 +66,7 @@ class ResolverController { void dump(netdutils::DumpWriter& dw, unsigned netId); private: - Dns64Configuration mDns64Configuration; + android::sp<Dns64Configuration> mDns64Configuration; }; } // namespace net } // namespace android diff --git a/aidl_api/dnsresolver_aidl_interface/12/.hash b/aidl_api/dnsresolver_aidl_interface/12/.hash new file mode 100644 index 00000000..788aef87 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/.hash @@ -0,0 +1 @@ +a65a6755e2e5f5c160e7be2be814019e2d5491b1 diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/IDnsResolver.aidl new file mode 100644 index 00000000..6b539c47 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/IDnsResolver.aidl @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface IDnsResolver { + boolean isAlive(); + void registerEventListener(android.net.metrics.INetdEventListener listener); + void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams); + void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count); + void startPrefix64Discovery(int netId); + void stopPrefix64Discovery(int netId); + @utf8InCpp String getPrefix64(int netId); + void createNetworkCache(int netId); + void destroyNetworkCache(int netId); + void setLogSeverity(int logSeverity); + void flushNetworkCache(int netId); + void setPrefix64(int netId, @utf8InCpp String prefix); + void registerUnsolicitedEventListener(android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener listener); + void setResolverOptions(int netId, in android.net.ResolverOptionsParcel optionParams); + const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0; + const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1; + const int RESOLVER_PARAMS_MIN_SAMPLES = 2; + const int RESOLVER_PARAMS_MAX_SAMPLES = 3; + const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4; + const int RESOLVER_PARAMS_RETRY_COUNT = 5; + const int RESOLVER_PARAMS_COUNT = 6; + const int RESOLVER_STATS_SUCCESSES = 0; + const int RESOLVER_STATS_ERRORS = 1; + const int RESOLVER_STATS_TIMEOUTS = 2; + const int RESOLVER_STATS_INTERNAL_ERRORS = 3; + const int RESOLVER_STATS_RTT_AVG = 4; + const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5; + const int RESOLVER_STATS_USABLE = 6; + const int RESOLVER_STATS_COUNT = 7; + const int DNS_RESOLVER_LOG_VERBOSE = 0; + const int DNS_RESOLVER_LOG_DEBUG = 1; + const int DNS_RESOLVER_LOG_INFO = 2; + const int DNS_RESOLVER_LOG_WARNING = 3; + const int DNS_RESOLVER_LOG_ERROR = 4; + const int TC_MODE_DEFAULT = 0; + const int TC_MODE_UDP_TCP = 1; + const int TRANSPORT_UNKNOWN = (-1) /* -1 */; + const int TRANSPORT_CELLULAR = 0; + const int TRANSPORT_WIFI = 1; + const int TRANSPORT_BLUETOOTH = 2; + const int TRANSPORT_ETHERNET = 3; + const int TRANSPORT_VPN = 4; + const int TRANSPORT_WIFI_AWARE = 5; + const int TRANSPORT_LOWPAN = 6; + const int TRANSPORT_TEST = 7; + const int TRANSPORT_USB = 8; + const int TRANSPORT_THREAD = 9; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverHostsParcel.aidl new file mode 100644 index 00000000..c24eb619 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverHostsParcel.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable ResolverHostsParcel { + @utf8InCpp String ipAddr; + @utf8InCpp String hostName = ""; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverOptionsParcel.aidl new file mode 100644 index 00000000..e806d040 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverOptionsParcel.aidl @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable ResolverOptionsParcel { + android.net.ResolverHostsParcel[] hosts = {}; + int tcMode = 0; + boolean enforceDnsUid = false; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverParamsParcel.aidl new file mode 100644 index 00000000..8a7bf997 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/ResolverParamsParcel.aidl @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable ResolverParamsParcel { + int netId; + int sampleValiditySeconds; + int successThreshold; + int minSamples; + int maxSamples; + int baseTimeoutMsec; + int retryCount; + @utf8InCpp String[] servers; + @utf8InCpp String[] domains; + @utf8InCpp String tlsName; + @utf8InCpp String[] tlsServers; + @utf8InCpp String[] tlsFingerprints = {}; + @utf8InCpp String caCertificate = ""; + int tlsConnectTimeoutMs = 0; + @nullable android.net.ResolverOptionsParcel resolverOptions; + int[] transportTypes = {}; + boolean meteredNetwork = false; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/DnsHealthEventParcel.aidl new file mode 100644 index 00000000..d32be919 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/DnsHealthEventParcel.aidl @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable DnsHealthEventParcel { + int netId; + int healthResult; + int[] successRttMicros; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl new file mode 100644 index 00000000..32963dfd --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +interface IDnsResolverUnsolicitedEventListener { + oneway void onDnsHealthEvent(in android.net.resolv.aidl.DnsHealthEventParcel dnsHealthEvent); + oneway void onNat64PrefixEvent(in android.net.resolv.aidl.Nat64PrefixEventParcel nat64PrefixEvent); + oneway void onPrivateDnsValidationEvent(in android.net.resolv.aidl.PrivateDnsValidationEventParcel privateDnsValidationEvent); + const int DNS_HEALTH_RESULT_OK = 0; + const int DNS_HEALTH_RESULT_TIMEOUT = 255; + const int PREFIX_OPERATION_ADDED = 1; + const int PREFIX_OPERATION_REMOVED = 2; + const int VALIDATION_RESULT_SUCCESS = 1; + const int VALIDATION_RESULT_FAILURE = 2; + const int PROTOCOL_DOT = 1; + const int PROTOCOL_DOH = 2; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl new file mode 100644 index 00000000..2daccb0e --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable Nat64PrefixEventParcel { + int netId; + int prefixOperation; + @utf8InCpp String prefixAddress; + int prefixLength; +} diff --git a/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl new file mode 100644 index 00000000..f3bfbc76 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/12/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable PrivateDnsValidationEventParcel { + int netId; + @utf8InCpp String ipAddress; + @utf8InCpp String hostname; + int validation; + int protocol; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/.hash b/aidl_api/dnsresolver_aidl_interface/13/.hash new file mode 100644 index 00000000..6b78f7b1 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/.hash @@ -0,0 +1 @@ +a1e64ca6e3e9aafead71a3415d939207725d39d5 diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/IDnsResolver.aidl new file mode 100644 index 00000000..6b539c47 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/IDnsResolver.aidl @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface IDnsResolver { + boolean isAlive(); + void registerEventListener(android.net.metrics.INetdEventListener listener); + void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams); + void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count); + void startPrefix64Discovery(int netId); + void stopPrefix64Discovery(int netId); + @utf8InCpp String getPrefix64(int netId); + void createNetworkCache(int netId); + void destroyNetworkCache(int netId); + void setLogSeverity(int logSeverity); + void flushNetworkCache(int netId); + void setPrefix64(int netId, @utf8InCpp String prefix); + void registerUnsolicitedEventListener(android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener listener); + void setResolverOptions(int netId, in android.net.ResolverOptionsParcel optionParams); + const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0; + const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1; + const int RESOLVER_PARAMS_MIN_SAMPLES = 2; + const int RESOLVER_PARAMS_MAX_SAMPLES = 3; + const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4; + const int RESOLVER_PARAMS_RETRY_COUNT = 5; + const int RESOLVER_PARAMS_COUNT = 6; + const int RESOLVER_STATS_SUCCESSES = 0; + const int RESOLVER_STATS_ERRORS = 1; + const int RESOLVER_STATS_TIMEOUTS = 2; + const int RESOLVER_STATS_INTERNAL_ERRORS = 3; + const int RESOLVER_STATS_RTT_AVG = 4; + const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5; + const int RESOLVER_STATS_USABLE = 6; + const int RESOLVER_STATS_COUNT = 7; + const int DNS_RESOLVER_LOG_VERBOSE = 0; + const int DNS_RESOLVER_LOG_DEBUG = 1; + const int DNS_RESOLVER_LOG_INFO = 2; + const int DNS_RESOLVER_LOG_WARNING = 3; + const int DNS_RESOLVER_LOG_ERROR = 4; + const int TC_MODE_DEFAULT = 0; + const int TC_MODE_UDP_TCP = 1; + const int TRANSPORT_UNKNOWN = (-1) /* -1 */; + const int TRANSPORT_CELLULAR = 0; + const int TRANSPORT_WIFI = 1; + const int TRANSPORT_BLUETOOTH = 2; + const int TRANSPORT_ETHERNET = 3; + const int TRANSPORT_VPN = 4; + const int TRANSPORT_WIFI_AWARE = 5; + const int TRANSPORT_LOWPAN = 6; + const int TRANSPORT_TEST = 7; + const int TRANSPORT_USB = 8; + const int TRANSPORT_THREAD = 9; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverHostsParcel.aidl new file mode 100644 index 00000000..2a1c748f --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverHostsParcel.aidl @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true) +parcelable ResolverHostsParcel { + @utf8InCpp String ipAddr; + @utf8InCpp String hostName = ""; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverOptionsParcel.aidl new file mode 100644 index 00000000..b07263f8 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverOptionsParcel.aidl @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) +parcelable ResolverOptionsParcel { + android.net.ResolverHostsParcel[] hosts = {}; + int tcMode = 0; + boolean enforceDnsUid = false; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverParamsParcel.aidl new file mode 100644 index 00000000..2dd93dd5 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/ResolverParamsParcel.aidl @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) +parcelable ResolverParamsParcel { + int netId; + int sampleValiditySeconds; + int successThreshold; + int minSamples; + int maxSamples; + int baseTimeoutMsec; + int retryCount; + @utf8InCpp String[] servers; + @utf8InCpp String[] domains; + @utf8InCpp String tlsName; + @utf8InCpp String[] tlsServers; + @utf8InCpp String[] tlsFingerprints = {}; + @utf8InCpp String caCertificate = ""; + int tlsConnectTimeoutMs = 0; + @nullable android.net.ResolverOptionsParcel resolverOptions; + int[] transportTypes = {}; + boolean meteredNetwork = false; + @nullable android.net.resolv.aidl.DohParamsParcel dohParams; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DnsHealthEventParcel.aidl new file mode 100644 index 00000000..d32be919 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DnsHealthEventParcel.aidl @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable DnsHealthEventParcel { + int netId; + int healthResult; + int[] successRttMicros; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DohParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DohParamsParcel.aidl new file mode 100644 index 00000000..ba1ea747 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/DohParamsParcel.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable DohParamsParcel { + String name = ""; + String[] ips = {}; + String dohpath = ""; + int port = (-1) /* -1 */; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl new file mode 100644 index 00000000..32963dfd --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +interface IDnsResolverUnsolicitedEventListener { + oneway void onDnsHealthEvent(in android.net.resolv.aidl.DnsHealthEventParcel dnsHealthEvent); + oneway void onNat64PrefixEvent(in android.net.resolv.aidl.Nat64PrefixEventParcel nat64PrefixEvent); + oneway void onPrivateDnsValidationEvent(in android.net.resolv.aidl.PrivateDnsValidationEventParcel privateDnsValidationEvent); + const int DNS_HEALTH_RESULT_OK = 0; + const int DNS_HEALTH_RESULT_TIMEOUT = 255; + const int PREFIX_OPERATION_ADDED = 1; + const int PREFIX_OPERATION_REMOVED = 2; + const int VALIDATION_RESULT_SUCCESS = 1; + const int VALIDATION_RESULT_FAILURE = 2; + const int PROTOCOL_DOT = 1; + const int PROTOCOL_DOH = 2; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl new file mode 100644 index 00000000..2daccb0e --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable Nat64PrefixEventParcel { + int netId; + int prefixOperation; + @utf8InCpp String prefixAddress; + int prefixLength; +} diff --git a/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl new file mode 100644 index 00000000..f3bfbc76 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/13/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(toString=true) +parcelable PrivateDnsValidationEventParcel { + int netId; + @utf8InCpp String ipAddress; + @utf8InCpp String hostname; + int validation; + int protocol; +} diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl index 19313e40..6b539c47 100644 --- a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl +++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl @@ -55,7 +55,7 @@ interface IDnsResolver { const int DNS_RESOLVER_LOG_ERROR = 4; const int TC_MODE_DEFAULT = 0; const int TC_MODE_UDP_TCP = 1; - const int TRANSPORT_UNKNOWN = -1; + const int TRANSPORT_UNKNOWN = (-1) /* -1 */; const int TRANSPORT_CELLULAR = 0; const int TRANSPORT_WIFI = 1; const int TRANSPORT_BLUETOOTH = 2; diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl index c24eb619..2a1c748f 100644 --- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl +++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl @@ -18,6 +18,7 @@ package android.net; /* @hide */ +@JavaDerive(equals=true) parcelable ResolverHostsParcel { @utf8InCpp String ipAddr; @utf8InCpp String hostName = ""; diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl index e806d040..b07263f8 100644 --- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl +++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl @@ -18,6 +18,7 @@ package android.net; /* @hide */ +@JavaDerive(equals=true, toString=true) parcelable ResolverOptionsParcel { android.net.ResolverHostsParcel[] hosts = {}; int tcMode = 0; diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl index 8d0bf75e..2dd93dd5 100644 --- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl +++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl @@ -18,6 +18,7 @@ package android.net; /* @hide */ +@JavaDerive(equals=true, toString=true) parcelable ResolverParamsParcel { int netId; int sampleValiditySeconds; @@ -35,4 +36,6 @@ parcelable ResolverParamsParcel { int tlsConnectTimeoutMs = 0; @nullable android.net.ResolverOptionsParcel resolverOptions; int[] transportTypes = {}; + boolean meteredNetwork = false; + @nullable android.net.resolv.aidl.DohParamsParcel dohParams; } diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DohParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DohParamsParcel.aidl new file mode 100644 index 00000000..ba1ea747 --- /dev/null +++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DohParamsParcel.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.resolv.aidl; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable DohParamsParcel { + String name = ""; + String[] ips = {}; + String dohpath = ""; + int port = (-1) /* -1 */; +} diff --git a/binder/android/net/ResolverHostsParcel.aidl b/binder/android/net/ResolverHostsParcel.aidl index 6b372b18..054fca79 100644 --- a/binder/android/net/ResolverHostsParcel.aidl +++ b/binder/android/net/ResolverHostsParcel.aidl @@ -23,6 +23,7 @@ package android.net; * * {@hide} */ +@JavaDerive(equals=true) parcelable ResolverHostsParcel { /** * The IPv4 or IPv6 address corresponding to |hostName| field. diff --git a/binder/android/net/ResolverOptionsParcel.aidl b/binder/android/net/ResolverOptionsParcel.aidl index bca9fb65..dd64cf8c 100644 --- a/binder/android/net/ResolverOptionsParcel.aidl +++ b/binder/android/net/ResolverOptionsParcel.aidl @@ -23,6 +23,7 @@ import android.net.ResolverHostsParcel; * * {@hide} */ +@JavaDerive(equals=true, toString=true) parcelable ResolverOptionsParcel { /** * An IP/hostname mapping table for DNS local lookup customization. diff --git a/binder/android/net/ResolverParamsParcel.aidl b/binder/android/net/ResolverParamsParcel.aidl index 5511f281..1aee26bd 100644 --- a/binder/android/net/ResolverParamsParcel.aidl +++ b/binder/android/net/ResolverParamsParcel.aidl @@ -17,12 +17,14 @@ package android.net; import android.net.ResolverOptionsParcel; +import android.net.resolv.aidl.DohParamsParcel; /** * Configuration for a resolver parameters. * * {@hide} */ +@JavaDerive(equals=true, toString=true) parcelable ResolverParamsParcel { /** * The network ID of the network for which information should be configured. @@ -113,4 +115,15 @@ parcelable ResolverParamsParcel { * reasonable network type by DnsResolver, it would be considered as unknown. */ int[] transportTypes = {}; + + /** + * Whether the network is metered or not. + */ + boolean meteredNetwork = false; + + /** + * Information about DNS-over-HTTPS servers to use + */ + @nullable + DohParamsParcel dohParams; } diff --git a/binder/android/net/resolv/aidl/DohParamsParcel.aidl b/binder/android/net/resolv/aidl/DohParamsParcel.aidl new file mode 100644 index 00000000..d9c05db1 --- /dev/null +++ b/binder/android/net/resolv/aidl/DohParamsParcel.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.resolv.aidl; + +/** + * DNS-over-HTTPS configuration parameters. Represents a single DoH server. + * + * Note that although this parcelable is for DNS-over-HTTPS configuration parameters, there is + * no field in this parcelable to specify an exact HTTPS protocol (h2 or h3) because DnsResolver + * only supports DNS-over-HTTPS/3. The configuration parameters are for h3. + * + * {@hide} + */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable DohParamsParcel { + /** + * The server hostname. + */ + String name = ""; + + /** + * The server IP addresses. They are not sorted. + */ + String[] ips = {}; + + /** + * A part of the URI template used to construct the URL for DNS resolution. + * It's derived only from DNS SVCB SvcParamKey "dohpath". + */ + String dohpath = ""; + + /** + * The port used to reach the servers. + */ + int port = -1; +} diff --git a/doh/boot_time.rs b/doh/boot_time.rs index 453235ba..666a44de 100644 --- a/doh/boot_time.rs +++ b/doh/boot_time.rs @@ -57,8 +57,7 @@ impl BootTime { /// Gets a `BootTime` representing the current moment in time. pub fn now() -> BootTime { let mut t = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - // # Safety - // clock_gettime's only action will be to possibly write to the pointer provided, + // SAFETY: clock_gettime's only action will be to possibly write to the pointer provided, // and no borrows exist from that object other than the &mut used to construct the pointer // itself. if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut t as *mut libc::timespec) } != 0 @@ -93,9 +92,8 @@ struct TimerFd(RawFd); impl Drop for TimerFd { fn drop(&mut self) { - // # Safety - // The fd is owned by the TimerFd struct, and no memory access occurs as a result of this - // call. + // SAFETY: The fd is owned by the TimerFd struct, and no memory access occurs as a result of + // this call. unsafe { libc::close(self.0); } @@ -110,9 +108,8 @@ impl AsRawFd for TimerFd { impl TimerFd { fn create() -> io::Result<Self> { - // # Unsafe - // This libc call will either give us back a file descriptor or fail, it does not act on - // memory or resources. + // SAFETY: This libc call will either give us back a file descriptor or fail, it does not + // act on memory or resources. let raw = unsafe { libc::timerfd_create(libc::CLOCK_BOOTTIME, libc::TFD_NONBLOCK | libc::TFD_CLOEXEC) }; @@ -131,8 +128,7 @@ impl TimerFd { tv_nsec: duration.subsec_nanos().try_into().unwrap(), }, }; - // # Unsafe - // We own `timer` and there are no borrows to it other than the pointer we pass to + // SAFETY: We own `timer` and there are no borrows to it other than the pointer we pass to // timerfd_settime. timerfd_settime is explicitly documented to handle a null output // parameter for its fourth argument by not filling out the output. The fd passed in at // self.0 is owned by the `TimerFd` struct, so we aren't breaking anyone else's invariants. diff --git a/doh/connection/mod.rs b/doh/connection/mod.rs index f0b27d79..5898228c 100644 --- a/doh/connection/mod.rs +++ b/doh/connection/mod.rs @@ -61,7 +61,7 @@ fn new_scid() -> [u8; quiche::MAX_CONN_ID_LEN] { fn mark_socket(socket: &std::net::UdpSocket, socket_mark: u32) -> io::Result<()> { use std::os::unix::io::AsRawFd; let fd = socket.as_raw_fd(); - // libc::setsockopt is a wrapper function calling into bionic setsockopt. + // SAFETY: libc::setsockopt is a wrapper function calling into bionic setsockopt. // The only pointer being passed in is &socket_mark, which is valid by virtue of being a // reference, and the foreign function doesn't take ownership or a reference to that memory // after completion. @@ -35,8 +35,12 @@ use tokio::sync::oneshot; use tokio::task; use url::Url; -pub type ValidationCallback = - extern "C" fn(net_id: uint32_t, success: bool, ip_addr: *const c_char, host: *const c_char); +pub type ValidationCallback = unsafe extern "C" fn( + net_id: uint32_t, + success: bool, + ip_addr: *const c_char, + host: *const c_char, +); pub type TagSocketCallback = extern "C" fn(sock: RawFd); #[repr(C)] @@ -61,7 +65,9 @@ fn wrap_validation_callback(validation_fn: ValidationCallback) -> ValidationRepo } }; let netd_id = info.net_id; - task::spawn_blocking(move || { + // SAFETY: The string pointers are obtained from `CString`, so they must be valid C + // strings. + task::spawn_blocking(move || unsafe { validation_fn(netd_id, success, ip_addr.as_ptr(), domain.as_ptr()) }) .await @@ -167,12 +173,16 @@ pub extern "C" fn doh_dispatcher_new( } /// Deletes a DoH engine created by doh_dispatcher_new(). +/// /// # Safety +/// /// `doh` must be a non-null pointer previously created by `doh_dispatcher_new()` /// and not yet deleted by `doh_dispatcher_delete()`. #[no_mangle] pub unsafe extern "C" fn doh_dispatcher_delete(doh: *mut DohDispatcher) { - Box::from_raw(doh).lock().exit_handler() + // SAFETY: The caller guarantees that `doh` was created by `doh_dispatcher_new` (which does so + // using `Box::into_raw`), and that it hasn't yet been deleted by this function. + unsafe { Box::from_raw(doh) }.lock().exit_handler() } /// Probes and stores the DoH server with the given configurations. @@ -194,12 +204,15 @@ pub unsafe extern "C" fn doh_net_new( network_type: uint32_t, private_dns_mode: uint32_t, ) -> int32_t { - let (url, domain, ip_addr, cert_path) = match ( - std::ffi::CStr::from_ptr(url).to_str(), - std::ffi::CStr::from_ptr(domain).to_str(), - std::ffi::CStr::from_ptr(ip_addr).to_str(), - std::ffi::CStr::from_ptr(cert_path).to_str(), - ) { + // SAFETY: The caller guarantees that these are all valid nul-terminated C strings. + let (url, domain, ip_addr, cert_path) = match unsafe { + ( + std::ffi::CStr::from_ptr(url).to_str(), + std::ffi::CStr::from_ptr(domain).to_str(), + std::ffi::CStr::from_ptr(ip_addr).to_str(), + std::ffi::CStr::from_ptr(cert_path).to_str(), + ) + } { (Ok(url), Ok(domain), Ok(ip_addr), Ok(cert_path)) => { if domain.is_empty() { (url, None, ip_addr.to_string(), None) @@ -268,7 +281,9 @@ pub unsafe extern "C" fn doh_query( response_len: size_t, timeout_ms: uint64_t, ) -> ssize_t { - let q = slice::from_raw_parts_mut(dns_query, dns_query_len); + // SAFETY: The caller guarantees that `dns_query` is a valid pointer to a buffer of at least + // `dns_query_len` items. + let q = unsafe { slice::from_raw_parts_mut(dns_query, dns_query_len) }; let (resp_tx, resp_rx) = oneshot::channel(); let t = Duration::from_millis(timeout_ms); @@ -298,7 +313,10 @@ pub unsafe extern "C" fn doh_query( if answer.len() > response_len || answer.len() > isize::MAX as usize { return DOH_RESULT_INTERNAL_ERROR; } - let response = slice::from_raw_parts_mut(response, answer.len()); + // SAFETY: The caller guarantees that response points to a valid buffer at + // least `response_len` long, and we just checked that `answer.len()` is no + // longer than `response_len`. + let response = unsafe { slice::from_raw_parts_mut(response, answer.len()) }; response.copy_from_slice(&answer); answer.len() as ssize_t } @@ -341,25 +359,27 @@ mod tests { const LOOPBACK_ADDR: &str = "127.0.0.1:443"; const LOCALHOST_URL: &str = "https://mylocal.com/dns-query"; - extern "C" fn success_cb( + unsafe extern "C" fn success_cb( net_id: uint32_t, success: bool, ip_addr: *const c_char, host: *const c_char, ) { assert!(success); + // SAFETY: The caller guarantees that ip_addr and host are valid nul-terminated C strings. unsafe { assert_validation_info(net_id, ip_addr, host); } } - extern "C" fn fail_cb( + unsafe extern "C" fn fail_cb( net_id: uint32_t, success: bool, ip_addr: *const c_char, host: *const c_char, ) { assert!(!success); + // SAFETY: The caller guarantees that ip_addr and host are valid nul-terminated C strings. unsafe { assert_validation_info(net_id, ip_addr, host); } @@ -373,10 +393,12 @@ mod tests { host: *const c_char, ) { assert_eq!(net_id, TEST_NET_ID); - let ip_addr = std::ffi::CStr::from_ptr(ip_addr).to_str().unwrap(); + // SAFETY: The caller guarantees that `ip_addr` is a valid nul-terminated C string. + let ip_addr = unsafe { std::ffi::CStr::from_ptr(ip_addr) }.to_str().unwrap(); let expected_addr: SocketAddr = LOOPBACK_ADDR.parse().unwrap(); assert_eq!(ip_addr, expected_addr.ip().to_string()); - let host = std::ffi::CStr::from_ptr(host).to_str().unwrap(); + // SAFETY: The caller guarantees that `host` is a valid nul-terminated C string. + let host = unsafe { std::ffi::CStr::from_ptr(host) }.to_str().unwrap(); assert_eq!(host, ""); } diff --git a/doh/tests/doh_frontend/src/dns_https_frontend.rs b/doh/tests/doh_frontend/src/dns_https_frontend.rs index b7d11b7d..d3c538d4 100644 --- a/doh/tests/doh_frontend/src/dns_https_frontend.rs +++ b/doh/tests/doh_frontend/src/dns_https_frontend.rs @@ -477,6 +477,9 @@ fn into_tokio_udp_socket(socket: std::net::UdpSocket) -> Result<UdpSocket> { fn build_pipe() -> Result<(File, File)> { let mut fds = [0, 0]; + // SAFETY: The pointer we pass to `pipe` must be valid because it comes from a reference. The + // file descriptors it returns must be valid and open, so they are safe to pass to + // `File::from_raw_fd`. unsafe { if libc::pipe(fds.as_mut_ptr()) == 0 { return Ok((File::from_raw_fd(fds[0]), File::from_raw_fd(fds[1]))); diff --git a/doh/tests/doh_frontend/src/ffi.rs b/doh/tests/doh_frontend/src/ffi.rs index 3235d5da..37a5fba0 100644 --- a/doh/tests/doh_frontend/src/ffi.rs +++ b/doh/tests/doh_frontend/src/ffi.rs @@ -38,10 +38,14 @@ pub unsafe extern "C" fn frontend_new( backend_addr: *const c_char, backend_port: *const c_char, ) -> *mut DohFrontend { - let addr = CStr::from_ptr(addr).to_str().unwrap(); - let port = CStr::from_ptr(port).to_str().unwrap(); - let backend_addr = CStr::from_ptr(backend_addr).to_str().unwrap(); - let backend_port = CStr::from_ptr(backend_port).to_str().unwrap(); + // SAFETY: Our caller promises that addr is a valid C string. + let addr = unsafe { CStr::from_ptr(addr) }.to_str().unwrap(); + // SAFETY: Our caller promises that port is a valid C string. + let port = unsafe { CStr::from_ptr(port) }.to_str().unwrap(); + // SAFETY: Our caller promises that backend_addr is a valid C string. + let backend_addr = unsafe { CStr::from_ptr(backend_addr) }.to_str().unwrap(); + // SAFETY: Our caller promises that backend_port is a valid C string. + let backend_port = unsafe { CStr::from_ptr(backend_port) }.to_str().unwrap(); let socket_addr = to_socket_addr(addr, port).or_else(logging_and_return_err); let backend_socket_addr = @@ -73,13 +77,19 @@ pub extern "C" fn frontend_stop(doh: &mut DohFrontend) -> bool { /// If the caller has called `frontend_start` to start `DohFrontend`, it has to call /// call `frontend_stop` to stop the worker thread before deleting the object. /// +/// The DohFrontend is not set to null pointer, caller needs to do it on its own. +/// /// # Safety /// -/// The DohFrontend is not set to null pointer, caller needs to do it on its own. +/// `doh` must be a pointer either null or previously returned by `frontend_new`, and not yet passed +/// to `frontend_delete`. #[no_mangle] pub unsafe extern "C" fn frontend_delete(doh: *mut DohFrontend) { if !doh.is_null() { - drop(Box::from_raw(doh)); + // SAFETY: Our caller promised that `doh` was either null or previously returned by + // `frontend_new`. We just checked that it's not null, so it must have been returned by + // `frontend_new`, which obtained it from `Box::into_raw`. + drop(unsafe { Box::from_raw(doh) }); } } @@ -96,7 +106,8 @@ pub unsafe extern "C" fn frontend_set_certificate( if certificate.is_null() { return false; } - let certificate = CStr::from_ptr(certificate).to_str().unwrap(); + // SAFETY: Our caller promises that certificate is a valid C string. + let certificate = unsafe { CStr::from_ptr(certificate) }.to_str().unwrap(); doh.set_certificate(certificate).or_else(logging_and_return_err).is_ok() } @@ -113,7 +124,8 @@ pub unsafe extern "C" fn frontend_set_private_key( if private_key.is_null() { return false; } - let private_key = CStr::from_ptr(private_key).to_str().unwrap(); + // SAFETY: Our caller promises that private_key is a valid C string. + let private_key = unsafe { CStr::from_ptr(private_key) }.to_str().unwrap(); doh.set_private_key(private_key).or_else(logging_and_return_err).is_ok() } diff --git a/getaddrinfo.cpp b/getaddrinfo.cpp index 6bae41d7..8d9ed6d4 100644 --- a/getaddrinfo.cpp +++ b/getaddrinfo.cpp @@ -1310,8 +1310,7 @@ static int _find_src_addr(const struct sockaddr* addr, struct sockaddr* src_addr return -1; } - if (Experiments::getInstance()->getFlag("skip_4a_query_on_v6_linklocal_addr", 1) && - src_addr->sa_family == AF_INET6) { + if (src_addr->sa_family == AF_INET6) { sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(src_addr); if (!allow_v6_linklocal && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { // There is no point in sending an AAAA query because the device does not have a global @@ -1637,7 +1636,8 @@ QueryResult doQuery(const char* name, res_target* t, ResState* res, ResState res_temp = res->clone(&event); int rcode = NOERROR; - n = res_nsend(&res_temp, {buf, n}, {t->answer.data(), anslen}, &rcode, 0, sleepTimeMs); + n = res_nsend(&res_temp, std::span(buf, n), std::span(t->answer.data(), anslen), &rcode, 0, + sleepTimeMs); if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { if (rcode != RCODE_TIMEOUT) rcode = hp->rcode; // if the query choked with EDNS0, retry without EDNS0 @@ -1646,7 +1646,8 @@ QueryResult doQuery(const char* name, res_target* t, ResState* res, (res_temp.flags & RES_F_EDNS0ERR)) { LOG(INFO) << __func__ << ": retry without EDNS0"; n = res_nmkquery(QUERY, name, cl, type, {}, buf, res_temp.netcontext_flags); - n = res_nsend(&res_temp, {buf, n}, {t->answer.data(), anslen}, &rcode, 0); + n = res_nsend(&res_temp, std::span(buf, n), std::span(t->answer.data(), anslen), &rcode, + 0); } } diff --git a/res_cache.cpp b/res_cache.cpp index bf0abdb9..fbe426c8 100644 --- a/res_cache.cpp +++ b/res_cache.cpp @@ -154,7 +154,7 @@ using std::span; * ***************************************** */ const int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; -const int MAX_ENTRIES_LOWER_BOUND = 0; +const int MAX_ENTRIES_LOWER_BOUND = 1; const int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1; @@ -1065,6 +1065,7 @@ struct NetConfig { int tc_mode = aidl::android::net::IDnsResolver::TC_MODE_DEFAULT; bool enforceDnsUid = false; std::vector<int32_t> transportTypes; + bool metered = false; }; /* gets cache associated with a network, or NULL if none exists */ @@ -1214,7 +1215,7 @@ static void _cache_remove_oldest(Cache* cache) { return; } LOG(DEBUG) << __func__ << ": Cache full - removing oldest"; - res_pquery({oldest->query, oldest->querylen}); + res_pquery(std::span(oldest->query, oldest->querylen)); _cache_remove_p(cache, lookup); } @@ -1318,13 +1319,13 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, span<const uint8_t> query, /* remove stale entries here */ if (now >= e->expires) { LOG(DEBUG) << __func__ << ": NOT IN CACHE (STALE ENTRY " << *lookup << "DISCARDED)"; - res_pquery({e->query, e->querylen}); + res_pquery(std::span(e->query, e->querylen)); _cache_remove_p(cache, lookup); return RESOLV_CACHE_NOTFOUND; } *answerlen = e->answerlen; - if (e->answerlen > answer.size()) { + if (e->answerlen > static_cast<ptrdiff_t>(answer.size())) { /* NOTE: we return UNSUPPORTED if the answer buffer is too short */ LOG(INFO) << __func__ << ": ANSWER TOO LONG"; return RESOLV_CACHE_UNSUPPORTED; @@ -1642,7 +1643,7 @@ std::vector<std::string> getCustomizedTableByName(const size_t netid, const char int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers, const std::vector<std::string>& domains, const res_params& params, const std::optional<ResolverOptionsParcel> optionalResolverOptions, - const std::vector<int32_t>& transportTypes) { + const std::vector<int32_t>& transportTypes, bool metered) { std::vector<std::string> nameservers = filter_nameservers(servers); const int numservers = static_cast<int>(nameservers.size()); @@ -1709,6 +1710,7 @@ int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& serve return -EINVAL; } netconfig->transportTypes = transportTypes; + netconfig->metered = metered; if (optionalResolverOptions.has_value()) { const ResolverOptionsParcel& resolverOptions = optionalResolverOptions.value(); return netconfig->setOptions(resolverOptions); @@ -2090,6 +2092,7 @@ void resolv_netconfig_dump(DumpWriter& dw, unsigned netid) { // TODO: dump info->hosts dw.println("TC mode: %s", tc_mode_to_str(info->tc_mode)); dw.println("TransportType: %s", transport_type_to_str(info->transportTypes)); + dw.println("Metered: %s", info->metered ? "true" : "false"); } } @@ -2101,4 +2104,20 @@ int resolv_get_max_cache_entries(unsigned netid) { return -1; } return info->cache->get_max_cache_entries(); -}
\ No newline at end of file +} + +bool resolv_is_enforceDnsUid_enabled_network(unsigned netid) { + std::lock_guard guard(cache_mutex); + if (const auto info = find_netconfig_locked(netid); info != nullptr) { + return info->enforceDnsUid; + } + return false; +} + +bool resolv_is_metered_network(unsigned netid) { + std::lock_guard guard(cache_mutex); + if (const auto info = find_netconfig_locked(netid); info != nullptr) { + return info->metered; + } + return false; +} diff --git a/res_debug.cpp b/res_debug.cpp index 848fed59..55356fea 100644 --- a/res_debug.cpp +++ b/res_debug.cpp @@ -166,7 +166,7 @@ static void do_section(ns_msg* handle, ns_sect section) { rdatalen = ns_rr_rdlen(rr); format_to(out, "; EDNS: version: {}, udp={}, flags={}\n", (rr.ttl >> 16) & 0xff, - ns_rr_class(rr), rr.ttl & 0xffff); + static_cast<int>(ns_rr_class(rr)), rr.ttl & 0xffff); const uint8_t* cp = ns_rr_rdata(rr); while (rdatalen <= ns_rr_rdlen(rr) && rdatalen >= 4) { int i; diff --git a/res_mkquery.cpp b/res_mkquery.cpp index 11fbfdb7..24cee30e 100644 --- a/res_mkquery.cpp +++ b/res_mkquery.cpp @@ -166,7 +166,7 @@ int res_nmkquery(int op, // opcode of query /* * Initialize answer section */ - if (ep - cp < 1 + RRFIXEDSZ + data.size()) return (-1); + if (ep - cp < static_cast<ptrdiff_t>(1 + RRFIXEDSZ + data.size())) return (-1); *cp++ = '\0'; /* no domain name */ *reinterpret_cast<uint16_t*>(cp) = htons(type); cp += INT16SZ; diff --git a/res_query.cpp b/res_query.cpp index 036a6e31..be176b6c 100644 --- a/res_query.cpp +++ b/res_query.cpp @@ -126,7 +126,7 @@ again: *herrno = NO_RECOVERY; return n; } - n = res_nsend(statp, {buf, n}, answer, &rcode, 0); + n = res_nsend(statp, std::span(buf, n), answer, &rcode, 0); if (n < 0) { // If the query choked with EDNS0, retry without EDNS0 that when the server // has no response, resovler won't retry and do nothing. Even fallback to UDP, diff --git a/res_send.cpp b/res_send.cpp index 38b9c6ac..554ace6f 100644 --- a/res_send.cpp +++ b/res_send.cpp @@ -491,7 +491,7 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* res_pquery(ans.first(resplen)); if (cache_status == RESOLV_CACHE_NOTFOUND) { - resolv_cache_add(statp->netid, msg, {ans.data(), resplen}); + resolv_cache_add(statp->netid, msg, std::span(ans.data(), resplen)); } return resplen; } @@ -667,7 +667,7 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* res_pquery(ans.first(resplen)); if (cache_status == RESOLV_CACHE_NOTFOUND) { - resolv_cache_add(statp->netid, msg, {ans.data(), resplen}); + resolv_cache_add(statp->netid, msg, std::span(ans.data(), resplen)); } statp->closeSockets(); return (resplen); @@ -807,7 +807,7 @@ same_ns: {.iov_base = const_cast<uint8_t*>(msg.data()), .iov_len = static_cast<size_t>(msg.size())}, }; - if (writev(statp->tcp_nssock, iov, 2) != (INT16SZ + msg.size())) { + if (writev(statp->tcp_nssock, iov, 2) != static_cast<ptrdiff_t>(INT16SZ + msg.size())) { *terrno = errno; PLOG(DEBUG) << __func__ << ": write failed: "; statp->closeSockets(); @@ -1111,7 +1111,8 @@ static int send_dg(ResState* statp, res_params* params, span<const uint8_t> msg, } LOG(DEBUG) << __func__ << ": new DG socket"; } - if (send(statp->udpsocks[*ns], msg.data(), msg.size(), 0) != msg.size()) { + if (send(statp->udpsocks[*ns], msg.data(), msg.size(), 0) != + static_cast<ptrdiff_t>(msg.size())) { *terrno = errno; PLOG(DEBUG) << __func__ << ": send: "; statp->closeSockets(); @@ -1155,11 +1156,14 @@ static int send_dg(ResState* statp, res_params* params, span<const uint8_t> msg, *terrno = EMSGSIZE; continue; } + if (resplen > static_cast<ptrdiff_t>(ans.size())) { + LOG(FATAL) << __func__ << ": invalid resplen (too large): " << resplen; + } int receivedFromNs = *ns; if (needRetry = ignoreInvalidAnswer(statp, from, msg, ans, &receivedFromNs); needRetry) { - res_pquery({ans.data(), (resplen > ans.size()) ? ans.size() : resplen}); + res_pquery(ans.first(resplen)); continue; } @@ -1169,7 +1173,7 @@ static int send_dg(ResState* statp, res_params* params, span<const uint8_t> msg, // The case has to be captured here, as FORMERR packet do not // carry query section, hence res_queriesmatch() returns 0. LOG(DEBUG) << __func__ << ": server rejected query with EDNS0:"; - res_pquery({ans.data(), (resplen > ans.size()) ? ans.size() : resplen}); + res_pquery(ans.first(resplen)); // record the error statp->flags |= RES_F_EDNS0ERR; *terrno = EREMOTEIO; @@ -1178,7 +1182,7 @@ static int send_dg(ResState* statp, res_params* params, span<const uint8_t> msg, if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) { LOG(DEBUG) << __func__ << ": server rejected query:"; - res_pquery({ans.data(), (resplen > ans.size()) ? ans.size() : resplen}); + res_pquery(ans.first(resplen)); *rcode = anhp->rcode; continue; } @@ -1212,7 +1216,8 @@ static int send_mdns(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans if (setupUdpSocket(statp, mdnsap, &fd, terrno) <= 0) return 0; - if (sendto(fd, msg.data(), msg.size(), 0, mdnsap, sockaddrSize(mdnsap)) != msg.size()) { + if (sendto(fd, msg.data(), msg.size(), 0, mdnsap, sockaddrSize(mdnsap)) != + static_cast<ptrdiff_t>(msg.size())) { *terrno = errno; return 0; } diff --git a/resolv_cache.h b/resolv_cache.h index 07697666..18f5b117 100644 --- a/resolv_cache.h +++ b/resolv_cache.h @@ -81,7 +81,7 @@ std::vector<std::string> getCustomizedTableByName(const size_t netid, const char int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers, const std::vector<std::string>& domains, const res_params& params, std::optional<aidl::android::net::ResolverOptionsParcel> resolverOptions, - const std::vector<int32_t>& transportTypes = {}); + const std::vector<int32_t>& transportTypes = {}, bool metered = false); // Sets options for a given network. int resolv_set_options(unsigned netid, const aidl::android::net::ResolverOptionsParcel& options); @@ -143,3 +143,9 @@ void resolv_netconfig_dump(android::netdutils::DumpWriter& dw, unsigned netid); // Get the maximum cache size of a network. // Return positive value on success, -1 on failure. int resolv_get_max_cache_entries(unsigned netid); + +// Return true if the enforceDnsUid is enabled on the network. +bool resolv_is_enforceDnsUid_enabled_network(unsigned netid); + +// Return true if the network is metered. +bool resolv_is_metered_network(unsigned netid); diff --git a/sethostent.cpp b/sethostent.cpp index a9b0de6e..55b8c8bc 100644 --- a/sethostent.cpp +++ b/sethostent.cpp @@ -69,6 +69,8 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) { char* aliases[MAXALIASES]; char* addr_ptrs[MAXADDRS]; + // TODO: Wrap the 'hf' into a RAII class or std::shared_ptr and modify the + // sethostent_r()/endhostent_r() to get rid of manually endhostent_r(&hf) everywhere. FILE* hf = NULL; sethostent_r(&hf); if (hf == NULL) { @@ -80,6 +82,7 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) { } if ((ptr = buf = (char*) malloc(len = info->buflen)) == NULL) { + endhostent_r(&hf); return EAI_MEMORY; } @@ -103,6 +106,7 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) { if (hp->h_name == nullptr) { free(buf); + endhostent_r(&hf); return EAI_FAIL; } const char* h_name = hp->h_name; @@ -131,6 +135,7 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) { if (num >= MAXADDRS) goto nospc; if (hp->h_addr_list[0] == nullptr) { free(buf); + endhostent_r(&hf); return EAI_FAIL; } const char* addr = hp->h_addr_list[0]; @@ -185,6 +190,7 @@ int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) { free(buf); return 0; nospc: + endhostent_r(&hf); free(buf); return EAI_MEMORY; } diff --git a/stats.proto b/stats.proto index 4052ec7d..1356b7fc 100644 --- a/stats.proto +++ b/stats.proto @@ -412,6 +412,9 @@ message NetworkDnsEventReported { // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom. optional int32 sampling_rate_denom = 9; + + // UID sends the DNS query. + optional int32 uid = 10; } enum HandshakeResult { diff --git a/tests/Android.bp b/tests/Android.bp index 72fb008b..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 @@ -271,6 +274,7 @@ cc_test { "libstatslog_resolv", "libstatspush_compat", "libsysutils", + "libutils", "resolv_stats_test_utils", "server_configurable_flags", "stats_proto", @@ -377,6 +381,18 @@ cc_defaults { "server_configurable_flags", "stats_proto", ], + target: { + android: { + shared_libs: [ + "libutils", + ], + }, + host: { + static_libs: [ + "libutils", + ], + }, + }, fuzz_config: { cc: [ "cken@google.com", @@ -421,14 +437,12 @@ cc_fuzz { shared_libs: [ "libbinder_ndk", "libbinder", - "libutils", ], }, host: { static_libs: [ "libbinder_ndk", "libbinder", - "libutils", ], }, darwin: { 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 49ccab87..ef9ecb30 100644 --- a/tests/resolv_cache_unit_test.cpp +++ b/tests/resolv_cache_unit_test.cpp @@ -50,7 +50,7 @@ constexpr int DNS_PORT = 53; // Constant values sync'd from res_cache.cpp constexpr int DNS_HEADER_SIZE = 12; constexpr int MAX_ENTRIES_DEFAULT = 64 * 2 * 5; -constexpr int MAX_ENTRIES_LOWER_BOUND = 0; +constexpr int MAX_ENTRIES_LOWER_BOUND = 1; constexpr int MAX_ENTRIES_UPPER_BOUND = 100 * 1000; namespace { @@ -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, @@ -630,9 +631,6 @@ class ResolvCacheParameterizedTest : public ResolvCacheTest, INSTANTIATE_TEST_SUITE_P(MaxCacheEntries, ResolvCacheParameterizedTest, testing::Values(MAX_ENTRIES_LOWER_BOUND - 1, MAX_ENTRIES_UPPER_BOUND + 1), [](const testing::TestParamInfo<int>& info) { - if (info.param < 0) { // '-' is an invalid character in test name - return "negative_" + std::to_string(abs(info.param)); - } return std::to_string(info.param); }); @@ -931,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 e4f26fad..ef2bf1e5 100644 --- a/tests/resolv_integration_test.cpp +++ b/tests/resolv_integration_test.cpp @@ -48,6 +48,7 @@ #include <algorithm> #include <chrono> +#include <functional> #include <iterator> #include <numeric> #include <string_view> @@ -126,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, @@ -859,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]() { @@ -883,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)); @@ -894,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) @@ -4098,54 +4103,46 @@ TEST_F(ResolverTest, GetHostByName2_Dns64QuerySpecialUseIPv4Addresses) { TEST_F(ResolverTest, PrefixDiscoveryBypassTls) { constexpr char listen_addr[] = "::1"; - constexpr char cleartext_port[] = "53"; - constexpr char tls_port[] = "853"; constexpr char dns64_name[] = "ipv4only.arpa."; const std::vector<std::string> servers = {listen_addr}; test::DNSResponder dns(listen_addr); StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}}); - test::DnsTlsFrontend tls(listen_addr, tls_port, listen_addr, cleartext_port); + test::DnsTlsFrontend tls(listen_addr, "853", listen_addr, "53"); ASSERT_TRUE(tls.startServer()); - // Setup OPPORTUNISTIC mode and wait for the validation complete. - ASSERT_TRUE(mDnsClient.SetResolversFromParcel( - ResolverParams::Builder().setDnsServers(servers).setDotServers(servers).build())); - EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true)); - EXPECT_TRUE(tls.waitForQueries(1)); - tls.clearQueries(); + for (const std::string_view dnsMode : {"OPPORTUNISTIC", "STRICT"}) { + SCOPED_TRACE(fmt::format("testConfig: [{}]", dnsMode)); + auto builder = ResolverParams::Builder().setDnsServers(servers).setDotServers(servers); + if (dnsMode == "STRICT") { + builder.setPrivateDnsProvider(kDefaultPrivateDnsHostName); + } + ASSERT_TRUE(mDnsClient.SetResolversFromParcel(builder.build())); + EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true)); + EXPECT_TRUE(tls.waitForQueries(1)); + tls.clearQueries(); - // Start NAT64 prefix discovery and wait for it complete. - EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk()); - EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND)); + // Start NAT64 prefix discovery. + EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk()); + EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND)); - // Verify it bypassed TLS even though there's a TLS server available. - EXPECT_EQ(0, tls.queries()) << dns.dumpQueries(); - EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries(); + // Verify that the DNS query for the NAT64 prefix bypassed private DNS. + EXPECT_EQ(0, tls.queries()) << dns.dumpQueries(); + EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries(); - // Restart the testing network to reset the cache. - mDnsClient.TearDown(); - mDnsClient.SetUp(); - dns.clearQueries(); + // Stop the prefix discovery to make DnsResolver send the prefix-removed event + // earlier. Without this, DnsResolver still sends the event once the network + // is destroyed; however, it will fail the next test if the test unexpectedly + // receives the event that it doesn't want. + EXPECT_TRUE(mDnsClient.resolvService()->stopPrefix64Discovery(TEST_NETID).isOk()); + EXPECT_TRUE(WaitForNat64Prefix(EXPECT_NOT_FOUND)); - // Setup STRICT mode and wait for the validation complete. - ASSERT_TRUE(mDnsClient.SetResolversFromParcel( - ResolverParams::Builder() - .setDnsServers(servers) - .setDotServers(servers) - .setPrivateDnsProvider(kDefaultPrivateDnsHostName) - .build())); - EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true)); - EXPECT_TRUE(tls.waitForQueries(1)); - tls.clearQueries(); - - // Start NAT64 prefix discovery and wait for it to complete. - EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk()); - EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND)); + dns.clearQueries(); + EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk()); + } - // Verify it bypassed TLS despite STRICT mode. - EXPECT_EQ(0, tls.queries()) << dns.dumpQueries(); - EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries(); + EXPECT_EQ(0, sDnsMetricsListener->getUnexpectedNat64PrefixUpdates()); + EXPECT_EQ(0, sUnsolicitedEventListener->getUnexpectedNat64PrefixUpdates()); } TEST_F(ResolverTest, SetAndClearNat64Prefix) { @@ -4510,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(); @@ -4830,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)); } } @@ -5635,12 +5631,11 @@ TEST_F(ResolverTest, RepeatedSetup_NoRedundantPrivateDnsValidation) { parcel.tlsServers = {addr1, addr2, unusable_addr}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - // Check the validation results. + // Check the validation status before proceed. The validation for `unresponsiveTls` + // should be running, and the other two should be finished. EXPECT_TRUE(WaitForPrivateDnsValidation(workableTls.listen_address(), true)); EXPECT_TRUE(WaitForPrivateDnsValidation(unusable_addr, false)); - - // The validation is still in progress. - EXPECT_EQ(unresponsiveTls.acceptConnectionsCount(), 1); + EXPECT_TRUE(PollForCondition([&]() { return unresponsiveTls.acceptConnectionsCount() == 1; })); unresponsiveTls.clearConnectionsCount(); static const struct TestConfig { @@ -5716,7 +5711,8 @@ TEST_F(ResolverTest, RepeatedSetup_NoRedundantPrivateDnsValidation) { } if (validationAttemptToUnresponsiveTls) { - EXPECT_GT(unresponsiveTls.acceptConnectionsCount(), 0); + EXPECT_TRUE(PollForCondition( + [&]() { return unresponsiveTls.acceptConnectionsCount() > 0; })); } else { EXPECT_EQ(unresponsiveTls.acceptConnectionsCount(), 0); } @@ -5785,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()); @@ -7434,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); @@ -7566,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"); @@ -7686,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 = @@ -7816,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 @@ -7980,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 569b74af..c9b8f46c 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; @@ -202,11 +177,27 @@ class BaseTest : public NetNativeTestBase { IDnsResolverUnsolicitedEventListener::PROTOCOL_DOT); } + bool WaitForDotValidationSuccess(std::string serverAddr) { + return WaitForDotValidation(serverAddr, true); + } + + bool WaitForDotValidationFailure(std::string serverAddr) { + return WaitForDotValidation(serverAddr, false); + } + bool WaitForDohValidation(std::string serverAddr, bool validated) { return WaitForPrivateDnsValidation(serverAddr, validated, IDnsResolverUnsolicitedEventListener::PROTOCOL_DOH); } + bool WaitForDohValidationSuccess(std::string serverAddr) { + return WaitForDohValidation(serverAddr, true); + } + + bool WaitForDohValidationFailure(std::string serverAddr) { + return WaitForDohValidation(serverAddr, false); + } + bool WaitForPrivateDnsValidation(std::string serverAddr, bool validated, int protocol) { return sUnsolicitedEventListener->waitForPrivateDnsValidation( serverAddr, @@ -224,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 @@ -328,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."; @@ -399,8 +416,8 @@ TEST_P(TransportParameterizedTest, GetAddrInfo) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + 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(); @@ -467,8 +484,8 @@ TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) { auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + 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(); @@ -511,6 +528,128 @@ 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); + + // DataSaver information is only meaningful after V. + // TODO: Add 'else' to check that DNS queries are not blocked before V. + if (android::modules::sdklevel::IsAtLeastV()) { + 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 { @@ -542,8 +681,8 @@ TEST_F(PrivateDnsDohTest, ValidationFail) { Stopwatch s; const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, false)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, false)); + EXPECT_TRUE(WaitForDohValidationFailure(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationFailure(test::kDefaultListenAddr)); EXPECT_LT(s.getTimeAndResetUs(), microseconds(kExpectedDohValidationTimeWhenServerUnreachable + TIMING_TOLERANCE) .count()); @@ -555,8 +694,8 @@ TEST_F(PrivateDnsDohTest, ValidationFail) { s.getTimeAndResetUs(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, false)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, false)); + EXPECT_TRUE(WaitForDohValidationFailure(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationFailure(test::kDefaultListenAddr)); EXPECT_LT(s.getTimeAndResetUs(), microseconds(kExpectedDohValidationTimeWhenTimeout + TIMING_TOLERANCE).count()); @@ -575,8 +714,8 @@ TEST_F(PrivateDnsDohTest, QueryFailover) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); dns.clearQueries(); @@ -593,7 +732,7 @@ TEST_F(PrivateDnsDohTest, QueryFailover) { resetNetwork(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); dns.clearQueries(); @@ -640,7 +779,7 @@ TEST_F(PrivateDnsDohTest, PreferIpv6) { // Currently, DnsResolver sorts the server list and did DoH validation only // for the first server. - EXPECT_TRUE(WaitForDohValidation(listen_ipv6_addr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(listen_ipv6_addr)); doh.clearQueries(); doh_ipv6.clearQueries(); @@ -671,7 +810,7 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); // Use v4 DoH server first. - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); doh.clearQueries(); EXPECT_NO_FAILURE(sendQueryAndCheckResult()); EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 0 /* dot */, 2 /* doh */)); @@ -680,7 +819,7 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { parcel.servers = {listen_ipv6_addr}; parcel.tlsServers = {listen_ipv6_addr}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(listen_ipv6_addr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(listen_ipv6_addr)); doh.clearQueries(); doh_ipv6.clearQueries(); flushCache(); @@ -729,7 +868,7 @@ TEST_F(PrivateDnsDohTest, ChangePrivateDnsServerAndVerifyOutput) { // Start the v4 DoH server. auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(expectLog(ipv4DohServerAddr, kDohPortString)); // Change to an invalid DoH server. @@ -742,7 +881,7 @@ TEST_F(PrivateDnsDohTest, ChangePrivateDnsServerAndVerifyOutput) { parcel.servers = {ipv6DohServerAddr}; parcel.tlsServers = {ipv6DohServerAddr}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(ipv6DohServerAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(ipv6DohServerAddr)); EXPECT_TRUE(expectLog(ipv6DohServerAddr, kDohPortString)); EXPECT_FALSE(expectLog(ipv4DohServerAddr, kDohPortString)); @@ -762,8 +901,8 @@ TEST_F(PrivateDnsDohTest, TemporaryConnectionStalled) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -788,9 +927,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. @@ -805,15 +954,15 @@ TEST_F(PrivateDnsDohTest, ExcessDnsRequests) { auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); 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. @@ -831,8 +980,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; @@ -899,8 +1050,8 @@ TEST_F(PrivateDnsDohTest, RunOutOfDataLimit) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -948,8 +1099,8 @@ TEST_F(PrivateDnsDohTest, RunOutOfStreams) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -986,8 +1137,8 @@ TEST_F(PrivateDnsDohTest, ReconnectAfterIdleTimeout) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1027,8 +1178,8 @@ TEST_F(PrivateDnsDohTest, ConnectionIdleTimer) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1061,8 +1212,8 @@ TEST_F(PrivateDnsDohTest, SessionResumption) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1100,8 +1251,8 @@ TEST_F(PrivateDnsDohTest, TestEarlyDataFlag) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1122,8 +1273,8 @@ TEST_F(PrivateDnsDohTest, TestEarlyDataFlag) { TEST_F(PrivateDnsDohTest, RemoteConnectionClosed) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1149,8 +1300,8 @@ TEST_F(PrivateDnsDohTest, RemoteConnectionClosed) { TEST_F(PrivateDnsDohTest, ReceiveResetStream) { const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); - EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); - EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); + EXPECT_TRUE(WaitForDohValidationSuccess(test::kDefaultListenAddr)); + EXPECT_TRUE(WaitForDotValidationSuccess(test::kDefaultListenAddr)); EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); doh.clearQueries(); @@ -1175,3 +1326,32 @@ TEST_F(PrivateDnsDohTest, ReceiveResetStream) { expectAnswersValid(fd, AF_INET6, kQueryAnswerAAAA); EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 1 /* dot */, 2 /* doh */)); } + +// Tests that, given an IP address with an allowed DoH provider name, PrivateDnsConfiguration +// attempts to probe the server for DoH. +TEST_F(PrivateDnsDohTest, UseDohAsLongAsHostnameMatch) { + // "example.com" is an allowed DoH provider name defined in + // PrivateDnsConfiguration::mAvailableDoHProviders. + constexpr char allowedDohName[] = "example.com"; + constexpr char someOtherIp[] = "127.99.99.99"; + + // The test currently doesn't support testing DoH in private DNS strict mode, so DnsResolver + // can't connect to the testing DoH servers. We use onPrivateDnsValidationEvent() to check + // whether DoT/DoH probes are performed. + // Without an allowed private DNS provider hostname, expect PrivateDnsConfiguration to probe + // the server for DoT only. + ASSERT_TRUE(mDnsClient.SetResolversFromParcel( + ResolverParams::Builder().setDotServers({someOtherIp}).build())); + EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); + EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); + + // With an allowed private DNS provider hostname, expect PrivateDnsConfiguration to probe the + // server for both DoT and DoH. + ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() + .setDotServers({someOtherIp}) + .setPrivateDnsProvider(allowedDohName) + .build())); + EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); + EXPECT_TRUE(WaitForDohValidationFailure(someOtherIp)); + EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); +} 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 1febb520..e3f744ce 100644 --- a/tests/resolv_test_utils.h +++ b/tests/resolv_test_utils.h @@ -20,6 +20,8 @@ #include <arpa/nameser.h> #include <netdb.h> +#include <filesystem> +#include <functional> #include <string> #include <vector> @@ -31,6 +33,7 @@ #include <netdutils/InternetAddresses.h> #include "dns_responder/dns_responder.h" +#include "util.h" class ScopeBlockedUIDRule { using INetd = aidl::android::net::INetd; @@ -79,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()) { @@ -154,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."); @@ -401,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(); @@ -62,6 +62,11 @@ inline bool isDebuggable() { return android::base::GetBoolProperty("ro.debuggable", false); } +inline bool isAtLeastT() { + const static bool isAtLeastT = android::modules::sdklevel::IsAtLeastT(); + return isAtLeastT; +} + inline bool isAtLeastU() { const static bool isAtLeastU = android::modules::sdklevel::IsAtLeastU(); return isAtLeastU; |