Snap for 5761090 from 72e88a79af497b294787c6ba7e232c3bd4f3c137 to qt-c2f2-release
Change-Id: Ibea92fe8f60ff6049c82d9ada661b9b01129e6bd
diff --git a/resolv/Android.bp b/resolv/Android.bp
index 30d5f5a..6819b7a 100644
--- a/resolv/Android.bp
+++ b/resolv/Android.bp
@@ -205,17 +205,22 @@
srcs: [
"dns_tls_test.cpp",
"libnetd_resolv_test.cpp",
+ "res_cache_test.cpp",
],
shared_libs: [
"libbase",
"libcrypto",
+ "libcutils",
"liblog",
"libssl",
],
static_libs: [
+ "libgmock",
"libnetd_resolv",
"libnetd_test_dnsresponder",
"libnetdutils",
+ "libprotobuf-cpp-lite",
"server_configurable_flags",
+ "stats_proto",
],
}
diff --git a/resolv/Dns64Configuration.cpp b/resolv/Dns64Configuration.cpp
index 6cd9c2e..a1dfdca 100644
--- a/resolv/Dns64Configuration.cpp
+++ b/resolv/Dns64Configuration.cpp
@@ -33,9 +33,11 @@
#include "netdutils/BackoffSequence.h"
#include "netdutils/DumpWriter.h"
#include "netid_client.h"
+#include "stats.pb.h"
namespace android {
+using android::net::NetworkDnsEventReported;
using netdutils::DumpWriter;
using netdutils::IPAddress;
using netdutils::IPPrefix;
@@ -152,8 +154,9 @@
// ourselves, which means we also bypass all the special netcontext flag
// handling and the resolver event logging.
struct addrinfo* res = nullptr;
- const int status =
- android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res);
+ NetworkDnsEventReported event;
+ const int status = android_getaddrinfofornetcontext(kIPv4OnlyHost, nullptr, &hints, &netcontext,
+ &res, &event);
ScopedAddrinfo result(res);
if (status != 0) {
ALOGW("(%u, %u) plat_prefix/dns(%s) status = %d/%s", cfg->netId, cfg->discoveryId,
diff --git a/resolv/DnsProxyListener.cpp b/resolv/DnsProxyListener.cpp
index 63aa331..8925bc4 100644
--- a/resolv/DnsProxyListener.cpp
+++ b/resolv/DnsProxyListener.cpp
@@ -32,7 +32,6 @@
#define LOG_TAG "DnsProxyListener"
#include <algorithm>
-#include <list>
#include <vector>
#include <android-base/stringprintf.h>
@@ -57,6 +56,7 @@
#include "gethnamaddr.h"
#include "netd_resolv/stats.h" // RCODE_TIMEOUT
#include "res_send.h"
+#include "resolv_cache.h"
#include "resolv_private.h"
#include "stats.pb.h"
@@ -299,17 +299,34 @@
return true;
}
+void initDnsEvent(NetworkDnsEventReported* event) {
+ // The value 0 has the special meaning of unset/unknown in Westworld atoms.
+ event->set_hints_ai_flags(-1);
+ event->set_res_nsend_flags(-1);
+}
+
+// Return 0 if the event should not be logged.
+// Otherwise, return subsampling_denom
+uint32_t getDnsEventSubsamplingRate(int netid, int returnCode) {
+ uint32_t subsampling_denom = resolv_cache_get_subsampling_denom(netid, returnCode);
+ if (subsampling_denom == 0) return 0;
+ // Sample the event with a chance of 1 / denom.
+ return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0;
+}
+
void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
- int returnCode, const NetworkDnsEventReported& dnsEvent,
- const std::string& query_name, const std::vector<std::string>& ip_addrs = {},
- int total_ip_addr_count = 0) {
- std::string dnsQueryStats = dnsEvent.dns_query_events().SerializeAsString();
- char const* dnsQueryStatsBytes = dnsQueryStats.c_str();
- stats::BytesField dnsQueryBytesField{dnsQueryStatsBytes, dnsQueryStats.size()};
- android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED, eventType,
- returnCode, latencyUs, dnsEvent.hints_ai_flags(),
- dnsEvent.res_nsend_flags(), dnsEvent.network_type(),
- dnsEvent.private_dns_modes(), dnsQueryBytesField);
+ int returnCode, NetworkDnsEventReported& event, const std::string& query_name,
+ const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
+ if (uint32_t rate = getDnsEventSubsamplingRate(netContext.dns_netid, returnCode)) {
+ const std::string& dnsQueryStats = event.dns_query_events().SerializeAsString();
+ stats::BytesField dnsQueryBytesField{dnsQueryStats.c_str(), dnsQueryStats.size()};
+ event.set_return_code(static_cast<ReturnCode>(returnCode));
+ 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);
+ }
const auto& listeners = ResolverEventReporter::getInstance().getListeners();
if (listeners.size() == 0) {
@@ -590,7 +607,8 @@
return true;
}
-void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res) {
+void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res,
+ NetworkDnsEventReported* event) {
if (mHost == nullptr) return;
const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA);
@@ -613,7 +631,8 @@
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 = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, res);
+ *rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, res,
+ event);
queryLimiter.finish(uid);
if (*rv) {
*rv = EAI_NODATA; // return original error code
@@ -648,9 +667,11 @@
maybeFixupNetContext(&mNetContext);
const uid_t uid = mClient->getUid();
int32_t rv = 0;
- NetworkDnsEventReported dnsEvent;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
if (queryLimiter.start(uid)) {
- rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
+ rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result,
+ &event);
queryLimiter.finish(uid);
} else {
// Note that this error code is currently not passed down to the client.
@@ -660,8 +681,11 @@
<< ", max concurrent queries reached";
}
- doDns64Synthesis(&rv, &result);
- const int latencyUs = int(s.timeTakenUs());
+ 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);
+ event.set_hints_ai_flags((mHints ? mHints->ai_flags : 0));
if (rv) {
// getaddrinfo failed
@@ -680,8 +704,8 @@
}
std::vector<std::string> ip_addrs;
const int total_ip_addr_count = extractGetAddrInfoAnswers(result, &ip_addrs);
- reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, dnsEvent,
- mHost, ip_addrs, total_ip_addr_count);
+ reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, event, mHost,
+ ip_addrs, total_ip_addr_count);
freeaddrinfo(result);
mClient->decRef();
}
@@ -854,10 +878,11 @@
// Send DNS query
std::vector<uint8_t> ansBuf(MAXPACKET, 0);
int arcode, nsendAns = -1;
- NetworkDnsEventReported dnsEvent;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
if (queryLimiter.start(uid)) {
nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
- &arcode, static_cast<ResNsendFlags>(mFlags));
+ &arcode, static_cast<ResNsendFlags>(mFlags), &event);
queryLimiter.finish(uid);
} else {
LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid
@@ -865,14 +890,17 @@
nsendAns = -EBUSY;
}
- const int latencyUs = int(s.timeTakenUs());
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_RES_NSEND);
+ event.set_res_nsend_flags(static_cast<ResNsendFlags>(mFlags));
// Fail, send -errno
if (nsendAns < 0) {
sendBE32(mClient, nsendAns);
if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
- resNSendToAiError(nsendAns, arcode), dnsEvent, rr_name);
+ resNSendToAiError(nsendAns, arcode), event, rr_name);
}
return;
}
@@ -895,7 +923,7 @@
const int total_ip_addr_count =
extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs);
reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
- resNSendToAiError(nsendAns, arcode), dnsEvent, rr_name, ip_addrs,
+ resNSendToAiError(nsendAns, arcode), event, rr_name, ip_addrs,
total_ip_addr_count);
}
}
@@ -994,7 +1022,8 @@
free(mName);
}
-void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp) {
+void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp,
+ NetworkDnsEventReported* event) {
// Don't have to consider family AF_UNSPEC case because gethostbyname{, 2} only supports
// family AF_INET or AF_INET6.
const bool ipv6WantedButNoData = (mAf == AF_INET6 && *rv == EAI_NODATA);
@@ -1011,7 +1040,7 @@
// If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis
const uid_t uid = mClient->getUid();
if (queryLimiter.start(uid)) {
- *rv = android_gethostbynamefornetcontext(mName, AF_INET, &mNetContext, hpp);
+ *rv = android_gethostbynamefornetcontext(mName, AF_INET, &mNetContext, hpp, event);
queryLimiter.finish(uid);
if (*rv) {
*rv = EAI_NODATA; // return original error code
@@ -1035,9 +1064,10 @@
const uid_t uid = mClient->getUid();
hostent* hp = nullptr;
int32_t rv = 0;
- NetworkDnsEventReported dnsEvent;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
if (queryLimiter.start(uid)) {
- rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp);
+ rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp, &event);
queryLimiter.finish(uid);
} else {
rv = EAI_MEMORY;
@@ -1045,8 +1075,11 @@
<< ", max concurrent queries reached";
}
- doDns64Synthesis(&rv, &hp);
- const int latencyUs = lround(s.timeTakenUs());
+ doDns64Synthesis(&rv, &hp, &event);
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_GETHOSTBYNAME);
+
LOG(DEBUG) << "GetHostByNameHandler::run: errno: " << (hp ? "success" : strerror(errno));
bool success = true;
@@ -1064,7 +1097,7 @@
std::vector<std::string> ip_addrs;
const int total_ip_addr_count = extractGetHostByNameAnswers(hp, &ip_addrs);
- reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, dnsEvent,
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, event,
mName, ip_addrs, total_ip_addr_count);
mClient->decRef();
}
@@ -1134,7 +1167,8 @@
free(mAddress);
}
-void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp) {
+void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp,
+ NetworkDnsEventReported* event) {
if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) {
return;
}
@@ -1162,7 +1196,8 @@
if (queryLimiter.start(uid)) {
// Remove NAT64 prefix and do reverse DNS query
struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]};
- android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp);
+ android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp,
+ event);
queryLimiter.finish(uid);
if (*hpp) {
// Replace IPv4 address with original queried IPv6 address in place. The space has
@@ -1184,10 +1219,11 @@
const uid_t uid = mClient->getUid();
hostent* hp = nullptr;
int32_t rv = 0;
- NetworkDnsEventReported dnsEvent;
+ NetworkDnsEventReported event;
+ initDnsEvent(&event);
if (queryLimiter.start(uid)) {
- rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,
- &mNetContext, &hp);
+ rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily, &mNetContext,
+ &hp, &event);
queryLimiter.finish(uid);
} else {
rv = EAI_MEMORY;
@@ -1195,8 +1231,10 @@
<< ", max concurrent queries reached";
}
- doDns64ReverseLookup(&hp);
- const int latencyUs = int(s.timeTakenUs());
+ doDns64ReverseLookup(&hp, &event);
+ const int32_t latencyUs = saturate_cast<int32_t>(s.timeTakenUs());
+ event.set_latency_micros(latencyUs);
+ event.set_event_type(EVENT_GETHOSTBYADDR);
LOG(DEBUG) << "GetHostByAddrHandler::run: result: " << (hp ? "success" : gai_strerror(rv));
@@ -1212,7 +1250,7 @@
LOG(WARNING) << "GetHostByAddrHandler::run: Error writing DNS result to client";
}
- reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, dnsEvent,
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, event,
(hp && hp->h_name) ? hp->h_name : "null", {}, 0);
mClient->decRef();
}
diff --git a/resolv/DnsProxyListener.h b/resolv/DnsProxyListener.h
index 423e77f..9b71bc5 100644
--- a/resolv/DnsProxyListener.h
+++ b/resolv/DnsProxyListener.h
@@ -28,6 +28,8 @@
namespace android {
namespace net {
+class NetworkDnsEventReported;
+
class DnsProxyListener : public FrameworkListener {
public:
DnsProxyListener();
@@ -54,7 +56,7 @@
void run();
private:
- void doDns64Synthesis(int32_t* rv, addrinfo** res);
+ void doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event);
SocketClient* mClient; // ref counted
char* mHost; // owned. TODO: convert to std::string.
@@ -80,7 +82,7 @@
void run();
private:
- void doDns64Synthesis(int32_t* rv, hostent** hpp);
+ void doDns64Synthesis(int32_t* rv, hostent** hpp, NetworkDnsEventReported* event);
SocketClient* mClient; // ref counted
char* mName; // owned. TODO: convert to std::string.
@@ -105,7 +107,7 @@
void run();
private:
- void doDns64ReverseLookup(hostent** hpp);
+ void doDns64ReverseLookup(hostent** hpp, NetworkDnsEventReported* event);
SocketClient* mClient; // ref counted
void* mAddress; // address to lookup; owned
diff --git a/resolv/DnsTlsDispatcher.cpp b/resolv/DnsTlsDispatcher.cpp
index d9896ad..baafa53 100644
--- a/resolv/DnsTlsDispatcher.cpp
+++ b/resolv/DnsTlsDispatcher.cpp
@@ -18,13 +18,17 @@
//#define LOG_NDEBUG 0
#include "DnsTlsDispatcher.h"
+#include <netdutils/Stopwatch.h>
#include "DnsTlsSocketFactory.h"
+#include "resolv_private.h"
+#include "stats.pb.h"
#include "log/log.h"
namespace android {
namespace net {
+using android::netdutils::Stopwatch;
using netdutils::Slice;
// static
@@ -82,29 +86,45 @@
return out;
}
-DnsTlsTransport::Response DnsTlsDispatcher::query(
- const std::list<DnsTlsServer> &tlsServers, unsigned mark,
- const Slice query, const Slice ans, int *resplen) {
- const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, mark));
+DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& tlsServers,
+ res_state statp, const Slice query,
+ const Slice ans, int* resplen) {
+ const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, statp->_mark));
if (orderedServers.empty()) ALOGW("Empty DnsTlsServer list");
DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error;
+ int serverCount = 0;
for (const auto& server : orderedServers) {
- code = this->query(server, mark, query, ans, resplen);
+ DnsQueryEvent* dnsQueryEvent =
+ statp->event->mutable_dns_query_events()->add_dns_query_event();
+ dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
+ Stopwatch query_stopwatch;
+ code = this->query(server, statp->_mark, query, ans, resplen);
+
+ dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(serverCount++);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(server.ss.ss_family));
+ dnsQueryEvent->set_protocol(PROTO_DOT);
+ dnsQueryEvent->set_type(getQueryType(query.base(), query.size()));
+
switch (code) {
// These response codes are valid responses and not expected to
// change if another server is queried.
case DnsTlsTransport::Response::success:
+ dnsQueryEvent->set_rcode(
+ static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode));
+ [[fallthrough]];
case DnsTlsTransport::Response::limit_error:
return code;
- break;
// These response codes might differ when trying other servers, so
// keep iterating to see if we can get a different (better) result.
case DnsTlsTransport::Response::network_error:
+ // Sync from res_tls_send in res_send.cpp
+ dnsQueryEvent->set_rcode(NS_R_TIMEOUT);
+ [[fallthrough]];
case DnsTlsTransport::Response::internal_error:
continue;
- break;
// No "default" statement.
}
}
diff --git a/resolv/DnsTlsDispatcher.h b/resolv/DnsTlsDispatcher.h
index 7a48089..b8e9968 100644
--- a/resolv/DnsTlsDispatcher.h
+++ b/resolv/DnsTlsDispatcher.h
@@ -28,6 +28,7 @@
#include "DnsTlsServer.h"
#include "DnsTlsTransport.h"
#include "IDnsTlsSocketFactory.h"
+#include "resolv_private.h"
namespace android {
namespace net {
@@ -48,9 +49,9 @@
// the count of bytes written in |resplen|. Returns a success or error code.
// The order in which servers from |tlsServers| are queried may not be the
// order passed in by the caller.
- DnsTlsTransport::Response query(const std::list<DnsTlsServer>& tlsServers, unsigned mark,
- const netdutils::Slice query, const netdutils::Slice ans,
- int* _Nonnull resplen);
+ DnsTlsTransport::Response query(const std::list<DnsTlsServer>& tlsServers,
+ res_state _Nonnull statp, const netdutils::Slice query,
+ const netdutils::Slice ans, int* _Nonnull resplen);
// Given a |query|, sends it to the server on the network indicated by |mark|,
// and writes the response into |ans|, and indicates
diff --git a/resolv/ResolverController.cpp b/resolv/ResolverController.cpp
index e16ca69..ac24259 100644
--- a/resolv/ResolverController.cpp
+++ b/resolv/ResolverController.cpp
@@ -317,6 +317,8 @@
if (servers.empty()) {
dw.println("No DNS servers defined");
} else {
+ dw.println("DnsEvent subsampling map: " +
+ android::base::Join(resolv_cache_dump_subsampling_map(netId), ' '));
dw.println(
"DNS servers: # IP (total, successes, errors, timeouts, internal errors, "
"RTT avg, last sample)");
@@ -357,8 +359,7 @@
mDns64Configuration.dump(dw, netId);
ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
gPrivateDnsConfiguration.getStatus(netId, &privateDnsStatus);
- dw.println("Private DNS mode: %s",
- getPrivateDnsModeString(static_cast<PrivateDnsMode>(privateDnsStatus.mode)));
+ dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode));
if (!privateDnsStatus.numServers) {
dw.println("No Private DNS servers configured");
} else {
diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp
index 1d5038e..bb9ef07 100644
--- a/resolv/getaddrinfo.cpp
+++ b/resolv/getaddrinfo.cpp
@@ -63,6 +63,8 @@
#define ANY 0
+using android::net::NetworkDnsEventReported;
+
const char in_addrany[] = {0, 0, 0, 0};
const char in_loopback[] = {127, 0, 0, 1};
const char in6_addrany[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -125,7 +127,7 @@
static int str2number(const char*);
static int explore_fqdn(const struct addrinfo*, const char*, const char*, struct addrinfo**,
- const struct android_net_context*);
+ const struct android_net_context*, NetworkDnsEventReported* event);
static int explore_null(const struct addrinfo*, const char*, struct addrinfo**);
static int explore_numeric(const struct addrinfo*, const char*, const char*, struct addrinfo**,
const char*);
@@ -141,7 +143,8 @@
static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*,
int* herrno);
static int dns_getaddrinfo(const char* name, const addrinfo* pai,
- const android_net_context* netcontext, addrinfo** rv);
+ const android_net_context* netcontext, addrinfo** rv,
+ NetworkDnsEventReported* event);
static void _sethtent(FILE**);
static void _endhtent(FILE**);
static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*);
@@ -265,13 +268,15 @@
.dns_mark = MARK_UNSET,
.uid = NET_CONTEXT_INVALID_UID,
};
- return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result);
+ NetworkDnsEventReported event;
+ return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result,
+ &event);
}
int android_getaddrinfofornetcontext(const char* hostname, const char* servname,
const struct addrinfo* hints,
const struct android_net_context* netcontext,
- struct addrinfo** res) {
+ struct addrinfo** res, NetworkDnsEventReported* event) {
struct addrinfo sentinel = {};
struct addrinfo* cur = &sentinel;
int error = 0;
@@ -281,6 +286,7 @@
// hints is allowed to be nullptr
assert(res != nullptr);
assert(netcontext != nullptr);
+ assert(event != nullptr);
struct addrinfo ai = {
.ai_flags = 0,
@@ -413,7 +419,7 @@
LOG(DEBUG) << __func__ << ": explore_fqdn(): ai_family=" << tmp.ai_family
<< " ai_socktype=" << tmp.ai_socktype << " ai_protocol=" << tmp.ai_protocol;
- error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext);
+ error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext, event);
while (cur->ai_next) cur = cur->ai_next;
}
@@ -436,7 +442,8 @@
// FQDN hostname, DNS lookup
static int explore_fqdn(const struct addrinfo* pai, const char* hostname, const char* servname,
- struct addrinfo** res, const struct android_net_context* netcontext) {
+ struct addrinfo** res, const struct android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
struct addrinfo* result;
int error = 0;
@@ -451,7 +458,7 @@
if (get_portmatch(pai, servname) != 0) return 0;
if (!files_getaddrinfo(hostname, pai, &result)) {
- error = dns_getaddrinfo(hostname, pai, netcontext, &result);
+ error = dns_getaddrinfo(hostname, pai, netcontext, &result, event);
}
if (!error) {
struct addrinfo* cur;
@@ -1379,7 +1386,8 @@
}
static int dns_getaddrinfo(const char* name, const addrinfo* pai,
- const android_net_context* netcontext, addrinfo** rv) {
+ const android_net_context* netcontext, addrinfo** rv,
+ NetworkDnsEventReported* event) {
res_target q = {};
res_target q2 = {};
@@ -1441,7 +1449,7 @@
* fully populate the thread private data here, but if we get down there
* and have a cache hit that would be wasted, so we do the rest there on miss
*/
- res_setnetcontext(res, netcontext);
+ res_setnetcontext(res, netcontext, event);
int he;
if (res_searchN(name, &q, res, &he) < 0) {
diff --git a/resolv/getaddrinfo.h b/resolv/getaddrinfo.h
index cf54cab..e8ba5dc 100644
--- a/resolv/getaddrinfo.h
+++ b/resolv/getaddrinfo.h
@@ -17,9 +17,11 @@
#pragma once
#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
struct addrinfo;
// This is the DNS proxy entry point for getaddrinfo().
int android_getaddrinfofornetcontext(const char*, const char*, const addrinfo*,
- const android_net_context*, addrinfo**);
+ const android_net_context*, addrinfo**,
+ android::net::NetworkDnsEventReported*);
diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp
index 1cf0694..a40a7b7 100644
--- a/resolv/gethnamaddr.cpp
+++ b/resolv/gethnamaddr.cpp
@@ -78,6 +78,9 @@
#include "netd_resolv/resolv.h"
#include "resolv_cache.h"
#include "resolv_private.h"
+#include "stats.pb.h"
+
+using android::net::NetworkDnsEventReported;
// NetBSD uses _DIAGASSERT to null-check arguments and the like,
// but it's clear from the number of mistakes in their assertions
@@ -116,19 +119,22 @@
static void addrsort(char**, int, res_state);
static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
- const android_net_context* netcontext, getnamaddr* info);
+ const android_net_context* netcontext, getnamaddr* info,
+ NetworkDnsEventReported* event);
static int dns_gethtbyname(const char* name, int af, getnamaddr* info);
static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
- size_t hbuflen, const android_net_context* netcontext);
+ size_t hbuflen, const android_net_context* netcontext,
+ NetworkDnsEventReported* event);
static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
char* buf, size_t buflen);
static int android_gethostbyaddrfornetcontext_proxy_internal(const void*, socklen_t, int,
struct hostent*, char*, size_t,
- const struct android_net_context*);
+ const struct android_net_context*,
+ NetworkDnsEventReported* event);
static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
const struct android_net_context* netcontext,
- hostent** hp);
+ hostent** hp, NetworkDnsEventReported* event);
#define BOUNDED_INCR(x) \
do { \
@@ -494,14 +500,16 @@
// very similar in proxy-ness to android_getaddrinfo_proxy
static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
- size_t hbuflen, const android_net_context* netcontext) {
- res_setnetcontext(res, netcontext);
+ size_t hbuflen, const android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
+ res_setnetcontext(res, netcontext, event);
return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen);
}
static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t len, int af,
struct hostent* hp, char* buf, size_t buflen,
- const struct android_net_context* netcontext) {
+ const struct android_net_context* netcontext,
+ NetworkDnsEventReported* event) {
const u_char* uaddr = (const u_char*) addr;
socklen_t size;
struct getnamaddr info;
@@ -543,7 +551,7 @@
info.buf = buf;
info.buflen = buflen;
if (_hf_gethtbyaddr(uaddr, len, af, &info)) {
- int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info);
+ int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info, event);
if (error != 0) return error;
}
return 0;
@@ -551,8 +559,9 @@
static int android_gethostbyaddrfornetcontext_proxy_internal(
const void* addr, socklen_t len, int af, struct hostent* hp, char* hbuf, size_t hbuflen,
- const struct android_net_context* netcontext) {
- return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext);
+ const struct android_net_context* netcontext, NetworkDnsEventReported* event) {
+ return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext,
+ event);
}
// TODO: Consider leaving function without returning error code as _gethtent() does because
@@ -795,7 +804,8 @@
}
static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
- const android_net_context* netcontext, getnamaddr* info) {
+ const android_net_context* netcontext, getnamaddr* info,
+ NetworkDnsEventReported* event) {
char qbuf[MAXDNAME + 1], *qp, *ep;
int n;
int advance;
@@ -842,7 +852,7 @@
res_state res = res_get_state();
if (!res) return EAI_MEMORY;
- res_setnetcontext(res, netcontext);
+ res_setnetcontext(res, netcontext, event);
int he;
n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
if (n < 0) {
@@ -886,13 +896,16 @@
*/
int android_gethostbynamefornetcontext(const char* name, int af,
- const struct android_net_context* netcontext, hostent** hp) {
- int error;
+ const struct android_net_context* netcontext, hostent** hp,
+ NetworkDnsEventReported* event) {
+ assert(event != nullptr);
+
res_state res = res_get_state();
if (res == NULL) return EAI_MEMORY;
res_static* rs = res_get_static(); // For thread-safety.
+ int error;
error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
- netcontext);
+ netcontext, event);
if (error == 0) {
*hp = &rs->host;
}
@@ -900,16 +913,19 @@
}
int android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,
- const struct android_net_context* netcontext, hostent** hp) {
- return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp);
+ const struct android_net_context* netcontext, hostent** hp,
+ NetworkDnsEventReported* event) {
+ return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp, event);
}
static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
const struct android_net_context* netcontext,
- hostent** hp) {
+ hostent** hp, NetworkDnsEventReported* event) {
+ assert(event != nullptr);
+
struct res_static* rs = res_get_static(); // For thread-safety.
int error = android_gethostbyaddrfornetcontext_proxy_internal(
- addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext);
+ addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext, event);
if (error == 0) *hp = &rs->host;
return error;
}
diff --git a/resolv/gethnamaddr.h b/resolv/gethnamaddr.h
index 27cc1c2..bfdb14e 100644
--- a/resolv/gethnamaddr.h
+++ b/resolv/gethnamaddr.h
@@ -18,10 +18,12 @@
#include <netdb.h> // struct hostent
#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
// This is the entry point for the gethostbyname() family of legacy calls.
-int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**);
+int android_gethostbynamefornetcontext(const char*, int, const android_net_context*, hostent**,
+ android::net::NetworkDnsEventReported*);
// This is the entry point for the gethostbyaddr() family of legacy calls.
int android_gethostbyaddrfornetcontext(const void*, socklen_t, int, const android_net_context*,
- hostent**);
+ hostent**, android::net::NetworkDnsEventReported*);
diff --git a/resolv/libnetd_resolv_test.cpp b/resolv/libnetd_resolv_test.cpp
index 7fc5669..6e4b9ee 100644
--- a/resolv/libnetd_resolv_test.cpp
+++ b/resolv/libnetd_resolv_test.cpp
@@ -26,6 +26,7 @@
#include "getaddrinfo.h"
#include "gethnamaddr.h"
#include "resolv_cache.h"
+#include "stats.pb.h"
// TODO: make this dynamic and stop depending on implementation details.
constexpr unsigned int TEST_NETID = 30;
@@ -39,6 +40,8 @@
namespace android {
namespace net {
+using android::net::NetworkDnsEventReported;
+
// Minimize class ResolverTest to be class TestBase because class TestBase doesn't need all member
// functions of class ResolverTest and class DnsResponderClient.
class TestBase : public ::testing::Test {
@@ -112,8 +115,9 @@
// Invalid hostname and servname.
// Both hostname and servname are null pointers. Expect error number EAI_NONAME.
struct addrinfo* result = nullptr;
+ NetworkDnsEventReported event;
int rv = android_getaddrinfofornetcontext(nullptr /*hostname*/, nullptr /*servname*/,
- nullptr /*hints*/, &mNetcontext, &result);
+ nullptr /*hints*/, &mNetcontext, &result, &event);
EXPECT_EQ(EAI_NONAME, rv);
if (result) {
freeaddrinfo(result);
@@ -167,8 +171,9 @@
.ai_next = config.ai_next,
};
+ NetworkDnsEventReported event;
rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
+ &mNetcontext, &result, &event);
EXPECT_EQ(config.expected_errorno, rv);
if (result) {
@@ -190,8 +195,9 @@
.ai_family = family, // unsupported family
};
+ NetworkDnsEventReported event;
int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
+ &mNetcontext, &result, &event);
EXPECT_EQ(EAI_FAMILY, rv);
if (result) freeaddrinfo(result);
@@ -237,8 +243,9 @@
.ai_socktype = socktype,
};
+ NetworkDnsEventReported event;
int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
- &mNetcontext, &result);
+ &mNetcontext, &result, &event);
EXPECT_EQ(EAI_BADHINTS, rv);
if (result) freeaddrinfo(result);
@@ -320,8 +327,9 @@
};
struct addrinfo* result = nullptr;
+ NetworkDnsEventReported event;
int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints,
- &mNetcontext, &result);
+ &mNetcontext, &result, &event);
EXPECT_EQ(config.expected_errorno, rv);
if (result) freeaddrinfo(result);
@@ -344,7 +352,9 @@
// Want AAAA answer but DNS server has A answer only.
struct addrinfo* result = nullptr;
const addrinfo hints = {.ai_family = AF_INET6};
- int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result);
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result,
+ &event);
EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
EXPECT_TRUE(result == nullptr);
EXPECT_EQ(EAI_NODATA, rv);
@@ -382,8 +392,9 @@
struct addrinfo* result = nullptr;
const struct addrinfo hints = {.ai_family = config.ai_family};
- int rv =
- android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result);
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result,
+ &event);
EXPECT_EQ(0, rv);
EXPECT_TRUE(result != nullptr);
EXPECT_EQ(1U, GetNumQueries(dns, host_name));
@@ -429,8 +440,9 @@
struct addrinfo* result = nullptr;
const struct addrinfo hints = {.ai_family = AF_UNSPEC};
- int rv =
- android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result);
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result,
+ &event);
EXPECT_EQ(config.expected_errorno, rv);
if (result) freeaddrinfo(result);
@@ -453,7 +465,9 @@
struct addrinfo* result = nullptr;
const struct addrinfo hints = {.ai_family = AF_UNSPEC};
- int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result);
+ NetworkDnsEventReported event;
+ int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result,
+ &event);
EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
if (result) freeaddrinfo(result);
@@ -488,8 +502,9 @@
dns.clearQueries();
struct hostent* hp = nullptr;
+ NetworkDnsEventReported event;
int rv = android_gethostbynamefornetcontext("jiababuei", config.ai_family, &mNetcontext,
- &hp);
+ &hp, &event);
EXPECT_EQ(0, rv);
EXPECT_TRUE(hp != nullptr);
EXPECT_EQ(1U, GetNumQueries(dns, host_name));
@@ -512,7 +527,8 @@
// Want AAAA answer but DNS server has A answer only.
struct hostent* hp = nullptr;
- int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp);
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp, &event);
EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
EXPECT_TRUE(hp == nullptr);
EXPECT_EQ(EAI_NODATA, rv);
@@ -555,7 +571,8 @@
mDefaultSearchDomains, &mDefaultParams_Binder));
struct hostent* hp = nullptr;
- int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event);
EXPECT_TRUE(hp == nullptr);
EXPECT_EQ(config.expected_errorno, rv);
}
@@ -576,7 +593,8 @@
mDefaultSearchDomains, &mDefaultParams_Binder));
struct hostent* hp = nullptr;
- int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
+ NetworkDnsEventReported event;
+ int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp, &event);
EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
}
diff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp
index f0ff564..d6f0cd8 100644
--- a/resolv/res_cache.cpp
+++ b/resolv/res_cache.cpp
@@ -36,7 +36,11 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+
#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
#include <arpa/inet.h>
#include <arpa/nameser.h>
@@ -47,6 +51,8 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
#include <android/multinetwork.h> // ResNsendFlags
@@ -1146,6 +1152,8 @@
char defdname[MAXDNSRCHPATH];
int dnsrch_offset[MAXDNSRCH + 1]; // offsets into defdname
int wait_for_pending_req_timeout_count;
+ // Map format: ReturnCode:rate_denom
+ std::unordered_map<int, uint32_t> dns_event_subsampling_map;
};
// A helper class for the Clang Thread Safety Analysis to deal with
@@ -1604,6 +1612,49 @@
return (info != nullptr) && (info->nscount > 0);
}
+namespace {
+
+// Map format: ReturnCode:rate_denom
+// if the ReturnCode is not associated with any rate_denom, use default
+// Sampling rate varies by return code; events to log are chosen randomly, with a
+// probability proportional to the sampling rate.
+constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
+
+std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
+ using android::base::ParseInt;
+ using android::base::ParseUint;
+ using android::base::Split;
+ using server_configurable_flags::GetServerConfigurableFlag;
+ std::unordered_map<int, uint32_t> sampling_rate_map{};
+ std::vector<std::string> subsampling_vector =
+ Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
+ DEFAULT_SUBSAMPLING_MAP),
+ " ");
+ for (const auto& pair : subsampling_vector) {
+ std::vector<std::string> rate_denom = Split(pair, ":");
+ int return_code;
+ uint32_t denom;
+ if (rate_denom.size() != 2) {
+ LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
+ continue;
+ }
+ if (rate_denom[0] == "default") {
+ return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
+ } else if (!ParseInt(rate_denom[0], &return_code)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ if (!ParseUint(rate_denom[1], &denom)) {
+ LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+ continue;
+ }
+ sampling_rate_map[return_code] = denom;
+ }
+ return sampling_rate_map;
+}
+
+} // namespace
+
static int resolv_create_cache_for_net_locked(unsigned netid) {
resolv_cache* cache = find_named_cache_locked(netid);
// Should not happen
@@ -1621,6 +1672,7 @@
}
cache_info->cache = cache;
cache_info->netid = netid;
+ cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
insert_cache_info_locked(cache_info);
return 0;
@@ -2000,6 +2052,42 @@
return revision_id;
}
+std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
+ using android::base::StringPrintf;
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* cache_info = find_cache_info_locked(netid);
+ if (cache_info == nullptr) return {};
+ std::vector<std::string> result;
+ for (const auto& pair : cache_info->dns_event_subsampling_map) {
+ result.push_back(StringPrintf("%s:%d",
+ (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
+ ? "default"
+ : std::to_string(pair.first).c_str(),
+ pair.second));
+ }
+ return result;
+}
+
+// Decides whether an event should be sampled using a random number generator and
+// a sampling factor derived from the netid and the return code.
+//
+// Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded.
+uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) {
+ std::lock_guard guard(cache_mutex);
+ resolv_cache_info* cache_info = find_cache_info_locked(netid);
+ if (cache_info == nullptr) return 0; // Don't log anything at all.
+ const auto& subsampling_map = cache_info->dns_event_subsampling_map;
+ auto search_returnCode = subsampling_map.find(return_code);
+ uint32_t denom;
+ if (search_returnCode != subsampling_map.end()) {
+ denom = search_returnCode->second;
+ } else {
+ auto search_default = subsampling_map.find(DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY);
+ denom = (search_default == subsampling_map.end()) ? 0 : search_default->second;
+ }
+ return denom;
+}
+
int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) {
std::lock_guard guard(cache_mutex);
resolv_cache_info* info = find_cache_info_locked(netid);
diff --git a/resolv/res_cache_test.cpp b/resolv/res_cache_test.cpp
new file mode 100644
index 0000000..0b09d88
--- /dev/null
+++ b/resolv/res_cache_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <netdb.h>
+
+#include <cutils/properties.h>
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include "netd_resolv/stats.h"
+#include "resolv_cache.h"
+
+constexpr int TEST_NETID = 30;
+
+namespace {
+
+constexpr int EAI_OK = 0;
+constexpr char DNS_EVENT_SUBSAMPLING_MAP_FLAG[] =
+ "persist.device_config.netd_native.dns_event_subsample_map";
+
+class ScopedCacheCreate {
+ public:
+ explicit ScopedCacheCreate(unsigned netid, const char* subsampling_map,
+ const char* property = DNS_EVENT_SUBSAMPLING_MAP_FLAG)
+ : mStoredNetId(netid), mStoredProperty(property) {
+ property_get(property, mStoredMap, "");
+ property_set(property, subsampling_map);
+ EXPECT_EQ(0, resolv_create_cache_for_net(netid));
+ }
+ ~ScopedCacheCreate() {
+ resolv_delete_cache_for_net(mStoredNetId);
+ property_set(mStoredProperty, mStoredMap);
+ }
+
+ private:
+ unsigned mStoredNetId;
+ const char* mStoredProperty;
+ char mStoredMap[PROPERTY_VALUE_MAX]{};
+};
+
+} // namespace
+
+TEST(ResolvCacheTest, DnsEventSubsampling) {
+ // Test defaults, default flag is "default:1 0:100 7:10" if no experiment flag is set
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "");
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 100U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS),
+ 1U); // default
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"default:1", "0:100", "7:10"}));
+ }
+ // Now change the experiment flag to "0:42 default:666"
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:42 default:666");
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 42U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA),
+ 666U); // default
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"default:666", "0:42"}));
+ }
+ // Now change the experiment flag to something illegal
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "asvaxx");
+ // 0(disable log) is the default value if experiment flag is invalid.
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 0U);
+ EXPECT_TRUE(resolv_cache_dump_subsampling_map(TEST_NETID).empty());
+ }
+ // Test negative and zero denom
+ {
+ ScopedCacheCreate scopedCacheCreate(TEST_NETID, "0:-42 default:-666 7:10 10:0");
+ // 0(disable log) is the default value if no valid denom is set
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_OK), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_BADFLAGS), 0U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_NODATA), 10U);
+ EXPECT_EQ(resolv_cache_get_subsampling_denom(TEST_NETID, EAI_SOCKTYPE), 0U);
+ EXPECT_THAT(resolv_cache_dump_subsampling_map(TEST_NETID),
+ testing::UnorderedElementsAreArray({"7:10", "10:0"}));
+ }
+}
diff --git a/resolv/res_init.cpp b/resolv/res_init.cpp
index ceb3023..bdbe162 100644
--- a/resolv/res_init.cpp
+++ b/resolv/res_init.cpp
@@ -383,7 +383,8 @@
return (statp->nscount);
}
-void res_setnetcontext(res_state statp, const struct android_net_context* netcontext) {
+void res_setnetcontext(res_state statp, const struct android_net_context* netcontext,
+ android::net::NetworkDnsEventReported* _Nonnull event) {
if (statp != NULL) {
statp->netid = netcontext->dns_netid;
statp->_mark = netcontext->dns_mark;
@@ -393,5 +394,6 @@
if (netcontext->flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) {
statp->use_local_nameserver = true;
}
+ statp->event = event;
}
}
diff --git a/resolv/res_send.cpp b/resolv/res_send.cpp
index d89ad7e..1ccfc74 100644
--- a/resolv/res_send.cpp
+++ b/resolv/res_send.cpp
@@ -101,6 +101,7 @@
#include <android/multinetwork.h> // ResNsendFlags
#include <netdutils/Slice.h>
+#include <netdutils/Stopwatch.h>
#include "DnsTlsDispatcher.h"
#include "DnsTlsTransport.h"
#include "PrivateDnsConfiguration.h"
@@ -110,10 +111,13 @@
#include "res_state_ext.h"
#include "resolv_cache.h"
#include "resolv_private.h"
+#include "stats.pb.h"
// TODO: use the namespace something like android::netd_resolv for libnetd_resolv
using namespace android::net;
+using android::net::NetworkDnsEventReported;
using android::netdutils::Slice;
+using android::netdutils::Stopwatch;
static DnsTlsDispatcher sDnsTlsDispatcher;
@@ -133,11 +137,29 @@
static int res_tls_send(res_state, const Slice query, const Slice answer, int* rcode,
bool* fallback);
-/* BIONIC-BEGIN: implement source port randomization */
+NsType getQueryType(const uint8_t* msg, size_t msgLen) {
+ ns_msg handle;
+ ns_rr rr;
+ if (ns_initparse((const uint8_t*)msg, msgLen, &handle) < 0 ||
+ ns_parserr(&handle, ns_s_qd, 0, &rr) < 0) {
+ return NS_T_INVALID;
+ }
+ return static_cast<NsType>(ns_rr_type(rr));
+}
+
+IpVersion ipFamilyToIPVersion(const int ipFamily) {
+ switch (ipFamily) {
+ case AF_INET:
+ return IV_IPV4;
+ case AF_INET6:
+ return IV_IPV6;
+ default:
+ return IV_UNKNOWN;
+ }
+}
// BEGIN: Code copied from ISC eventlib
// TODO: move away from this code
-
#define BILLION 1000000000
static struct timespec evConsTime(time_t sec, long nsec) {
@@ -201,6 +223,7 @@
// END: Code copied from ISC eventlib
+/* BIONIC-BEGIN: implement source port randomization */
static int random_bind(int s, int family) {
sockaddr_union u;
int j;
@@ -373,6 +396,10 @@
return (1);
}
+static DnsQueryEvent* addDnsQueryEvent(NetworkDnsEventReported* event) {
+ return event->mutable_dns_query_events()->add_dns_query_event();
+}
+
int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode,
uint32_t flags) {
int gotsomewhere, terrno, v_circuit, resplen, n;
@@ -391,11 +418,15 @@
terrno = ETIMEDOUT;
int anslen = 0;
+ Stopwatch cache_stopwatch;
cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
-
+ const int32_t cacheLatencyUs = saturate_cast<int32_t>(cache_stopwatch.timeTakenUs());
if (cache_status == RESOLV_CACHE_FOUND) {
HEADER* hp = (HEADER*)(void*)ans;
*rcode = hp->rcode;
+ DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+ dnsQueryEvent->set_latency_micros(cacheLatencyUs);
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
return anslen;
} else if (cache_status != RESOLV_CACHE_UNSUPPORTED) {
// had a cache miss for a known network, so populate the thread private
@@ -522,12 +553,11 @@
for (int ns = 0; ns < statp->nscount; ns++) {
if (!usable_servers[ns]) continue;
- struct sockaddr* nsap;
int nsaplen;
time_t now = 0;
int delay = 0;
*rcode = RCODE_INTERNAL_ERROR;
- nsap = get_nsaddr(statp, (size_t) ns);
+ const sockaddr* nsap = get_nsaddr(statp, ns);
nsaplen = get_salen(nsap);
same_ns:
@@ -551,13 +581,16 @@
}
}
- [[maybe_unused]] static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
- [[maybe_unused]] char abuf[NI_MAXHOST];
+ static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+ char abuf[NI_MAXHOST];
+ DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+ dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0)
LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1
<< ") address = " << abuf;
+ Stopwatch query_stopwatch;
if (v_circuit) {
/* Use VC; at most one attempt per server. */
bool shouldRecordStats = (attempt == 0);
@@ -566,6 +599,15 @@
n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode,
&delay);
+ dnsQueryEvent->set_latency_micros(
+ saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(ns);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+ dnsQueryEvent->set_retry_times(attempt);
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+ dnsQueryEvent->set_protocol(PROTO_TCP);
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
/*
* Only record stats the first time we try a query. This ensures that
* queries that deterministically fail (e.g., a name that always returns
@@ -594,6 +636,15 @@
n = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit,
&gotsomewhere, &now, rcode, &delay);
+ dnsQueryEvent->set_latency_micros(
+ saturate_cast<int32_t>(query_stopwatch.timeTakenUs()));
+ dnsQueryEvent->set_dns_server_index(ns);
+ dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+ dnsQueryEvent->set_retry_times(attempt);
+ dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+ dnsQueryEvent->set_protocol(PROTO_UDP);
+ dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
/* Only record stats the first time we try a query. See above. */
if (attempt == 0) {
res_sample sample;
@@ -1211,13 +1262,25 @@
}
}
+PrivateDnsModes convertEnumType(PrivateDnsMode privateDnsmode) {
+ switch (privateDnsmode) {
+ case PrivateDnsMode::OFF:
+ return PrivateDnsModes::PDM_OFF;
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return PrivateDnsModes::PDM_OPPORTUNISTIC;
+ case PrivateDnsMode::STRICT:
+ return PrivateDnsModes::PDM_STRICT;
+ }
+ return PrivateDnsModes::PDM_UNKNOWN;
+}
+
static int res_tls_send(res_state statp, const Slice query, const Slice answer, int* rcode,
bool* fallback) {
int resplen = 0;
const unsigned netId = statp->netid;
- const unsigned mark = statp->_mark;
PrivateDnsStatus privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId);
+ statp->event->set_private_dns_modes(convertEnumType(privateDnsStatus.mode));
if (privateDnsStatus.mode == PrivateDnsMode::OFF) {
*fallback = true;
@@ -1256,7 +1319,7 @@
LOG(INFO) << __func__ << ": performing query over TLS";
- const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, mark, query,
+ const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, statp, query,
answer, &resplen);
LOG(INFO) << __func__ << ": TLS query result: " << static_cast<int>(response);
@@ -1298,9 +1361,11 @@
}
int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen,
- uint8_t* ans, int ansLen, int* rcode, uint32_t flags) {
+ uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
+ NetworkDnsEventReported* event) {
+ assert(event != nullptr);
res_state res = res_get_state();
- res_setnetcontext(res, netContext);
+ res_setnetcontext(res, netContext, event);
_resolv_populate_res_for_net(res);
*rcode = NOERROR;
return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags);
diff --git a/resolv/res_send.h b/resolv/res_send.h
index 7fc77cb..fb80160 100644
--- a/resolv/res_send.h
+++ b/resolv/res_send.h
@@ -17,7 +17,9 @@
#pragma once
#include "netd_resolv/resolv.h" // struct android_net_context
+#include "stats.pb.h"
// Query dns with raw msg
int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen,
- uint8_t* ans, int ansLen, int* rcode, uint32_t flags);
+ uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
+ android::net::NetworkDnsEventReported* event);
diff --git a/resolv/resolv_cache.h b/resolv/resolv_cache.h
index 0056076..df21fb3 100644
--- a/resolv/resolv_cache.h
+++ b/resolv/resolv_cache.h
@@ -32,10 +32,13 @@
#include <stddef.h>
+#include <unordered_map>
#include <vector>
struct __res_state;
+constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
+
/* sets the name server addresses to the provided res_state structure. The
* name servers are retrieved from the cache which is associated
* with the network to which the res_state structure is associated */
@@ -43,6 +46,9 @@
std::vector<unsigned> resolv_list_caches();
+std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid);
+uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code);
+
typedef enum {
RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */
/* or the answer buffer is too small */
diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h
index 9fb0d48..3ecb6c1 100644
--- a/resolv/resolv_private.h
+++ b/resolv/resolv_private.h
@@ -64,6 +64,7 @@
#include "netd_resolv/resolv.h"
#include "netd_resolv/stats.h"
#include "resolv_static.h"
+#include "stats.pb.h"
// Linux defines MAXHOSTNAMELEN as 64, while the domain name limit in
// RFC 1034 and RFC 1035 is 255 octets.
@@ -118,6 +119,7 @@
} _u;
struct res_static rstatic[1];
bool use_local_nameserver; /* DNS-over-TLS bypass */
+ android::net::NetworkDnsEventReported* event;
};
typedef struct __res_state* res_state;
@@ -226,7 +228,8 @@
int res_getservers(res_state, sockaddr_union*, int);
struct android_net_context; /* forward */
-void res_setnetcontext(res_state, const struct android_net_context*);
+void res_setnetcontext(res_state, const struct android_net_context*,
+ android::net::NetworkDnsEventReported* event);
int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
addrinfo** result);
@@ -237,4 +240,16 @@
// switch resolver log severity
android::base::LogSeverity logSeverityStrToEnum(const std::string& logSeverityStr);
+template <typename Dest>
+Dest saturate_cast(int64_t x) {
+ using DestLimits = std::numeric_limits<Dest>;
+ if (x > DestLimits::max()) return DestLimits::max();
+ if (x < DestLimits::min()) return DestLimits::min();
+ return static_cast<Dest>(x);
+}
+
+android::net::NsType getQueryType(const uint8_t* msg, size_t msgLen);
+
+android::net::IpVersion ipFamilyToIPVersion(int ipFamily);
+
#endif // NETD_RESOLV_PRIVATE_H
diff --git a/resolv/stats.proto b/resolv/stats.proto
index dd9992c..60e2071 100644
--- a/resolv/stats.proto
+++ b/resolv/stats.proto
@@ -68,6 +68,8 @@
// NS_R_BADSIG = 16,
NS_R_BADKEY = 17;
NS_R_BADTIME = 18;
+ NS_R_INTERNAL_ERROR = 254;
+ NS_R_TIMEOUT = 255;
}
// Currently defined type values for resources and queries.
@@ -144,11 +146,11 @@
IV_IPV6 = 2;
}
-enum TransportType {
- TT_UNKNOWN = 0;
- TT_UDP = 1;
- TT_TCP = 2;
- TT_DOT = 3;
+enum Protocol {
+ PROTO_UNKNOWN = 0;
+ PROTO_UDP = 1;
+ PROTO_TCP = 2;
+ PROTO_DOT = 3;
}
enum PrivateDnsModes {
@@ -158,21 +160,22 @@
PDM_STRICT = 3;
}
-enum Transport {
+enum NetworkType {
+ NT_UNKNOWN = 0;
// Indicates this network uses a Cellular transport.
- TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR
+ NT_CELLULAR = 1;
// Indicates this network uses a Wi-Fi transport.
- TRANSPORT_WIFI = 1;
+ NT_WIFI = 2;
// Indicates this network uses a Bluetooth transport.
- TRANSPORT_BLUETOOTH = 2;
+ NT_BLUETOOTH = 3;
// Indicates this network uses an Ethernet transport.
- TRANSPORT_ETHERNET = 3;
+ NT_ETHERNET = 4;
// Indicates this network uses a VPN transport.
- TRANSPORT_VPN = 4;
+ NT_VPN = 5;
// Indicates this network uses a Wi-Fi Aware transport.
- TRANSPORT_WIFI_AWARE = 5;
+ NT_WIFI_AWARE = 6;
// Indicates this network uses a LoWPAN transport.
- TRANSPORT_LOWPAN = 6;
+ NT_LOWPAN = 7;
}
enum CacheStatus{
@@ -196,13 +199,13 @@
optional IpVersion ip_version = 4;
- optional TransportType transport = 5;
+ optional Protocol protocol = 5;
// Number of DNS query retry times
optional int32 retry_times = 6;
// Ordinal number of name server.
- optional int32 dns_server_count = 7;
+ optional int32 dns_server_index = 7;
// Used only by TCP and DOT. True for new connections.
optional bool connected = 8;
@@ -238,7 +241,7 @@
// Only valid for event_type = EVENT_RESNSEND.
optional int32 res_nsend_flags = 5;
- optional Transport network_type = 6;
+ optional NetworkType network_type = 6;
// The DNS over TLS mode on a specific netId.
optional PrivateDnsModes private_dns_modes = 7;
@@ -246,4 +249,7 @@
// Additional pass-through fields opaque to statsd.
// The DNS resolver Mainline module can add new fields here without requiring an OS update.
optional DnsQueryEvents dns_query_events = 8;
-}
\ No newline at end of file
+
+ // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom.
+ optional int32 sampling_rate_denom = 9;
+}