Merge "Sort and group network stats collected from eBPF."
diff --git a/libnetdbpf/Android.bp b/libnetdbpf/Android.bp
index 64a6b66..8592e6f 100644
--- a/libnetdbpf/Android.bp
+++ b/libnetdbpf/Android.bp
@@ -43,7 +43,7 @@
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_android",
"libutils",
"liblog",
"libnetdutils",
@@ -76,7 +76,7 @@
static_libs: ["libgmock"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_android",
"liblog",
"libnetdbpf",
"libnetdutils",
diff --git a/resolv/Android.bp b/resolv/Android.bp
index 252264d..5e88ab8 100644
--- a/resolv/Android.bp
+++ b/resolv/Android.bp
@@ -22,16 +22,17 @@
"DnsTlsServer.cpp",
"DnsTlsSessionCache.cpp",
"DnsTlsSocket.cpp",
+ "PrivateDnsConfiguration.cpp",
],
// Link everything statically (except for libc) to minimize our dependence
// on system ABIs
stl: "libc++_static",
static_libs: [
"libbase",
+ "libcrypto",
"liblog",
"libnetdutils",
"libssl",
- "libcrypto",
],
export_include_dirs: ["include"],
// TODO: pie in the sky: make this code clang-tidy clean
@@ -48,17 +49,28 @@
cc_test {
name: "libnetd_resolv_test",
defaults: ["netd_defaults"],
+ // Add DnsTls* files since they are not visible outside libnetd_resolv library.
srcs: [
+ "DnsTlsDispatcher.cpp",
+ "DnsTlsQueryMap.cpp",
+ "DnsTlsTransport.cpp",
+ "DnsTlsServer.cpp",
+ "DnsTlsSessionCache.cpp",
+ "DnsTlsSocket.cpp",
"dns_tls_test.cpp",
],
+ // TODO: Remove the include path "system/netd/include" after Fwmark used in DnsTlsTransport is
+ // moved out.
include_dirs: [
+ "system/netd/include",
+ "system/netd/resolv/include",
"system/netd/server",
],
shared_libs: [
"libbase",
+ "libcrypto",
"liblog",
"libnetdutils",
- "libnetd_resolv",
"libssl",
],
}
diff --git a/resolv/PrivateDnsConfiguration.cpp b/resolv/PrivateDnsConfiguration.cpp
new file mode 100644
index 0000000..b854a38
--- /dev/null
+++ b/resolv/PrivateDnsConfiguration.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "PrivateDnsConfiguration"
+#define DBG 0
+
+#include <log/log.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "netd_resolv/DnsTlsTransport.h"
+#include "netd_resolv/PrivateDnsConfiguration.h"
+#include "netdutils/BackoffSequence.h"
+
+int resolv_set_private_dns_for_net(unsigned netid, uint32_t mark, const char** servers,
+ const unsigned numServers, const char* tlsName,
+ const uint8_t** fingerprints, const unsigned numFingerprint) {
+ std::vector<std::string> tlsServers;
+ for (unsigned i = 0; i < numServers; i++) {
+ tlsServers.push_back(std::string(servers[i]));
+ }
+
+ std::set<std::vector<uint8_t>> tlsFingerprints;
+ for (unsigned i = 0; i < numFingerprint; i++) {
+ // Each fingerprint stored are 32(SHA256_SIZE) bytes long.
+ tlsFingerprints.emplace(std::vector<uint8_t>(fingerprints[i], fingerprints[i] + 32));
+ }
+
+ return android::net::gPrivateDnsConfiguration.set(netid, mark, tlsServers, std::string(tlsName),
+ tlsFingerprints);
+}
+
+void resolv_delete_private_dns_for_net(unsigned netid) {
+ android::net::gPrivateDnsConfiguration.clear(netid);
+}
+
+void resolv_get_private_dns_status_for_net(unsigned netid, ExternalPrivateDnsStatus* status) {
+ android::net::gPrivateDnsConfiguration.getStatus(netid, status);
+}
+
+void resolv_register_private_dns_callback(private_dns_validated_callback callback) {
+ android::net::gPrivateDnsConfiguration.setCallback(callback);
+}
+
+namespace android {
+
+using android::netdutils::BackoffSequence;
+
+namespace net {
+
+std::string addrToString(const sockaddr_storage* addr) {
+ char out[INET6_ADDRSTRLEN] = {0};
+ getnameinfo((const sockaddr*) addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
+ NI_NUMERICHOST);
+ return std::string(out);
+}
+
+bool parseServer(const char* server, sockaddr_storage* parsed) {
+ addrinfo hints = {.ai_family = AF_UNSPEC, .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV};
+ addrinfo* res;
+
+ int err = getaddrinfo(server, "853", &hints, &res);
+ if (err != 0) {
+ ALOGW("Failed to parse server address (%s): %s", server, gai_strerror(err));
+ return false;
+ }
+
+ memcpy(parsed, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return true;
+}
+
+void PrivateDnsConfiguration::setCallback(private_dns_validated_callback callback) {
+ if (mCallback == nullptr) {
+ mCallback = callback;
+ }
+}
+
+int PrivateDnsConfiguration::set(int32_t netId, uint32_t mark,
+ const std::vector<std::string>& servers, const std::string& name,
+ const std::set<std::vector<uint8_t>>& fingerprints) {
+ if (DBG) {
+ ALOGD("PrivateDnsConfiguration::set(%u, %zu, %s, %zu)", netId, servers.size(), name.c_str(),
+ fingerprints.size());
+ }
+
+ const bool explicitlyConfigured = !name.empty() || !fingerprints.empty();
+
+ // Parse the list of servers that has been passed in
+ std::set<DnsTlsServer> tlsServers;
+ for (size_t i = 0; i < servers.size(); ++i) {
+ sockaddr_storage parsed;
+ if (!parseServer(servers[i].c_str(), &parsed)) {
+ return -EINVAL;
+ }
+ DnsTlsServer server(parsed);
+ server.name = name;
+ server.fingerprints = fingerprints;
+ tlsServers.insert(server);
+ }
+
+ std::lock_guard guard(mPrivateDnsLock);
+ if (explicitlyConfigured) {
+ mPrivateDnsModes[netId] = PrivateDnsMode::STRICT;
+ } else if (!tlsServers.empty()) {
+ mPrivateDnsModes[netId] = PrivateDnsMode::OPPORTUNISTIC;
+ } else {
+ mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
+ mPrivateDnsTransports.erase(netId);
+ return 0;
+ }
+
+ // Create the tracker if it was not present
+ auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair == mPrivateDnsTransports.end()) {
+ // No TLS tracker yet for this netId.
+ bool added;
+ std::tie(netPair, added) = mPrivateDnsTransports.emplace(netId, PrivateDnsTracker());
+ if (!added) {
+ ALOGE("Memory error while recording private DNS for netId %d", netId);
+ return -ENOMEM;
+ }
+ }
+ auto& tracker = netPair->second;
+
+ // Remove any servers from the tracker that are not in |servers| exactly.
+ for (auto it = tracker.begin(); it != tracker.end();) {
+ if (tlsServers.count(it->first) == 0) {
+ it = tracker.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Add any new or changed servers to the tracker, and initiate async checks for them.
+ for (const auto& server : tlsServers) {
+ if (needsValidation(tracker, server)) {
+ validatePrivateDnsProvider(server, tracker, netId, mark);
+ }
+ }
+ return 0;
+}
+
+PrivateDnsStatus PrivateDnsConfiguration::getStatus(unsigned netId) {
+ PrivateDnsStatus status{PrivateDnsMode::OFF, {}};
+
+ // This mutex is on the critical path of every DNS lookup.
+ //
+ // If the overhead of mutex acquisition proves too high, we could reduce
+ // it by maintaining an atomic_int32_t counter of TLS-enabled netids, or
+ // by using an RWLock.
+ std::lock_guard guard(mPrivateDnsLock);
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) return status;
+ status.mode = mode->second;
+
+ const auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair != mPrivateDnsTransports.end()) {
+ for (const auto& serverPair : netPair->second) {
+ if (serverPair.second == Validation::success) {
+ status.validatedServers.push_back(serverPair.first);
+ }
+ }
+ }
+
+ return status;
+}
+
+void PrivateDnsConfiguration::getStatus(unsigned netId, ExternalPrivateDnsStatus* status) {
+ // This mutex is on the critical path of every DNS lookup.
+ //
+ // If the overhead of mutex acquisition proves too high, we could reduce
+ // it by maintaining an atomic_int32_t counter of TLS-enabled netids, or
+ // by using an RWLock.
+ std::lock_guard guard(mPrivateDnsLock);
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) return;
+ status->mode = mode->second;
+
+ const auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair != mPrivateDnsTransports.end()) {
+ status->numServers = netPair->second.size();
+ int count = 0;
+ for (const auto& serverPair : netPair->second) {
+ status->serverStatus[count].ss = serverPair.first.ss;
+ status->serverStatus[count].hostname =
+ serverPair.first.name.empty() ? "" : serverPair.first.name.c_str();
+ status->serverStatus[count].validation = serverPair.second;
+ /*
+ unsigned numFingerprint = 0;
+ for (const auto& fp : serverPair.first.fingerprints) {
+ std::copy(
+ fp.begin(), fp.end(),
+ status->serverStatus[count].fingerprints.fingerprint[numFingerprint].data);
+ numFingerprint++;
+ }
+ status->serverStatus[count].fingerprints.num = numFingerprint;
+ */
+ count++;
+ }
+ }
+}
+
+void PrivateDnsConfiguration::clear(unsigned netId) {
+ if (DBG) {
+ ALOGD("PrivateDnsConfiguration::clear(%u)", netId);
+ }
+ std::lock_guard guard(mPrivateDnsLock);
+ mPrivateDnsModes.erase(netId);
+ mPrivateDnsTransports.erase(netId);
+}
+
+void PrivateDnsConfiguration::validatePrivateDnsProvider(const DnsTlsServer& server,
+ PrivateDnsTracker& tracker, unsigned netId,
+ uint32_t mark) REQUIRES(mPrivateDnsLock) {
+ if (DBG) {
+ ALOGD("validatePrivateDnsProvider(%s, %u)", addrToString(&(server.ss)).c_str(), netId);
+ }
+
+ tracker[server] = Validation::in_process;
+ if (DBG) {
+ ALOGD("Server %s marked as in_process. Tracker now has size %zu",
+ addrToString(&(server.ss)).c_str(), tracker.size());
+ }
+ // Note that capturing |server| and |netId| in this lambda create copies.
+ std::thread validate_thread([this, server, netId, mark] {
+ // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
+ //
+ // Start with a 1 minute delay and backoff to once per hour.
+ //
+ // Assumptions:
+ // [1] Each TLS validation is ~10KB of certs+handshake+payload.
+ // [2] Network typically provision clients with <=4 nameservers.
+ // [3] Average month has 30 days.
+ //
+ // Each validation pass in a given hour is ~1.2MB of data. And 24
+ // such validation passes per day is about ~30MB per month, in the
+ // worst case. Otherwise, this will cost ~600 SYNs per month
+ // (6 SYNs per ip, 4 ips per validation pass, 24 passes per day).
+ auto backoff = BackoffSequence<>::Builder()
+ .withInitialRetransmissionTime(std::chrono::seconds(60))
+ .withMaximumRetransmissionTime(std::chrono::seconds(3600))
+ .build();
+
+ while (true) {
+ // ::validate() is a blocking call that performs network operations.
+ // It can take milliseconds to minutes, up to the SYN retry limit.
+ const bool success = DnsTlsTransport::validate(server, netId, mark);
+ if (DBG) {
+ ALOGD("validateDnsTlsServer returned %d for %s", success,
+ addrToString(&(server.ss)).c_str());
+ }
+
+ const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success);
+ if (!needs_reeval) {
+ break;
+ }
+
+ if (backoff.hasNextTimeout()) {
+ std::this_thread::sleep_for(backoff.getNextTimeout());
+ } else {
+ break;
+ }
+ }
+ });
+ validate_thread.detach();
+}
+
+bool PrivateDnsConfiguration::recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId,
+ bool success) {
+ constexpr bool NEEDS_REEVALUATION = true;
+ constexpr bool DONT_REEVALUATE = false;
+
+ std::lock_guard guard(mPrivateDnsLock);
+
+ auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair == mPrivateDnsTransports.end()) {
+ ALOGW("netId %u was erased during private DNS validation", netId);
+ return DONT_REEVALUATE;
+ }
+
+ const auto mode = mPrivateDnsModes.find(netId);
+ if (mode == mPrivateDnsModes.end()) {
+ ALOGW("netId %u has no private DNS validation mode", netId);
+ return DONT_REEVALUATE;
+ }
+ const bool modeDoesReevaluation = (mode->second == PrivateDnsMode::STRICT);
+
+ bool reevaluationStatus =
+ (success || !modeDoesReevaluation) ? DONT_REEVALUATE : NEEDS_REEVALUATION;
+
+ auto& tracker = netPair->second;
+ auto serverPair = tracker.find(server);
+ if (serverPair == tracker.end()) {
+ ALOGW("Server %s was removed during private DNS validation",
+ addrToString(&(server.ss)).c_str());
+ success = false;
+ reevaluationStatus = DONT_REEVALUATE;
+ } else if (!(serverPair->first == server)) {
+ // TODO: It doesn't seem correct to overwrite the tracker entry for
+ // |server| down below in this circumstance... Fix this.
+ ALOGW("Server %s was changed during private DNS validation",
+ addrToString(&(server.ss)).c_str());
+ success = false;
+ reevaluationStatus = DONT_REEVALUATE;
+ }
+
+ // Invoke the callback to send a validation event to NetdEventListenerService.
+ if (mCallback != nullptr) {
+ const char* ipLiteral = addrToString(&(server.ss)).c_str();
+ const char* hostname = server.name.empty() ? "" : server.name.c_str();
+ mCallback(netId, ipLiteral, hostname, success);
+ }
+
+ if (success) {
+ tracker[server] = Validation::success;
+ if (DBG) {
+ ALOGD("Validation succeeded for %s! Tracker now has %zu entries.",
+ addrToString(&(server.ss)).c_str(), tracker.size());
+ }
+ } else {
+ // Validation failure is expected if a user is on a captive portal.
+ // TODO: Trigger a second validation attempt after captive portal login
+ // succeeds.
+ tracker[server] = (reevaluationStatus == NEEDS_REEVALUATION) ? Validation::in_process
+ : Validation::fail;
+ if (DBG) {
+ ALOGD("Validation failed for %s!", addrToString(&(server.ss)).c_str());
+ }
+ }
+
+ return reevaluationStatus;
+}
+
+// Start validation for newly added servers as well as any servers that have
+// landed in Validation::fail state. Note that servers that have failed
+// multiple validation attempts but for which there is still a validating
+// thread running are marked as being Validation::in_process.
+bool PrivateDnsConfiguration::needsValidation(const PrivateDnsTracker& tracker,
+ const DnsTlsServer& server) {
+ const auto& iter = tracker.find(server);
+ return (iter == tracker.end()) || (iter->second == Validation::fail);
+}
+
+PrivateDnsConfiguration gPrivateDnsConfiguration;
+
+} // namespace net
+} // namespace android
diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp
index 0364be4..c229588 100644
--- a/resolv/getaddrinfo.cpp
+++ b/resolv/getaddrinfo.cpp
@@ -127,24 +127,23 @@
int e_af;
int e_socktype;
int e_protocol;
- const char* e_protostr;
int e_wild;
#define WILD_AF(ex) ((ex)->e_wild & 0x01)
#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
};
-static const struct explore explore[] = {
- {PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07},
- {PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07},
- {PF_INET6, SOCK_RAW, ANY, NULL, 0x05},
- {PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07},
- {PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07},
- {PF_INET, SOCK_RAW, ANY, NULL, 0x05},
- {PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07},
- {PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07},
- {PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05},
- {-1, 0, 0, NULL, 0},
+static const struct explore explore_options[] = {
+ {PF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_INET6, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_INET6, SOCK_RAW, ANY, 0x05},
+ {PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_INET, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_INET, SOCK_RAW, ANY, 0x05},
+ {PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0x07},
+ {PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0x07},
+ {PF_UNSPEC, SOCK_RAW, ANY, 0x05},
+ {-1, 0, 0, 0},
};
#define PTON_MAX 16
@@ -230,20 +229,6 @@
if (error != 0) goto free; \
} while (0)
-#define GET_CANONNAME(ai, str) \
- do { \
- /* external reference: pai, error and label free */ \
- error = get_canonname(pai, (ai), (str)); \
- if (error != 0) goto free; \
- } while (0)
-
-#define ERR(err) \
- do { \
- /* external reference: error, and label bad */ \
- error = (err); \
- goto bad; \
- } while (0)
-
#define MATCH_FAMILY(x, y, w) \
((x) == (y) || ((w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
#define MATCH(x, y, w) ((x) == (y) || ((w) && ((x) == ANY || (y) == ANY)))
@@ -334,7 +319,7 @@
const struct addrinfo* hints,
const struct android_net_context* netcontext,
struct addrinfo** res) {
- struct addrinfo sentinel;
+ struct addrinfo sentinel = {};
struct addrinfo* cur;
int error = 0;
struct addrinfo ai;
@@ -347,7 +332,6 @@
/* hints is allowed to be NULL */
assert(res != NULL);
assert(netcontext != NULL);
- memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
pai = &ai;
pai->ai_flags = 0;
@@ -358,137 +342,149 @@
pai->ai_canonname = NULL;
pai->ai_addr = NULL;
pai->ai_next = NULL;
-
- if (hostname == NULL && servname == NULL) return EAI_NONAME;
- if (hints) {
- /* error check for hints */
- if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next)
- ERR(EAI_BADHINTS); /* xxx */
- if (hints->ai_flags & ~AI_MASK) ERR(EAI_BADFLAGS);
- switch (hints->ai_family) {
- case PF_UNSPEC:
- case PF_INET:
- case PF_INET6:
- break;
- default:
- ERR(EAI_FAMILY);
+ do {
+ if (hostname == NULL && servname == NULL) {
+ error = EAI_NONAME;
+ break;
}
- memcpy(pai, hints, sizeof(*pai));
+ if (hints) {
+ /* error check for hints */
+ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) {
+ error = EAI_BADHINTS;
+ break;
+ }
+ if (hints->ai_flags & ~AI_MASK) {
+ error = EAI_BADFLAGS;
+ break;
+ }
- /*
- * if both socktype/protocol are specified, check if they
- * are meaningful combination.
- */
- if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
- for (ex = explore; ex->e_af >= 0; ex++) {
- if (pai->ai_family != ex->e_af) continue;
- if (ex->e_socktype == ANY) continue;
- if (ex->e_protocol == ANY) continue;
- if (pai->ai_socktype == ex->e_socktype && pai->ai_protocol != ex->e_protocol) {
- ERR(EAI_BADHINTS);
+ if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET ||
+ hints->ai_family == PF_INET6)) {
+ error = EAI_FAMILY;
+ break;
+ }
+ *pai = *hints;
+
+ /*
+ * if both socktype/protocol are specified, check if they
+ * are meaningful combination.
+ */
+ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
+ for (ex = explore_options; ex->e_af >= 0; ex++) {
+ if (pai->ai_family != ex->e_af) continue;
+ if (ex->e_socktype == ANY) continue;
+ if (ex->e_protocol == ANY) continue;
+ if (pai->ai_socktype == ex->e_socktype && pai->ai_protocol != ex->e_protocol) {
+ error = EAI_BADHINTS;
+ break;
+ }
}
+ if (error) break;
}
}
- }
- /*
- * check for special cases. (1) numeric servname is disallowed if
- * socktype/protocol are left unspecified. (2) servname is disallowed
- * for raw and other inet{,6} sockets.
- */
- if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
- || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
- ) {
- ai0 = *pai; /* backup *pai */
+ /*
+ * check for special cases. (1) numeric servname is disallowed if
+ * socktype/protocol are left unspecified. (2) servname is disallowed
+ * for raw and other inet{,6} sockets.
+ */
+ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
+ || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
+ ) {
+ ai0 = *pai; /* backup *pai */
- if (pai->ai_family == PF_UNSPEC) {
- pai->ai_family = PF_INET6;
- }
- error = get_portmatch(pai, servname);
- if (error) ERR(error);
+ if (pai->ai_family == PF_UNSPEC) {
+ pai->ai_family = PF_INET6;
+ }
+ error = get_portmatch(pai, servname);
+ if (error) break;
- *pai = ai0;
- }
-
- ai0 = *pai;
-
- /* NULL hostname, or numeric hostname */
- for (ex = explore; ex->e_af >= 0; ex++) {
- *pai = ai0;
-
- /* PF_UNSPEC entries are prepared for DNS queries only */
- if (ex->e_af == PF_UNSPEC) continue;
-
- if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue;
- if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue;
- if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue;
-
- if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af;
- if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype;
- if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol;
-
- if (hostname == NULL)
- error = explore_null(pai, servname, &cur->ai_next);
- else
- error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
-
- if (error) goto free;
-
- while (cur->ai_next) cur = cur->ai_next;
- }
-
- /*
- * XXX
- * If numeric representation of AF1 can be interpreted as FQDN
- * representation of AF2, we need to think again about the code below.
- */
- if (sentinel.ai_next) goto good;
-
- if (hostname == NULL) ERR(EAI_NODATA);
- if (pai->ai_flags & AI_NUMERICHOST) ERR(EAI_NONAME);
-
- /*
- * hostname as alphabetical name.
- * we would like to prefer AF_INET6 than AF_INET, so we'll make a
- * outer loop by AFs.
- */
- for (ex = explore; ex->e_af >= 0; ex++) {
- *pai = ai0;
-
- /* require exact match for family field */
- if (pai->ai_family != ex->e_af) continue;
-
- if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) {
- continue;
- }
- if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) {
- continue;
+ *pai = ai0;
}
- if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype;
- if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol;
+ ai0 = *pai;
- error = explore_fqdn(pai, hostname, servname, &cur->ai_next, netcontext);
+ /* NULL hostname, or numeric hostname */
+ for (ex = explore_options; ex->e_af >= 0; ex++) {
+ *pai = ai0;
- while (cur && cur->ai_next) cur = cur->ai_next;
- }
+ /* PF_UNSPEC entries are prepared for DNS queries only */
+ if (ex->e_af == PF_UNSPEC) continue;
- /* XXX */
- if (sentinel.ai_next) error = 0;
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue;
+ if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue;
+ if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue;
- if (error) goto free;
- if (error == 0) {
+ if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af;
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol;
+
+ if (hostname == NULL)
+ error = explore_null(pai, servname, &cur->ai_next);
+ else
+ error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
+
+ if (error) break;
+
+ while (cur->ai_next) cur = cur->ai_next;
+ }
+ if (error) break;
+
+ /*
+ * XXX
+ * If numeric representation of AF1 can be interpreted as FQDN
+ * representation of AF2, we need to think again about the code below.
+ */
+ if (sentinel.ai_next) break;
+
+ if (hostname == NULL) {
+ error = EAI_NODATA;
+ break;
+ }
+ if (pai->ai_flags & AI_NUMERICHOST) {
+ error = EAI_NONAME;
+ break;
+ }
+
+ /*
+ * hostname as alphabetical name.
+ * we would like to prefer AF_INET6 than AF_INET, so we'll make a
+ * outer loop by AFs.
+ */
+ for (ex = explore_options; ex->e_af >= 0; ex++) {
+ *pai = ai0;
+
+ /* require exact match for family field */
+ if (pai->ai_family != ex->e_af) continue;
+
+ if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) {
+ continue;
+ }
+ if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) {
+ continue;
+ }
+
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol;
+
+ error = explore_fqdn(pai, hostname, servname, &cur->ai_next, netcontext);
+
+ while (cur->ai_next) cur = cur->ai_next;
+ }
+
if (sentinel.ai_next) {
- good:
- *res = sentinel.ai_next;
- return 0;
- } else
+ error = 0;
+ } else if (error == 0) {
error = EAI_FAIL;
+ }
+ } while (0);
+
+ if (error) {
+ freeaddrinfo(sentinel.ai_next);
+ *res = NULL;
+ } else {
+ *res = sentinel.ai_next;
}
-free:
-bad:
- freeaddrinfo(sentinel.ai_next);
- *res = NULL;
return error;
}
@@ -566,15 +562,9 @@
if (pai->ai_flags & AI_PASSIVE) {
GET_AI(cur->ai_next, afd, afd->a_addrany);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "anyaddr");
- */
GET_PORT(cur->ai_next, servname);
} else {
GET_AI(cur->ai_next, afd, afd->a_loopback);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "localhost");
- */
GET_PORT(cur->ai_next, servname);
}
cur = cur->ai_next;
@@ -625,18 +615,21 @@
* the canonical name, based on a
* clarification in rfc2553bis-03.
*/
- GET_CANONNAME(cur->ai_next, canonname);
+ error = get_canonname(pai, cur->ai_next, canonname);
+ if (error != 0) {
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+ }
}
while (cur->ai_next) cur = cur->ai_next;
} else
- ERR(EAI_FAMILY); /*xxx*/
+ return EAI_FAMILY;
}
*res = sentinel.ai_next;
return 0;
free:
-bad:
freeaddrinfo(sentinel.ai_next);
return error;
}
@@ -886,7 +879,8 @@
static struct addrinfo* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
const struct addrinfo* pai) {
- struct addrinfo sentinel, *cur;
+ struct addrinfo sentinel = {};
+ struct addrinfo *cur;
struct addrinfo ai;
const struct afd* afd;
char* canonname;
@@ -905,7 +899,6 @@
assert(qname != NULL);
assert(pai != NULL);
- memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
canonname = NULL;
@@ -1439,14 +1432,13 @@
static int dns_getaddrinfo(const char* name, const addrinfo* pai,
const android_net_context* netcontext, addrinfo** rv) {
- struct addrinfo* ai;
- struct addrinfo sentinel, *cur;
+ struct addrinfo *ai, *cur;
+ struct addrinfo sentinel = {};
struct res_target q, q2;
res_state res;
memset(&q, 0, sizeof(q));
memset(&q2, 0, sizeof(q2));
- memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
querybuf* buf = (querybuf*) malloc(sizeof(*buf));
@@ -1526,7 +1518,6 @@
*/
res_setnetcontext(res, netcontext);
if (res_searchN(name, &q, res) < 0) {
- res_put_state(res);
free(buf);
free(buf2);
return EAI_NODATA; // TODO: Decode error from h_errno like we do below
@@ -1543,7 +1534,6 @@
free(buf);
free(buf2);
if (sentinel.ai_next == NULL) {
- res_put_state(res);
switch (h_errno) {
case HOST_NOT_FOUND:
return EAI_NODATA;
@@ -1556,8 +1546,6 @@
_rfc6724_sort(&sentinel, netcontext->app_mark, netcontext->uid);
- res_put_state(res);
-
*rv = sentinel.ai_next;
return 0;
}
@@ -1629,11 +1617,10 @@
}
static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res) {
- struct addrinfo sentinel, *cur;
- struct addrinfo* p;
+ struct addrinfo sentinel = {};
+ struct addrinfo *p, *cur;
FILE* hostf = NULL;
- memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
_sethtent(&hostf);
diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp
index 834a962..b36f5c8 100644
--- a/resolv/gethnamaddr.cpp
+++ b/resolv/gethnamaddr.cpp
@@ -137,10 +137,13 @@
static bool _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
const android_net_context* netcontext, getnamaddr* info);
-static bool _dns_gethtbyname(const char* name, int af, getnamaddr* info);
+static int _dns_gethtbyname(const char* name, int af, getnamaddr* info);
-static struct hostent* gethostbyname_internal(const char*, int, res_state, struct hostent*, char*,
- size_t, int*, const struct android_net_context*);
+static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
+ size_t hbuflen, int* errorp,
+ const android_net_context* netcontext);
+static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
+ char* buf, size_t buflen, int* he);
static struct hostent* android_gethostbyaddrfornetcontext_proxy_internal(
const void*, socklen_t, int, struct hostent*, char*, size_t, int*,
const struct android_net_context*);
@@ -466,10 +469,9 @@
return NULL;
}
-static struct hostent* gethostbyname_internal_real(const char* name, int af, res_state res,
- struct hostent* hp, char* buf, size_t buflen,
- int* he) {
- struct getnamaddr info;
+static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
+ char* buf, size_t buflen, int* he) {
+ getnamaddr info;
size_t size;
_DIAGASSERT(name != NULL);
@@ -484,7 +486,7 @@
default:
*he = NETDB_INTERNAL;
errno = EAFNOSUPPORT;
- return NULL;
+ return EAI_FAMILY;
}
if (buflen < size) goto nospc;
@@ -530,16 +532,18 @@
info.buflen = buflen;
info.he = he;
if (!_hf_gethtbyname2(name, af, &info)) {
- if (!_dns_gethtbyname(name, af, &info)) {
- return NULL;
+ int error = _dns_gethtbyname(name, af, &info);
+ if (error != 0) {
+ return error;
}
}
*he = NETDB_SUCCESS;
- return hp;
+ return 0;
nospc:
*he = NETDB_INTERNAL;
errno = ENOSPC;
- return NULL;
+ // Bad arguments
+ return EAI_FAIL;
fake:
HENT_ARRAY(hp->h_addr_list, 1, buf, buflen);
HENT_ARRAY(hp->h_aliases, 0, buf, buflen);
@@ -549,7 +553,7 @@
if (inet_pton(af, name, buf) <= 0) {
*he = HOST_NOT_FOUND;
- return NULL;
+ return EAI_NODATA;
}
hp->h_addr_list[0] = buf;
hp->h_addr_list[1] = NULL;
@@ -558,14 +562,13 @@
HENT_SCOPY(hp->h_name, name, buf, buflen);
if (res->options & RES_USE_INET6) map_v4v6_hostent(hp, &buf, buf + buflen);
*he = NETDB_SUCCESS;
- return hp;
+ return 0;
}
// very similar in proxy-ness to android_getaddrinfo_proxy
-static struct hostent* gethostbyname_internal(const char* name, int af, res_state res,
- struct hostent* hp, char* hbuf, size_t hbuflen,
- int* errorp,
- const struct android_net_context* netcontext) {
+static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
+ size_t hbuflen, int* errorp,
+ const android_net_context* netcontext) {
res_setnetcontext(res, netcontext);
return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen, errorp);
}
@@ -691,7 +694,6 @@
af = AF_INET;
len = NS_INADDRSZ;
}
- res_put_state(res);
}
/* if this is not something we're looking for, skip it. */
@@ -841,7 +843,7 @@
}
}
-static bool _dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
+static int _dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
int n, type;
struct hostent* hp;
res_state res;
@@ -858,32 +860,37 @@
type = T_AAAA;
break;
default:
- return false;
+ return EAI_FAMILY;
}
querybuf* buf = (querybuf*) malloc(sizeof(querybuf));
if (buf == NULL) {
*info->he = NETDB_INTERNAL;
- return false;
+ return EAI_MEMORY;
}
res = res_get_state();
if (res == NULL) {
free(buf);
- return false;
+ return EAI_MEMORY;
}
n = res_nsearch(res, name, C_IN, type, buf->buf, (int) sizeof(buf->buf));
if (n < 0) {
free(buf);
debugprintf("res_nsearch failed (%d)\n", res, n);
- res_put_state(res);
- return false;
+ return EAI_NODATA;
}
hp = getanswer(buf, n, name, type, res, info->hp, info->buf, info->buflen, info->he);
free(buf);
- res_put_state(res);
if (hp == NULL) {
- return false;
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ return EAI_NODATA;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ default:
+ return EAI_FAIL;
+ }
}
- return true;
+ return 0;
}
static bool _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
@@ -940,13 +947,11 @@
if (n < 0) {
free(buf);
debugprintf("res_nquery failed (%d)\n", res, n);
- res_put_state(res);
return false;
}
hp = getanswer(buf, n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, info->he);
free(buf);
if (hp == NULL) {
- res_put_state(res);
return false;
}
@@ -970,7 +975,6 @@
memcpy(bf + NS_INADDRSZ, NAT64_PAD, sizeof(NAT64_PAD));
}
- res_put_state(res);
*info->he = NETDB_SUCCESS;
return true;
@@ -984,16 +988,18 @@
* Non-reentrant versions.
*/
-struct hostent* android_gethostbynamefornetcontext(const char* name, int af,
- const struct android_net_context* netcontext) {
- struct hostent* hp;
+int android_gethostbynamefornetcontext(const char* name, int af,
+ const struct android_net_context* netcontext, hostent** hp) {
+ int error;
res_state res = res_get_state();
- if (res == NULL) return NULL;
- struct res_static* rs = res_get_static(); // For thread-safety.
- hp = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
- &h_errno, netcontext);
- res_put_state(res);
- return hp;
+ if (res == NULL) return EAI_MEMORY;
+ res_static* rs = res_get_static(); // For thread-safety.
+ error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
+ &h_errno, netcontext);
+ if (error == 0) {
+ *hp = &rs->host;
+ }
+ return error;
}
struct hostent* android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,
diff --git a/resolv/include/netd_resolv/DnsTlsDispatcher.h b/resolv/include/netd_resolv/DnsTlsDispatcher.h
index 0bb19f2..45142b8 100644
--- a/resolv/include/netd_resolv/DnsTlsDispatcher.h
+++ b/resolv/include/netd_resolv/DnsTlsDispatcher.h
@@ -38,8 +38,8 @@
// This is a singleton class that manages the collection of active DnsTlsTransports.
// Queries made here are dispatched to an existing or newly constructed DnsTlsTransport.
-class LIBNETD_RESOLV_TLS_EXPORT DnsTlsDispatcher {
-public:
+class DnsTlsDispatcher {
+ public:
// Default constructor.
DnsTlsDispatcher();
diff --git a/resolv/include/netd_resolv/DnsTlsQueryMap.h b/resolv/include/netd_resolv/DnsTlsQueryMap.h
index 4c8010c..f013e01 100644
--- a/resolv/include/netd_resolv/DnsTlsQueryMap.h
+++ b/resolv/include/netd_resolv/DnsTlsQueryMap.h
@@ -35,8 +35,8 @@
// Keeps track of queries and responses. This class matches responses with queries.
// All methods are thread-safe and non-blocking.
-class LIBNETD_RESOLV_TLS_EXPORT DnsTlsQueryMap {
-public:
+class DnsTlsQueryMap {
+ public:
struct Query {
// The new ID number assigned to this query.
uint16_t newId;
diff --git a/resolv/include/netd_resolv/DnsTlsServer.h b/resolv/include/netd_resolv/DnsTlsServer.h
index 752dc5f..bb161f2 100644
--- a/resolv/include/netd_resolv/DnsTlsServer.h
+++ b/resolv/include/netd_resolv/DnsTlsServer.h
@@ -30,7 +30,7 @@
// DnsTlsServer represents a recursive resolver that supports, or may support, a
// secure protocol.
-struct LIBNETD_RESOLV_TLS_EXPORT DnsTlsServer {
+struct DnsTlsServer {
// Default constructor.
DnsTlsServer() {}
@@ -68,7 +68,7 @@
};
// This comparison only checks the IP address. It ignores ports, names, and fingerprints.
-struct LIBNETD_RESOLV_TLS_EXPORT AddressComparator {
+struct AddressComparator {
bool operator() (const DnsTlsServer& x, const DnsTlsServer& y) const;
};
diff --git a/resolv/include/netd_resolv/DnsTlsSocket.h b/resolv/include/netd_resolv/DnsTlsSocket.h
index 7190dd2..195705b 100644
--- a/resolv/include/netd_resolv/DnsTlsSocket.h
+++ b/resolv/include/netd_resolv/DnsTlsSocket.h
@@ -46,8 +46,8 @@
// or the destructor in a callback. Doing so will result in deadlocks.
// This class may call the observer at any time after initialize(), until the destructor
// returns (but not after).
-class LIBNETD_RESOLV_TLS_EXPORT DnsTlsSocket : public IDnsTlsSocket {
-public:
+class DnsTlsSocket : public IDnsTlsSocket {
+ public:
DnsTlsSocket(const DnsTlsServer& server, unsigned mark,
IDnsTlsSocketObserver* _Nonnull observer,
DnsTlsSessionCache* _Nonnull cache) :
diff --git a/resolv/include/netd_resolv/DnsTlsTransport.h b/resolv/include/netd_resolv/DnsTlsTransport.h
index 011be61..4590868 100644
--- a/resolv/include/netd_resolv/DnsTlsTransport.h
+++ b/resolv/include/netd_resolv/DnsTlsTransport.h
@@ -41,8 +41,8 @@
// Manages at most one DnsTlsSocket at a time. This class handles socket lifetime issues,
// such as reopening the socket and reissuing pending queries.
-class LIBNETD_RESOLV_TLS_EXPORT DnsTlsTransport : public IDnsTlsSocketObserver {
-public:
+class DnsTlsTransport : public IDnsTlsSocketObserver {
+ public:
DnsTlsTransport(const DnsTlsServer& server, unsigned mark,
IDnsTlsSocketFactory* _Nonnull factory) :
mMark(mark), mServer(server), mFactory(factory) {}
diff --git a/resolv/include/netd_resolv/PrivateDnsConfiguration.h b/resolv/include/netd_resolv/PrivateDnsConfiguration.h
new file mode 100644
index 0000000..cee0205
--- /dev/null
+++ b/resolv/include/netd_resolv/PrivateDnsConfiguration.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef NETD_RESOLV_PRIVATEDNSCONFIGURATION_H
+#define NETD_RESOLV_PRIVATEDNSCONFIGURATION_H
+
+#include <list>
+#include <map>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+
+#include "DnsTlsServer.h"
+#include "resolv.h"
+
+namespace android {
+namespace net {
+
+struct PrivateDnsStatus {
+ PrivateDnsMode mode;
+ std::list<DnsTlsServer> validatedServers;
+};
+
+class PrivateDnsConfiguration {
+ public:
+ int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers,
+ const std::string& name, const std::set<std::vector<uint8_t>>& fingerprints);
+
+ PrivateDnsStatus getStatus(unsigned netId);
+
+ // Externally used for netd.
+ void getStatus(unsigned netId, ExternalPrivateDnsStatus* status);
+
+ int getPrivateDnsModeAndStatus(unsigned netId);
+ void clear(unsigned netId);
+ void setCallback(private_dns_validated_callback callback);
+
+ private:
+ typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
+
+ void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
+ unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock);
+
+ bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success);
+
+ // Start validation for newly added servers as well as any servers that have
+ // landed in Validation::fail state. Note that servers that have failed
+ // multiple validation attempts but for which there is still a validating
+ // thread running are marked as being Validation::in_process.
+ bool needsValidation(const PrivateDnsTracker& tracker, const DnsTlsServer& server);
+
+ std::mutex mPrivateDnsLock;
+ std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock);
+ // Structure for tracking the validation status of servers on a specific netId.
+ // Using the AddressComparator ensures at most one entry per IP address.
+ std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
+ private_dns_validated_callback mCallback = nullptr;
+};
+
+extern PrivateDnsConfiguration gPrivateDnsConfiguration;
+
+} // namespace net
+} // namespace android
+
+#endif /* NETD_RESOLV_PRIVATEDNSCONFIGURATION_H */
diff --git a/resolv/include/netd_resolv/params.h b/resolv/include/netd_resolv/params.h
index c3f3363..939bfc4 100644
--- a/resolv/include/netd_resolv/params.h
+++ b/resolv/include/netd_resolv/params.h
@@ -36,17 +36,12 @@
int base_timeout_msec; // base query retry timeout (if 0, use RES_TIMEOUT)
};
-typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error } res_sendhookact;
+// The DNS over TLS mode on a specific netId.
+enum class PrivateDnsMode : uint8_t { OFF, OPPORTUNISTIC, STRICT };
-typedef res_sendhookact (*res_send_qhook)(struct sockaddr* const*, const uint8_t**, int*, uint8_t*,
- int, int*);
-
-typedef res_sendhookact (*res_send_rhook)(const struct sockaddr*, const uint8_t*, int, uint8_t*,
- int, int*);
+// Validation status of a DNS over TLS server (on a specific netId).
+enum class Validation : uint8_t { in_process, success, fail, unknown_server, unknown_netid };
#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
-// TODO: Remove it after we move PrivateDnsConfiguration and qhook() into libnetd_resolv.
-#define LIBNETD_RESOLV_TLS_EXPORT [[gnu::visibility("default")]]
-
#endif // NETD_RESOLV_PARAMS_H
diff --git a/resolv/include/netd_resolv/resolv.h b/resolv/include/netd_resolv/resolv.h
index 96133e5..9176996 100644
--- a/resolv/include/netd_resolv/resolv.h
+++ b/resolv/include/netd_resolv/resolv.h
@@ -75,7 +75,6 @@
unsigned dns_mark;
uid_t uid;
unsigned flags;
- res_send_qhook qhook;
};
#define NET_CONTEXT_INVALID_UID ((uid_t) -1)
@@ -83,10 +82,23 @@
#define NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS 0x00000001
#define NET_CONTEXT_FLAG_USE_EDNS 0x00000002
+struct ExternalPrivateDnsStatus {
+ PrivateDnsMode mode;
+ unsigned numServers;
+ struct PrivateDnsInfo {
+ sockaddr_storage ss;
+ const char* hostname;
+ Validation validation;
+ } serverStatus[MAXNS];
+};
+
+typedef void (*private_dns_validated_callback)(unsigned netid, const char* server,
+ const char* hostname, bool success);
+
LIBNETD_RESOLV_PUBLIC hostent* android_gethostbyaddrfornetcontext(const void*, socklen_t, int,
const android_net_context*);
-LIBNETD_RESOLV_PUBLIC hostent* android_gethostbynamefornetcontext(const char*, int,
- const android_net_context*);
+LIBNETD_RESOLV_PUBLIC int android_gethostbynamefornetcontext(const char*, int,
+ const android_net_context*, hostent**);
LIBNETD_RESOLV_PUBLIC int android_getaddrinfofornetcontext(const char*, const char*,
const addrinfo*,
const android_net_context*, addrinfo**);
@@ -96,6 +108,22 @@
unsigned numservers, const char* domains,
const __res_params* params);
+LIBNETD_RESOLV_PUBLIC int resolv_set_private_dns_for_net(unsigned netid, uint32_t mark,
+ const char** servers,
+ const unsigned numServers,
+ const char* tlsName,
+ const uint8_t** fingerprints,
+ const unsigned numFingerprints);
+
+LIBNETD_RESOLV_PUBLIC void resolv_delete_private_dns_for_net(unsigned netid);
+
+LIBNETD_RESOLV_PUBLIC void resolv_get_private_dns_status_for_net(unsigned netid,
+ ExternalPrivateDnsStatus* status);
+
+// Register callback to listen whether private DNS validated
+LIBNETD_RESOLV_PUBLIC void resolv_register_private_dns_callback(
+ private_dns_validated_callback callback);
+
// Flush the cache associated with a certain network
LIBNETD_RESOLV_PUBLIC void resolv_flush_cache_for_net(unsigned netid);
diff --git a/resolv/libnetd_resolv.map.txt b/resolv/libnetd_resolv.map.txt
index bb1e408..e139ab7 100644
--- a/resolv/libnetd_resolv.map.txt
+++ b/resolv/libnetd_resolv.map.txt
@@ -28,53 +28,10 @@
android_net_res_stats_get_usable_servers;
resolv_delete_cache_for_net;
resolv_set_nameservers_for_net;
-
- # These symbol names are generated by 'nm -D libnetd_resolv.so' with reverting the commit
- # (aosp/801454). They are temporarily necessary for netd to access DnsTls* classes before
- # we move qhook() and PrivateDnsConfiguration to libnetd_resolv library.
- # TODO: Remove them after we hide DnsTls* stuff thus netd no longer needs to access them.
- _ZN7android3net12DnsTlsSocket10initializeEv;
- _ZN7android3net12DnsTlsSocket10sslConnectEi;
- _ZN7android3net12DnsTlsSocket10tcpConnectEv;
- _ZN7android3net12DnsTlsSocket12readResponseEv;
- _ZN7android3net12DnsTlsSocket13sslDisconnectEv;
- _ZN7android3net12DnsTlsSocket4loopEv;
- _ZN7android3net12DnsTlsSocket5queryEtNS_9netdutils5SliceE;
- _ZN7android3net12DnsTlsSocket7sslReadENS_9netdutils5SliceEb;
- _ZN7android3net12DnsTlsSocket8sslWriteENS_9netdutils5SliceE;
- _ZN7android3net12DnsTlsSocket9sendQueryERKNS1_5QueryE;
- _ZN7android3net12DnsTlsSocketD0Ev;
- _ZN7android3net12DnsTlsSocketD1Ev;
- _ZN7android3net12DnsTlsSocketD2Ev;
- _ZN7android3net14DnsTlsQueryMap10onResponseENSt3__16vectorIhNS2_9allocatorIhEEEE;
- _ZN7android3net14DnsTlsQueryMap11recordQueryENS_9netdutils5SliceE;
- _ZN7android3net14DnsTlsQueryMap5clearEv;
- _ZN7android3net14DnsTlsQueryMap5emptyEv;
- _ZN7android3net14DnsTlsQueryMap6expireEPNS1_12QueryPromiseE;
- _ZN7android3net14DnsTlsQueryMap6getAllEv;
- _ZN7android3net14DnsTlsQueryMap7cleanupEv;
- _ZN7android3net14DnsTlsQueryMap9getFreeIdEv;
- _ZN7android3net14DnsTlsQueryMap9markTriedEt;
- _ZN7android3net15DnsTlsTransport10onResponseENSt3__16vectorIhNS2_9allocatorIhEEEE;
- _ZN7android3net15DnsTlsTransport11doReconnectEv;
- _ZN7android3net15DnsTlsTransport5queryENS_9netdutils5SliceE;
- _ZN7android3net15DnsTlsTransport8onClosedEv;
- _ZN7android3net15DnsTlsTransport8validateERKNS0_12DnsTlsServerEjj;
- _ZN7android3net15DnsTlsTransport9doConnectEv;
- _ZN7android3net15DnsTlsTransport9sendQueryENS0_14DnsTlsQueryMap5QueryE;
- _ZN7android3net15DnsTlsTransportD0Ev;
- _ZN7android3net15DnsTlsTransportD1Ev;
- _ZN7android3net15DnsTlsTransportD2Ev;
- _ZN7android3net16DnsTlsDispatcher5queryERKNS0_12DnsTlsServerEjNS_9netdutils5SliceES6_Pi;
- _ZN7android3net16DnsTlsDispatcher5queryERKNSt3__14listINS0_12DnsTlsServerENS2_9allocatorIS4_EEEEjNS_9netdutils5SliceESB_Pi;
- _ZN7android3net16DnsTlsDispatcherC1Ev;
- _ZN7android3net16DnsTlsDispatcherC2Ev;
- _ZNK7android3net12DnsTlsServer23wasExplicitlyConfiguredEv;
- _ZNK7android3net12DnsTlsServereqERKS1_;
- _ZNK7android3net12DnsTlsServerltERKS1_;
- _ZNK7android3net16DnsTlsDispatcher20getOrderedServerListERKNSt3__14listINS0_12DnsTlsServerENS2_9allocatorIS4_EEEEj;
- _ZNK7android3net17AddressComparatorclERKNS0_12DnsTlsServerES4_;
- _ZTVN7android3net15DnsTlsTransportE;
+ resolv_set_private_dns_for_net;
+ resolv_delete_private_dns_for_net;
+ resolv_get_private_dns_status_for_net;
+ resolv_register_private_dns_callback;
local:
*;
};
diff --git a/resolv/res_init.cpp b/resolv/res_init.cpp
index cfb6f08..06d4452 100644
--- a/resolv/res_init.cpp
+++ b/resolv/res_init.cpp
@@ -158,10 +158,9 @@
statp->pfcode = 0;
statp->_vcsock = -1;
statp->_flags = 0;
- statp->qhook = NULL;
- statp->rhook = NULL;
statp->_u._ext.nscount = 0;
statp->_u._ext.ext = (res_state_ext*) malloc(sizeof(*statp->_u._ext.ext));
+ statp->use_local_nameserver = false;
if (statp->_u._ext.ext != NULL) {
memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
@@ -419,9 +418,11 @@
if (statp != NULL) {
statp->netid = netcontext->dns_netid;
statp->_mark = netcontext->dns_mark;
- statp->qhook = netcontext->qhook;
if (netcontext->flags & NET_CONTEXT_FLAG_USE_EDNS) {
statp->options |= RES_USE_EDNS0 | RES_USE_DNSSEC;
}
+ if (netcontext->flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) {
+ statp->use_local_nameserver = true;
+ }
}
}
diff --git a/resolv/res_send.cpp b/resolv/res_send.cpp
index 5a79f28..4c0ca1a 100644
--- a/resolv/res_send.cpp
+++ b/resolv/res_send.cpp
@@ -100,6 +100,10 @@
#include <android-base/logging.h>
+#include <netdutils/Slice.h>
+#include "netd_resolv/DnsTlsDispatcher.h"
+#include "netd_resolv/DnsTlsTransport.h"
+#include "netd_resolv/PrivateDnsConfiguration.h"
#include "netd_resolv/resolv.h"
#include "netd_resolv/stats.h"
#include "private/android_filesystem_config.h"
@@ -107,6 +111,8 @@
#include "resolv_cache.h"
#include "resolv_private.h"
+// TODO: use the namespace something like android::netd_resolv for libnetd_resolv
+using namespace android::net;
#define VLOG if (!kVerboseLogging) {} else LOG(INFO)
@@ -134,6 +140,10 @@
}
#endif // DEBUG
+typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error } res_sendhookact;
+
+static DnsTlsDispatcher sDnsTlsDispatcher;
+
static int get_salen(const struct sockaddr*);
static struct sockaddr* get_nsaddr(res_state, size_t);
static int send_vc(res_state, struct __res_params* params, const u_char*, int, u_char*, int, int*,
@@ -146,6 +156,7 @@
static int connect_with_timeout(int sock, const struct sockaddr* nsap, socklen_t salen,
const struct timespec timeout);
static int retrying_poll(const int sock, short events, const struct timespec* finish);
+static res_sendhookact res_tls_send(res_state, const Slice query, const Slice answer, int* resplen);
/* BIONIC-BEGIN: implement source port randomization */
@@ -514,37 +525,40 @@
statp->_flags |= (ns << RES_F_LASTSHIFT);
same_ns:
- if (statp->qhook) {
- int done = 0, loops = 0;
+ int done = 0, loops = 0;
- do {
- res_sendhookact act;
+ do {
+ res_sendhookact act;
- act = (*statp->qhook)(&nsap, &buf, &buflen, ans, anssiz, &resplen);
- switch (act) {
- case res_goahead:
- done = 1;
+ // TODO: This function tries to query on all of private DNS servers. Putting
+ // it here is strange since we should expect there is only one DNS server
+ // being queried. Consider moving it to other reasonable place. In addition,
+ // enum res_sendhookact can be removed.
+ act = res_tls_send(statp, Slice(const_cast<u_char*>(buf), buflen),
+ Slice(ans, anssiz), &resplen);
+ switch (act) {
+ case res_goahead:
+ done = 1;
+ break;
+ case res_nextns:
+ res_nclose(statp);
+ goto next_ns;
+ case res_done:
+ if (cache_status == RESOLV_CACHE_NOTFOUND) {
+ _resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
+ }
+ return (resplen);
+ case res_modified:
+ /* give the hook another try */
+ if (++loops < 42) /*doug adams*/
break;
- case res_nextns:
- res_nclose(statp);
- goto next_ns;
- case res_done:
- if (cache_status == RESOLV_CACHE_NOTFOUND) {
- _resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
- }
- return (resplen);
- case res_modified:
- /* give the hook another try */
- if (++loops < 42) /*doug adams*/
- break;
- [[fallthrough]];
- case res_error:
- [[fallthrough]];
- default:
- goto fail;
- }
- } while (!done);
- }
+ [[fallthrough]];
+ case res_error:
+ [[fallthrough]];
+ default:
+ goto fail;
+ }
+ } while (!done);
[[maybe_unused]] static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
[[maybe_unused]] char abuf[NI_MAXHOST];
@@ -620,33 +634,6 @@
(statp->options & RES_STAYOPEN) == 0U) {
res_nclose(statp);
}
- if (statp->rhook) {
- int done = 0, loops = 0;
-
- do {
- res_sendhookact act;
-
- act = (*statp->rhook)(nsap, buf, buflen, ans, anssiz, &resplen);
- switch (act) {
- case res_goahead:
- case res_done:
- done = 1;
- break;
- case res_nextns:
- res_nclose(statp);
- goto next_ns;
- case res_modified:
- /* give the hook another try */
- if (++loops < 42) /*doug adams*/
- break;
- [[fallthrough]];
- case res_error:
- [[fallthrough]];
- default:
- goto fail;
- }
- } while (!done);
- }
return (resplen);
next_ns:;
} // for each ns
@@ -1249,3 +1236,59 @@
return 0;
}
}
+
+res_sendhookact res_tls_send(res_state statp, const Slice query, const Slice answer, int* resplen) {
+ const unsigned netId = statp->netid;
+ const unsigned mark = statp->_mark;
+
+ // TLS bypass
+ if (statp->use_local_nameserver) {
+ return res_goahead;
+ }
+
+ const auto privateDnsStatus = gPrivateDnsConfiguration.getStatus(netId);
+
+ if (privateDnsStatus.mode == PrivateDnsMode::OFF) return res_goahead;
+
+ if (privateDnsStatus.validatedServers.empty()) {
+ if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
+ return res_goahead;
+ } else {
+ // Sleep and iterate some small number of times checking for the
+ // arrival of resolved and validated server IP addresses, instead
+ // of returning an immediate error.
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(100ms);
+ return res_modified;
+ }
+ }
+
+ VLOG << __func__ << ": performing query over TLS";
+
+ const auto response = sDnsTlsDispatcher.query(privateDnsStatus.validatedServers, mark, query,
+ answer, resplen);
+ if (response == DnsTlsTransport::Response::success) {
+ VLOG << __func__ << ": success";
+ return res_done;
+ }
+
+ VLOG << __func__ << ": abort: TLS query failed: " << static_cast<int>(response);
+
+ if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
+ // In opportunistic mode, handle falling back to cleartext in some
+ // cases (DNS shouldn't fail if a validated opportunistic mode server
+ // becomes unreachable for some reason).
+ switch (response) {
+ case DnsTlsTransport::Response::network_error:
+ case DnsTlsTransport::Response::internal_error:
+ // Note: this will cause cleartext queries to be emitted, with
+ // all of the EDNS0 goodness enabled. Fingers crossed. :-/
+ return res_goahead;
+ default:
+ break;
+ }
+ }
+
+ // There was an internal error. Fail hard.
+ return res_error;
+}
diff --git a/resolv/res_state.cpp b/resolv/res_state.cpp
index 900c56a..325bb6e 100644
--- a/resolv/res_state.cpp
+++ b/resolv/res_state.cpp
@@ -132,10 +132,6 @@
return rt ? rt->_nres : NULL;
}
-void res_put_state(res_state /*res*/) {
- /* nothing to do */
-}
-
res_static* res_get_static(void) {
_res_thread* rt = res_thread_get();
diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h
index d4ee044..879eec7 100644
--- a/resolv/resolv_private.h
+++ b/resolv/resolv_private.h
@@ -126,8 +126,6 @@
struct in_addr addr;
uint32_t mask;
} sort_list[MAXRESOLVSORT];
- res_send_qhook qhook; /* query hook */
- res_send_rhook rhook; /* response hook */
int res_h_errno; /* last one set for this context */
unsigned _mark; /* If non-0 SET_MARK to _mark on all request sockets */
int _vcsock; /* PRIVATE: for res_send VC i/o */
@@ -144,6 +142,7 @@
} _ext;
} _u;
struct res_static rstatic[1];
+ bool use_local_nameserver; /* DNS-over-TLS bypass */
};
typedef struct __res_state* res_state;
@@ -236,7 +235,6 @@
/* Things involving an internal (static) resolver context. */
struct __res_state* res_get_state(void);
-void res_put_state(struct __res_state*);
void res_close(void);
int res_init(void);
diff --git a/server/Android.bp b/server/Android.bp
index 49ecba3..99164d8 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -98,7 +98,7 @@
":netd_metrics_aidl",
],
shared_libs: [
- "libbpf",
+ "libbpf_android",
"libnetd_resolv",
"libnetdaidl",
"libbase",
@@ -130,7 +130,7 @@
"android.system.net.netd@1.1",
"libbase",
"libbinder",
- "libbpf",
+ "libbpf_android",
"libcrypto",
"libcutils",
"libdl",
@@ -222,7 +222,7 @@
shared_libs: [
"libbase",
"libbinder",
- "libbpf",
+ "libbpf_android",
"libcrypto",
"libcutils",
"liblog",
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index 8f8c38a..c205a4c 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -55,8 +55,6 @@
#include "ResponseCode.h"
#include "Stopwatch.h"
#include "android/net/metrics/INetdEventListener.h"
-#include "netd_resolv/DnsTlsDispatcher.h"
-#include "netd_resolv/DnsTlsTransport.h"
#include "thread_util.h"
using android::String16;
@@ -110,90 +108,16 @@
return true;
}
-thread_local android_net_context thread_netcontext = {};
-
-DnsTlsDispatcher dnsTlsDispatcher;
-
-void catnap() {
- using namespace std::chrono_literals;
- std::this_thread::sleep_for(100ms);
-}
-
-res_sendhookact qhook(sockaddr* const * /*ns*/, const u_char** buf, int* buflen,
- u_char* ans, int anssiz, int* resplen) {
- if (!thread_netcontext.qhook) {
- ALOGE("qhook abort: thread qhook is null");
- return res_goahead;
- }
- if (thread_netcontext.flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) {
- return res_goahead;
- }
- if (!net::gCtls) {
- ALOGE("qhook abort: gCtls is null");
- return res_goahead;
- }
-
- const auto privateDnsStatus =
- net::gCtls->resolverCtrl.getPrivateDnsStatus(thread_netcontext.dns_netid);
-
- if (privateDnsStatus.mode == PrivateDnsMode::OFF) return res_goahead;
-
- if (privateDnsStatus.validatedServers.empty()) {
- if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
- return res_goahead;
- } else {
- // Sleep and iterate some small number of times checking for the
- // arrival of resolved and validated server IP addresses, instead
- // of returning an immediate error.
- catnap();
- return res_modified;
- }
- }
-
- if (DBG) ALOGD("Performing query over TLS");
-
- Slice query = netdutils::Slice(const_cast<u_char*>(*buf), *buflen);
- Slice answer = netdutils::Slice(const_cast<u_char*>(ans), anssiz);
- const auto response = dnsTlsDispatcher.query(
- privateDnsStatus.validatedServers, thread_netcontext.dns_mark,
- query, answer, resplen);
- if (response == DnsTlsTransport::Response::success) {
- if (DBG) ALOGD("qhook success");
- return res_done;
- }
-
- if (DBG) {
- ALOGW("qhook abort: TLS query failed: %d", (int)response);
- }
-
- if (privateDnsStatus.mode == PrivateDnsMode::OPPORTUNISTIC) {
- // In opportunistic mode, handle falling back to cleartext in some
- // cases (DNS shouldn't fail if a validated opportunistic mode server
- // becomes unreachable for some reason).
- switch (response) {
- case DnsTlsTransport::Response::network_error:
- case DnsTlsTransport::Response::internal_error:
- // Note: this will cause cleartext queries to be emitted, with
- // all of the EDNS0 goodness enabled. Fingers crossed. :-/
- return res_goahead;
- default:
- break;
- }
- }
-
- // There was an internal error. Fail hard.
- return res_error;
-}
-
constexpr bool requestingUseLocalNameservers(unsigned flags) {
return (flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) != 0;
}
inline bool queryingViaTls(unsigned dns_netid) {
- const auto privateDnsStatus = net::gCtls->resolverCtrl.getPrivateDnsStatus(dns_netid);
- switch (privateDnsStatus.mode) {
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ resolv_get_private_dns_status_for_net(dns_netid, &privateDnsStatus);
+ switch (static_cast<PrivateDnsMode>(privateDnsStatus.mode)) {
case PrivateDnsMode::OPPORTUNISTIC:
- return !privateDnsStatus.validatedServers.empty();
+ return privateDnsStatus.numServers != 0;
case PrivateDnsMode::STRICT:
return true;
default:
@@ -230,16 +154,6 @@
ctx->flags |= NET_CONTEXT_FLAG_USE_EDNS;
}
}
-
- // Always set the qhook. An opportunistic mode server might have finished
- // validating by the time the qhook runs. Note that this races with the
- // queryingViaTls() check above, resulting in possibly sending queries over
- // TLS without taking advantage of features like EDNS; c'est la guerre.
- ctx->qhook = &qhook;
-
- // Store the android_net_context instance in a thread_local variable
- // so that the static qhook can access other fields of the struct.
- thread_netcontext = *ctx;
}
} // namespace
@@ -357,7 +271,7 @@
Stopwatch s;
maybeFixupNetContext(&mNetContext);
const uid_t uid = mClient->getUid();
- uint32_t rv = 0;
+ int32_t rv = 0;
if (queryLimiter.start(uid)) {
rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
queryLimiter.finish(uid);
@@ -408,13 +322,13 @@
case INetdEventListener::REPORTING_LEVEL_METRICS:
// Metrics reporting is on. Send metrics.
mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
- INetdEventListener::EVENT_GETADDRINFO, (int32_t) rv,
+ INetdEventListener::EVENT_GETADDRINFO, rv,
latencyMs, String16(""), {}, -1, -1);
break;
case INetdEventListener::REPORTING_LEVEL_FULL:
// Full event info reporting is on. Send full info.
mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
- INetdEventListener::EVENT_GETADDRINFO, (int32_t) rv,
+ INetdEventListener::EVENT_GETADDRINFO, rv,
latencyMs, String16(mHost), ip_addrs,
total_ip_addr_count, mNetContext.uid);
break;
@@ -583,10 +497,12 @@
maybeFixupNetContext(&mNetContext);
const uid_t uid = mClient->getUid();
struct hostent* hp = nullptr;
+ int32_t rv = 0;
if (queryLimiter.start(uid)) {
- hp = android_gethostbynamefornetcontext(mName, mAf, &mNetContext);
+ rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp);
queryLimiter.finish(uid);
} else {
+ rv = EAI_MEMORY;
ALOGE("gethostbyname: from UID %d, max concurrent queries reached", uid);
}
const int latencyMs = lround(s.timeTaken());
@@ -600,6 +516,7 @@
bool success = true;
if (hp) {
+ // hp is not nullptr iff. rv is 0.
success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
success &= sendhostent(mClient, hp);
} else {
@@ -638,13 +555,13 @@
// Metrics reporting is on. Send metrics.
mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
INetdEventListener::EVENT_GETHOSTBYNAME,
- h_errno, latencyMs, String16(""), {}, -1, -1);
+ rv, latencyMs, String16(""), {}, -1, -1);
break;
case INetdEventListener::REPORTING_LEVEL_FULL:
// Full event info reporting is on. Send full info.
mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
INetdEventListener::EVENT_GETHOSTBYNAME,
- h_errno, latencyMs, String16(mName), ip_addrs,
+ rv, latencyMs, String16(mName), ip_addrs,
total_ip_addr_count, uid);
break;
}
diff --git a/server/ResolverController.cpp b/server/ResolverController.cpp
index a299120..0b326b0 100644
--- a/server/ResolverController.cpp
+++ b/server/ResolverController.cpp
@@ -38,6 +38,7 @@
#include <android/net/INetd.h>
#include <android/net/metrics/INetdEventListener.h>
+#include "Controllers.h"
#include "DumpWriter.h"
#include "EventReporter.h"
#include "Fwmark.h"
@@ -45,18 +46,13 @@
#include "Permission.h"
#include "ResolverController.h"
#include "ResolverStats.h"
-#include "netd_resolv/DnsTlsServer.h"
-#include "netd_resolv/DnsTlsTransport.h"
#include "netd_resolv/params.h"
#include "netd_resolv/resolv.h"
#include "netd_resolv/stats.h"
-#include "netdutils/BackoffSequence.h"
namespace android {
-
-using netdutils::BackoffSequence;
-
namespace net {
+
namespace {
std::string addrToString(const sockaddr_storage* addr) {
@@ -66,324 +62,50 @@
return std::string(out);
}
-bool parseServer(const char* server, sockaddr_storage* parsed) {
- addrinfo hints = {
- .ai_family = AF_UNSPEC,
- .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV
- };
- addrinfo* res;
-
- int err = getaddrinfo(server, "853", &hints, &res);
- if (err != 0) {
- ALOGW("Failed to parse server address (%s): %s", server, gai_strerror(err));
- return false;
- }
-
- memcpy(parsed, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
- return true;
-}
-
const char* getPrivateDnsModeString(PrivateDnsMode mode) {
switch (mode) {
- case PrivateDnsMode::OFF: return "OFF";
- case PrivateDnsMode::OPPORTUNISTIC: return "OPPORTUNISTIC";
- case PrivateDnsMode::STRICT: return "STRICT";
+ case PrivateDnsMode::OFF:
+ return "OFF";
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return "OPPORTUNISTIC";
+ case PrivateDnsMode::STRICT:
+ return "STRICT";
}
}
-constexpr const char* validationStatusToString(ResolverController::Validation value) {
+constexpr const char* validationStatusToString(Validation value) {
switch (value) {
- case ResolverController::Validation::in_process: return "in_process";
- case ResolverController::Validation::success: return "success";
- case ResolverController::Validation::fail: return "fail";
- case ResolverController::Validation::unknown_server: return "unknown_server";
- case ResolverController::Validation::unknown_netid: return "unknown_netid";
- default: return "unknown_status";
+ case Validation::in_process:
+ return "in_process";
+ case Validation::success:
+ return "success";
+ case Validation::fail:
+ return "fail";
+ case Validation::unknown_server:
+ return "unknown_server";
+ case Validation::unknown_netid:
+ return "unknown_netid";
+ default:
+ return "unknown_status";
}
}
-
-class PrivateDnsConfiguration {
- public:
- typedef ResolverController::PrivateDnsStatus PrivateDnsStatus;
- typedef ResolverController::Validation Validation;
- typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
-
- int set(uint32_t netId, uint32_t mark, const std::vector<std::string>& servers,
- const std::string& name, const std::set<std::vector<uint8_t>>& fingerprints) {
+void onPrivateDnsValidation(unsigned netId, const char* server, const char* hostname,
+ bool success) {
+ // Send a validation event to NetdEventListenerService.
+ const auto netdEventListener = net::gCtls->eventReporter.getNetdEventListener();
+ if (netdEventListener != nullptr) {
+ netdEventListener->onPrivateDnsValidationEvent(netId, android::String16(server),
+ android::String16(hostname), success);
if (DBG) {
- ALOGD("PrivateDnsConfiguration::set(%u, 0x%x, %zu, %s, %zu)", netId, mark,
- servers.size(), name.c_str(), fingerprints.size());
+ ALOGD("Sending validation %s event on netId %u for %s with hostname %s",
+ success ? "success" : "failure", netId, server, hostname);
}
- const bool explicitlyConfigured = !name.empty() || !fingerprints.empty();
-
- // Parse the list of servers that has been passed in
- std::set<DnsTlsServer> tlsServers;
- for (size_t i = 0; i < servers.size(); ++i) {
- sockaddr_storage parsed;
- if (!parseServer(servers[i].c_str(), &parsed)) {
- return -EINVAL;
- }
- DnsTlsServer server(parsed);
- server.name = name;
- server.fingerprints = fingerprints;
- tlsServers.insert(server);
- }
-
- std::lock_guard guard(mPrivateDnsLock);
- if (explicitlyConfigured) {
- mPrivateDnsModes[netId] = PrivateDnsMode::STRICT;
- } else if (!tlsServers.empty()) {
- mPrivateDnsModes[netId] = PrivateDnsMode::OPPORTUNISTIC;
- } else {
- mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
- mPrivateDnsTransports.erase(netId);
- return 0;
- }
-
- // Create the tracker if it was not present
- auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- // No TLS tracker yet for this netId.
- bool added;
- std::tie(netPair, added) = mPrivateDnsTransports.emplace(netId, PrivateDnsTracker());
- if (!added) {
- ALOGE("Memory error while recording private DNS for netId %d", netId);
- return -ENOMEM;
- }
- }
- auto& tracker = netPair->second;
-
- // Remove any servers from the tracker that are not in |servers| exactly.
- for (auto it = tracker.begin(); it != tracker.end();) {
- if (tlsServers.count(it->first) == 0) {
- it = tracker.erase(it);
- } else {
- ++it;
- }
- }
-
- // Add any new or changed servers to the tracker, and initiate async checks for them.
- for (const auto& server : tlsServers) {
- if (needsValidation(tracker, server)) {
- validatePrivateDnsProvider(server, tracker, netId, mark);
- }
- }
- return 0;
+ } else {
+ ALOGE("Validation event not sent since NetdEventListenerService is unavailable.");
}
-
- PrivateDnsStatus getStatus(unsigned netId) {
- PrivateDnsStatus status{PrivateDnsMode::OFF, {}};
-
- // This mutex is on the critical path of every DNS lookup.
- //
- // If the overhead of mutex acquisition proves too high, we could reduce
- // it by maintaining an atomic_int32_t counter of TLS-enabled netids, or
- // by using an RWLock.
- std::lock_guard guard(mPrivateDnsLock);
-
- const auto mode = mPrivateDnsModes.find(netId);
- if (mode == mPrivateDnsModes.end()) return status;
- status.mode = mode->second;
-
- const auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair != mPrivateDnsTransports.end()) {
- for (const auto& serverPair : netPair->second) {
- if (serverPair.second == Validation::success) {
- status.validatedServers.push_back(serverPair.first);
- }
- }
- }
-
- return status;
- }
-
- void clear(unsigned netId) {
- if (DBG) {
- ALOGD("PrivateDnsConfiguration::clear(%u)", netId);
- }
- std::lock_guard guard(mPrivateDnsLock);
- mPrivateDnsModes.erase(netId);
- mPrivateDnsTransports.erase(netId);
- }
-
- void dump(DumpWriter& dw, unsigned netId) {
- std::lock_guard guard(mPrivateDnsLock);
-
- const auto& mode = mPrivateDnsModes.find(netId);
- dw.println("Private DNS mode: %s", getPrivateDnsModeString(
- (mode != mPrivateDnsModes.end()) ? mode->second : PrivateDnsMode::OFF));
- const auto& netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- dw.println("No Private DNS servers configured");
- } else {
- const auto& tracker = netPair->second;
- dw.println("Private DNS configuration (%zu entries)", tracker.size());
- dw.incIndent();
- for (const auto& kv : tracker) {
- const auto& server = kv.first;
- const auto status = kv.second;
- dw.println("%s name{%s} status{%s}",
- addrToString(&(server.ss)).c_str(),
- server.name.c_str(),
- validationStatusToString(status));
- }
- dw.decIndent();
- }
- }
-
- private:
- void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
- unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock) {
- if (DBG) {
- ALOGD("validatePrivateDnsProvider(%s, %u)", addrToString(&(server.ss)).c_str(), netId);
- }
-
- tracker[server] = Validation::in_process;
- if (DBG) {
- ALOGD("Server %s marked as in_process. Tracker now has size %zu",
- addrToString(&(server.ss)).c_str(), tracker.size());
- }
- // Note that capturing |server| and |netId| in this lambda create copies.
- std::thread validate_thread([this, server, netId, mark] {
- // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
- //
- // Start with a 1 minute delay and backoff to once per hour.
- //
- // Assumptions:
- // [1] Each TLS validation is ~10KB of certs+handshake+payload.
- // [2] Network typically provision clients with <=4 nameservers.
- // [3] Average month has 30 days.
- //
- // Each validation pass in a given hour is ~1.2MB of data. And 24
- // such validation passes per day is about ~30MB per month, in the
- // worst case. Otherwise, this will cost ~600 SYNs per month
- // (6 SYNs per ip, 4 ips per validation pass, 24 passes per day).
- auto backoff = BackoffSequence<>::Builder()
- .withInitialRetransmissionTime(std::chrono::seconds(60))
- .withMaximumRetransmissionTime(std::chrono::seconds(3600))
- .build();
-
- while (true) {
- // ::validate() is a blocking call that performs network operations.
- // It can take milliseconds to minutes, up to the SYN retry limit.
- const bool success = DnsTlsTransport::validate(server, netId, mark);
- if (DBG) {
- ALOGD("validateDnsTlsServer returned %d for %s", success,
- addrToString(&(server.ss)).c_str());
- }
-
- const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success);
- if (!needs_reeval) {
- break;
- }
-
- if (backoff.hasNextTimeout()) {
- std::this_thread::sleep_for(backoff.getNextTimeout());
- } else {
- break;
- }
- }
- });
- validate_thread.detach();
- }
-
- bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success) {
- constexpr bool NEEDS_REEVALUATION = true;
- constexpr bool DONT_REEVALUATE = false;
-
- std::lock_guard guard(mPrivateDnsLock);
-
- auto netPair = mPrivateDnsTransports.find(netId);
- if (netPair == mPrivateDnsTransports.end()) {
- ALOGW("netId %u was erased during private DNS validation", netId);
- return DONT_REEVALUATE;
- }
-
- const auto mode = mPrivateDnsModes.find(netId);
- if (mode == mPrivateDnsModes.end()) {
- ALOGW("netId %u has no private DNS validation mode", netId);
- return DONT_REEVALUATE;
- }
- const bool modeDoesReevaluation = (mode->second == PrivateDnsMode::STRICT);
-
- bool reevaluationStatus =
- (success || !modeDoesReevaluation) ? DONT_REEVALUATE : NEEDS_REEVALUATION;
-
- auto& tracker = netPair->second;
- auto serverPair = tracker.find(server);
- if (serverPair == tracker.end()) {
- ALOGW("Server %s was removed during private DNS validation",
- addrToString(&(server.ss)).c_str());
- success = false;
- reevaluationStatus = DONT_REEVALUATE;
- } else if (!(serverPair->first == server)) {
- // TODO: It doesn't seem correct to overwrite the tracker entry for
- // |server| down below in this circumstance... Fix this.
- ALOGW("Server %s was changed during private DNS validation",
- addrToString(&(server.ss)).c_str());
- success = false;
- reevaluationStatus = DONT_REEVALUATE;
- }
-
- // Send a validation event to NetdEventListenerService.
- if (mNetdEventListener == nullptr) {
- mNetdEventListener = mEventReporter.getNetdEventListener();
- }
- if (mNetdEventListener != nullptr) {
- const String16 ipLiteral(addrToString(&(server.ss)).c_str());
- const String16 hostname(server.name.empty() ? "" : server.name.c_str());
- mNetdEventListener->onPrivateDnsValidationEvent(netId, ipLiteral, hostname, success);
- if (DBG) {
- ALOGD("Sending validation %s event on netId %u for %s with hostname %s",
- success ? "success" : "failure", netId,
- addrToString(&(server.ss)).c_str(), server.name.c_str());
- }
- } else {
- ALOGE("Validation event not sent since NetdEventListenerService is unavailable.");
- }
-
- if (success) {
- tracker[server] = Validation::success;
- if (DBG) {
- ALOGD("Validation succeeded for %s! Tracker now has %zu entries.",
- addrToString(&(server.ss)).c_str(), tracker.size());
- }
- } else {
- // Validation failure is expected if a user is on a captive portal.
- // TODO: Trigger a second validation attempt after captive portal login
- // succeeds.
- tracker[server] = (reevaluationStatus == NEEDS_REEVALUATION) ? Validation::in_process
- : Validation::fail;
- if (DBG) {
- ALOGD("Validation failed for %s!", addrToString(&(server.ss)).c_str());
- }
- }
-
- return reevaluationStatus;
- }
-
- // Start validation for newly added servers as well as any servers that have
- // landed in Validation::fail state. Note that servers that have failed
- // multiple validation attempts but for which there is still a validating
- // thread running are marked as being Validation::in_process.
- static bool needsValidation(const PrivateDnsTracker& tracker, const DnsTlsServer& server) {
- const auto& iter = tracker.find(server);
- return (iter == tracker.end()) || (iter->second == Validation::fail);
- }
-
- EventReporter mEventReporter;
-
- std::mutex mPrivateDnsLock;
- std::map<unsigned, PrivateDnsMode> mPrivateDnsModes GUARDED_BY(mPrivateDnsLock);
- // Structure for tracking the validation status of servers on a specific netId.
- // Using the AddressComparator ensures at most one entry per IP address.
- std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
- android::sp<android::net::metrics::INetdEventListener>
- mNetdEventListener GUARDED_BY(mPrivateDnsLock);
-} sPrivateDnsConfiguration;
+}
bool allIPv6Only(const std::vector<std::string>& servers) {
for (const auto& server : servers) {
@@ -402,18 +124,13 @@
return -resolv_set_nameservers_for_net(netId, servers, numservers, searchDomains, params);
}
-ResolverController::PrivateDnsStatus
-ResolverController::getPrivateDnsStatus(unsigned netId) const {
- return sPrivateDnsConfiguration.getStatus(netId);
-}
-
int ResolverController::clearDnsServers(unsigned netId) {
resolv_set_nameservers_for_net(netId, nullptr, 0, "", nullptr);
if (DBG) {
ALOGD("clearDnsServers netId = %u\n", netId);
}
mDns64Configuration.stopPrefixDiscovery(netId);
- sPrivateDnsConfiguration.clear(netId);
+ resolv_delete_private_dns_for_net(netId);
return 0;
}
@@ -512,6 +229,20 @@
return -EINVAL;
}
+ std::vector<const char*> server_ptrs;
+ size_t count = std::min<size_t>(MAXNS, tlsServers.size());
+ server_ptrs.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ server_ptrs.push_back(tlsServers[i].data());
+ }
+
+ std::vector<const uint8_t*> fingerprint_ptrs;
+ count = tlsFingerprints.size();
+ fingerprint_ptrs.reserve(count);
+ for (const auto& fp : tlsFingerprints) {
+ fingerprint_ptrs.push_back(fp.data());
+ }
+
// At private DNS validation time, we only know the netId, so we have to guess/compute the
// corresponding socket mark.
Fwmark fwmark;
@@ -520,16 +251,19 @@
fwmark.protectedFromVpn = true;
fwmark.permission = PERMISSION_SYSTEM;
- const int err = sPrivateDnsConfiguration.set(netId, fwmark.intValue, tlsServers, tlsName,
- tlsFingerprints);
+ const int err = resolv_set_private_dns_for_net(
+ netId, fwmark.intValue, server_ptrs.data(), server_ptrs.size(), tlsName.c_str(),
+ fingerprint_ptrs.data(), fingerprint_ptrs.size());
if (err != 0) {
return err;
}
+ resolv_register_private_dns_callback(&onPrivateDnsValidation);
// Convert network-assigned server list to bionic's format.
- auto server_count = std::min<size_t>(MAXNS, servers.size());
- std::vector<const char*> server_ptrs;
- for (size_t i = 0 ; i < server_count ; ++i) {
+ server_ptrs.clear();
+ count = std::min<size_t>(MAXNS, servers.size());
+ server_ptrs.reserve(count);
+ for (size_t i = 0; i < count; ++i) {
server_ptrs.push_back(servers[i].c_str());
}
@@ -653,7 +387,24 @@
}
mDns64Configuration.dump(dw, netId);
- sPrivateDnsConfiguration.dump(dw, netId);
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ resolv_get_private_dns_status_for_net(netId, &privateDnsStatus);
+ dw.println("Private DNS mode: %s",
+ getPrivateDnsModeString(static_cast<PrivateDnsMode>(privateDnsStatus.mode)));
+ if (!privateDnsStatus.numServers) {
+ dw.println("No Private DNS servers configured");
+ } else {
+ dw.println("Private DNS configuration (%u entries)", privateDnsStatus.numServers);
+ dw.incIndent();
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ dw.println("%s name{%s} status{%s}",
+ addrToString(&(privateDnsStatus.serverStatus[i].ss)).c_str(),
+ privateDnsStatus.serverStatus[i].hostname,
+ validationStatusToString(static_cast<Validation>(
+ privateDnsStatus.serverStatus[i].validation)));
+ }
+ dw.decIndent();
+ }
}
dw.decIndent();
}
diff --git a/server/ResolverController.h b/server/ResolverController.h
index a99bbee..ea95ee4 100644
--- a/server/ResolverController.h
+++ b/server/ResolverController.h
@@ -27,17 +27,9 @@
namespace android {
namespace net {
-struct DnsTlsServer;
class DumpWriter;
struct ResolverStats;
-enum class PrivateDnsMode {
- OFF,
- OPPORTUNISTIC,
- STRICT,
-};
-
-
class ResolverController {
public:
ResolverController(const NetworkController& netCtrl) : mDns64Configuration(netCtrl) {}
@@ -48,20 +40,6 @@
int setDnsServers(unsigned netId, const char* searchDomains, const char** servers,
int numservers, const __res_params* params);
- // Validation status of a DNS over TLS server (on a specific netId).
- enum class Validation : uint8_t { in_process, success, fail, unknown_server, unknown_netid };
-
- struct PrivateDnsStatus {
- PrivateDnsMode mode;
- std::list<DnsTlsServer> validatedServers;
- };
-
- // Retrieve the Private DNS status for the given |netid|.
- //
- // If the requested |netid| is not known, the PrivateDnsStatus's mode has a
- // default value of PrivateDnsMode::OFF, and validatedServers is empty.
- PrivateDnsStatus getPrivateDnsStatus(unsigned netid) const;
-
int clearDnsServers(unsigned netid);
int flushDnsCache(unsigned netid);
diff --git a/tests/Android.bp b/tests/Android.bp
index a757e32..5b35fdc 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -62,7 +62,7 @@
"libcap",
"libnetd_test_dnsresponder",
"libnetd_test_tun_interface",
- "libbpf",
+ "libbpf_android",
"liblogwrap",
"libnetdaidl",
"libnetdbpf",
diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp
index 08a75e0..d2079c1 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/benchmarks/Android.bp
@@ -39,7 +39,7 @@
defaults: ["netd_defaults"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_android",
"libnetdutils",
],
static_libs: [