Move DnsProxyListener to libnetd_resolv
[1] Support APIs for netd to set callbacks and bring up
DnsProxyListener.
[2] Keep DnsProxyListener functioning as usual by function pointers,
including getNetworkContext(), checkCallingPermission(), and
getPrefix64().
[3] Use libbinder_ndk to report onDnsEvent().
Test: as follows
- built, flashed, booted
- system/netd/tests/runtests.sh passed
- netd_benchmark passed
- Browsing websites passed
Change-Id: Ib6575833c248579aa079e302795b6d6cddde1f2b
diff --git a/Android.bp b/Android.bp
index ccea41f..ab333f4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,6 +20,7 @@
"res_send.cpp",
"res_state.cpp",
"res_stats.cpp",
+ "DnsProxyListener.cpp",
"DnsTlsDispatcher.cpp",
"DnsTlsQueryMap.cpp",
"DnsTlsTransport.cpp",
@@ -35,13 +36,21 @@
static_libs: [
"libbase",
"libcrypto",
+ "libcutils",
"liblog",
"libnetdutils",
"libssl",
+ "libsysutils",
"netd_event_listener_interface-ndk_platform",
],
shared_libs: [
"libbinder_ndk",
+ "libstatslog",
+ ],
+ // TODO: Get rid of these include paths used in DnsProxyListener.
+ include_dirs: [
+ "system/netd/include",
+ "system/netd/server",
],
export_include_dirs: ["include"],
// TODO: pie in the sky: make this code clang-tidy clean
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
new file mode 100644
index 0000000..18006f1
--- /dev/null
+++ b/DnsProxyListener.cpp
@@ -0,0 +1,1191 @@
+/*
+ * Copyright (C) 2010 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <math.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define LOG_TAG "DnsProxyListener"
+#define DBG 0
+#define VDBG 0
+
+#include <algorithm>
+#include <list>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <cutils/misc.h>
+#include <log/log.h>
+#include <netdutils/OperationLimiter.h>
+#include <netdutils/Slice.h>
+#include <private/android_filesystem_config.h> // AID_SYSTEM
+#include <resolv.h>
+#include <statslog.h>
+#include <sysutils/SocketClient.h>
+
+// TODO: Considering moving ResponseCode.h Stopwatch.h thread_util.h to libnetdutils.
+#include "NetdClient.h" // NETID_USE_LOCAL_NAMESERVERS
+#include "ResponseCode.h"
+#include "Stopwatch.h"
+#include "netd_resolv/DnsProxyListener.h"
+#include "netd_resolv/ResolverEventReporter.h"
+#include "netd_resolv/stats.h" // RCODE_TIMEOUT
+#include "netdutils/InternetAddresses.h"
+#include "thread_util.h"
+
+using aidl::android::net::metrics::INetdEventListener;
+using android::base::StringPrintf;
+
+static android::net::DnsProxyListener gDnsProxyListener;
+
+bool resolv_init(const dnsproxylistener_callbacks& callbacks) {
+ if (!gDnsProxyListener.setCallbacks(callbacks)) {
+ ALOGE("Unable to set callbacks to DnsProxyListener");
+ return false;
+ }
+ if (gDnsProxyListener.startListener()) {
+ ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+namespace android {
+namespace net {
+
+namespace {
+
+// TODO: move to a separate file (with other constants from FwmarkService and NetdNativeService)
+constexpr const char CONNECTIVITY_USE_RESTRICTED_NETWORKS[] =
+ "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+constexpr const char NETWORK_BYPASS_PRIVATE_DNS[] =
+ "android.permission.NETWORK_BYPASS_PRIVATE_DNS";
+
+// Limits the number of outstanding DNS queries by client UID.
+constexpr int MAX_QUERIES_PER_UID = 256;
+
+// Max packet size for answer, sync with getaddrinfo.c
+constexpr int MAXPACKET = 8 * 1024;
+
+android::netdutils::OperationLimiter<uid_t> queryLimiter(MAX_QUERIES_PER_UID);
+
+void logArguments(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ ALOGD("argv[%i]=%s", i, argv[i]);
+ }
+}
+
+template<typename T>
+void tryThreadOrError(SocketClient* cli, T* handler) {
+ cli->incRef();
+
+ const int rval = threadLaunch(handler);
+ if (rval == 0) {
+ // SocketClient decRef() happens in the handler's run() method.
+ return;
+ }
+
+ char* msg = nullptr;
+ asprintf(&msg, "%s (%d)", strerror(-rval), -rval);
+ cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+ free(msg);
+
+ delete handler;
+ cli->decRef();
+}
+
+bool checkAndClearUseLocalNameserversFlag(unsigned* netid) {
+ if (netid == nullptr || ((*netid) & NETID_USE_LOCAL_NAMESERVERS) == 0) {
+ return false;
+ }
+ *netid = (*netid) & ~NETID_USE_LOCAL_NAMESERVERS;
+ return true;
+}
+
+constexpr bool requestingUseLocalNameservers(unsigned flags) {
+ return (flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS) != 0;
+}
+
+inline bool queryingViaTls(unsigned dns_netid) {
+ ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
+ resolv_get_private_dns_status_for_net(dns_netid, &privateDnsStatus);
+ switch (static_cast<PrivateDnsMode>(privateDnsStatus.mode)) {
+ case PrivateDnsMode::OPPORTUNISTIC:
+ for (int i = 0; i < privateDnsStatus.numServers; i++) {
+ if (privateDnsStatus.serverStatus[i].validation == Validation::success) {
+ return true;
+ }
+ }
+ return false;
+ case PrivateDnsMode::STRICT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool hasPermissionToBypassPrivateDns(uid_t uid) {
+ static_assert(AID_SYSTEM >= 0 && AID_SYSTEM < FIRST_APPLICATION_UID,
+ "Calls from AID_SYSTEM must not result in a permission check to avoid deadlock.");
+ if (uid >= 0 && uid < FIRST_APPLICATION_UID) {
+ return true;
+ }
+
+ for (const char* const permission :
+ {CONNECTIVITY_USE_RESTRICTED_NETWORKS, NETWORK_BYPASS_PRIVATE_DNS}) {
+ if (gDnsProxyListener.mCallbacks.check_calling_permission(permission)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void maybeFixupNetContext(android_net_context* ctx) {
+ if (requestingUseLocalNameservers(ctx->flags) && !hasPermissionToBypassPrivateDns(ctx->uid)) {
+ // Not permitted; clear the flag.
+ ctx->flags &= ~NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ if (!requestingUseLocalNameservers(ctx->flags)) {
+ // If we're not explicitly bypassing DNS-over-TLS servers, check whether
+ // DNS-over-TLS is in use as an indicator for when to use more modern
+ // DNS resolution mechanics.
+ if (queryingViaTls(ctx->dns_netid)) {
+ ctx->flags |= NET_CONTEXT_FLAG_USE_EDNS;
+ }
+ }
+}
+
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
+ socklen_t addrlen);
+
+int extractResNsendAnswers(const uint8_t* answer, size_t anslen, int ipType,
+ std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ ns_msg handle;
+ if (ns_initparse((const uint8_t*) answer, anslen, &handle) < 0) {
+ return 0;
+ }
+ int ancount = ns_msg_count(handle, ns_s_an);
+ ns_rr rr;
+ for (int i = 0; i < ancount; i++) {
+ if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
+ continue;
+ }
+ const uint8_t* rdata = ns_rr_rdata(rr);
+ if (ipType == ns_t_a) {
+ sockaddr_in sin = {.sin_family = AF_INET};
+ memcpy(&sin.sin_addr, rdata, sizeof(sin.sin_addr));
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
+ total_ip_addr_count++;
+ } else if (ipType == ns_t_aaaa) {
+ sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
+ memcpy(&sin6.sin6_addr, rdata, sizeof(sin6.sin6_addr));
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
+ total_ip_addr_count++;
+ }
+ }
+
+ return total_ip_addr_count;
+}
+
+int extractGetAddrInfoAnswers(const addrinfo* result, std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ if (result == nullptr) {
+ return 0;
+ }
+ for (const addrinfo* ai = result; ai; ai = ai->ai_next) {
+ sockaddr* ai_addr = ai->ai_addr;
+ if (ai_addr) {
+ addIpAddrWithinLimit(ip_addrs, ai_addr, ai->ai_addrlen);
+ total_ip_addr_count++;
+ }
+ }
+ return total_ip_addr_count;
+}
+
+int extractGetHostByNameAnswers(const hostent* hp, std::vector<std::string>* ip_addrs) {
+ int total_ip_addr_count = 0;
+ if (hp == nullptr) {
+ return 0;
+ }
+ if (hp->h_addrtype == AF_INET) {
+ in_addr** list = (in_addr**) hp->h_addr_list;
+ for (int i = 0; list[i] != nullptr; i++) {
+ sockaddr_in sin = {.sin_family = AF_INET, .sin_addr = *list[i]};
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
+ total_ip_addr_count++;
+ }
+ } else if (hp->h_addrtype == AF_INET6) {
+ in6_addr** list = (in6_addr**) hp->h_addr_list;
+ for (int i = 0; list[i] != nullptr; i++) {
+ sockaddr_in6 sin6 = {.sin6_family = AF_INET6, .sin6_addr = *list[i]};
+ addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
+ total_ip_addr_count++;
+ }
+ }
+ return total_ip_addr_count;
+}
+
+int rcodeToAiError(int rcode) {
+ switch (rcode) {
+ case NOERROR:
+ return 0;
+ case RCODE_TIMEOUT:
+ return NETD_RESOLV_TIMEOUT;
+ default:
+ return EAI_NODATA;
+ }
+}
+
+int resNSendToAiError(int err, int rcode) {
+ if (err > 0) {
+ return rcodeToAiError(rcode);
+ }
+ if (err == -ETIMEDOUT) {
+ return NETD_RESOLV_TIMEOUT;
+ }
+ return EAI_SYSTEM;
+}
+
+template <typename IntegralType>
+bool simpleStrtoul(const char* input, IntegralType* output, int base = 10) {
+ char* endPtr;
+ errno = 0;
+ auto result = strtoul(input, &endPtr, base);
+ // Check the length in order to ensure there is no "-" sign
+ if (!*input || *endPtr || (endPtr - input) != static_cast<ptrdiff_t>(strlen(input)) ||
+ (errno == ERANGE && (result == ULONG_MAX))) {
+ return false;
+ }
+ *output = result;
+ return true;
+}
+
+bool parseQuery(const uint8_t* msg, size_t msgLen, int* rr_type, std::string* rr_name) {
+ 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 false;
+ }
+
+ *rr_name = ns_rr_name(rr);
+ *rr_type = ns_rr_type(rr);
+ return true;
+}
+
+void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
+ int returnCode, const std::string& query_name,
+ const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
+ android::util::stats_write(android::util::NETWORK_DNS_EVENT_REPORTED, eventType, returnCode,
+ latencyUs);
+
+ const std::shared_ptr<INetdEventListener> listener = ResolverEventReporter::getListener();
+ if (!listener) return;
+ const int latencyMs = latencyUs / 1000;
+ listener->onDnsEvent(netContext.dns_netid, eventType, returnCode, latencyMs, query_name,
+ ip_addrs, total_ip_addr_count, netContext.uid);
+}
+
+bool onlyIPv4Answers(const addrinfo* res) {
+ // Null addrinfo pointer isn't checked because the caller doesn't pass null pointer.
+
+ for (const addrinfo* ai = res; ai; ai = ai->ai_next)
+ if (ai->ai_family != AF_INET) return false;
+
+ return true;
+}
+
+bool isSpecialUseIPv4Address(const struct in_addr& ia) {
+ const uint32_t addr = ntohl(ia.s_addr);
+
+ // Only check necessary IP ranges in RFC 5735 section 4
+ return ((addr & 0xff000000) == 0x00000000) || // "This" Network
+ ((addr & 0xff000000) == 0x7f000000) || // Loopback
+ ((addr & 0xffff0000) == 0xa9fe0000) || // Link Local
+ ((addr & 0xf0000000) == 0xe0000000) || // Multicast
+ (addr == INADDR_BROADCAST); // Limited Broadcast
+}
+
+bool isSpecialUseIPv4Address(const struct sockaddr* sa) {
+ if (sa->sa_family != AF_INET) return false;
+
+ return isSpecialUseIPv4Address(((struct sockaddr_in*) sa)->sin_addr);
+}
+
+bool onlyNonSpecialUseIPv4Addresses(struct hostent* hp) {
+ // Null hostent pointer isn't checked because the caller doesn't pass null pointer.
+
+ if (hp->h_addrtype != AF_INET) return false;
+
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++)
+ if (isSpecialUseIPv4Address(*(struct in_addr*) hp->h_addr_list[i])) return false;
+
+ return true;
+}
+
+bool onlyNonSpecialUseIPv4Addresses(const addrinfo* res) {
+ // Null addrinfo pointer isn't checked because the caller doesn't pass null pointer.
+
+ for (const addrinfo* ai = res; ai; ai = ai->ai_next) {
+ if (ai->ai_family != AF_INET) return false;
+ if (isSpecialUseIPv4Address(ai->ai_addr)) return false;
+ }
+
+ return true;
+}
+
+void logDnsQueryResult(const struct hostent* hp) {
+ if (hp == nullptr) return;
+
+ ALOGD("DNS records:");
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++) {
+ char ip_addr[INET6_ADDRSTRLEN];
+ if (inet_ntop(hp->h_addrtype, hp->h_addr_list[i], ip_addr, sizeof(ip_addr)) != nullptr) {
+ ALOGD("[%d] %s, %d, %d, %s (%p)", i, hp->h_name ? hp->h_name : "null", hp->h_addrtype,
+ hp->h_length, ip_addr, hp->h_addr_list[i]);
+ } else {
+ ALOGD("[%d] numeric hostname translation fail (%d)", i, errno);
+ }
+ }
+}
+
+void logDnsQueryResult(const addrinfo* res) {
+ if (res == nullptr) return;
+
+ int i;
+ const addrinfo* ai;
+ ALOGD("DNS records:");
+ for (ai = res, i = 0; ai; ai = ai->ai_next, i++) {
+ if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6)) continue;
+ char ip_addr[INET6_ADDRSTRLEN];
+ int ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ip_addr, sizeof(ip_addr), nullptr, 0,
+ NI_NUMERICHOST);
+ if (!ret) {
+ ALOGD("[%d] 0x%x,%d,%d,%d,%d,%s,%s,%p", i, ai->ai_flags, ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol, ai->ai_addrlen, ai->ai_canonname ? ai->ai_canonname : "null",
+ ip_addr, ai);
+ } else {
+ ALOGD("[%d] numeric hostname translation fail (%d)", i, ret);
+ }
+ }
+}
+
+bool isValidNat64Prefix(const netdutils::IPPrefix prefix) {
+ if (prefix.family() != AF_INET6) {
+ ALOGE("Only IPv6 NAT64 prefixes are supported (%u)", prefix.family());
+ return false;
+ }
+ if (prefix.length() != 96) {
+ ALOGE("Only /96 NAT64 prefixes are supported (%d)", prefix.length());
+ return false;
+ }
+ return true;
+}
+
+bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, struct hostent* hp) {
+ if (hp == nullptr) return false;
+ if (!onlyNonSpecialUseIPv4Addresses(hp)) return false;
+ if (!isValidNat64Prefix(prefix)) return false;
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ for (int i = 0; hp->h_addr_list[i] != nullptr; i++) {
+ struct in_addr iaOriginal = *(struct in_addr*) hp->h_addr_list[i];
+ struct in6_addr* ia6 = (struct in6_addr*) hp->h_addr_list[i];
+ memset(ia6, 0, sizeof(struct in6_addr));
+
+ // Synthesize /96 NAT64 prefix in place. The space has reserved by getanswer() and
+ // _hf_gethtbyname2() in system/netd/resolv/gethnamaddr.cpp and
+ // system/netd/resolv/sethostent.cpp.
+ *ia6 = v6prefix->sin6_addr;
+ ia6->s6_addr32[3] = iaOriginal.s_addr;
+
+ if (DBG) {
+ char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6
+ inet_ntop(AF_INET, &iaOriginal.s_addr, buf, sizeof(buf));
+ ALOGD("DNS A record: %s", buf);
+ inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf));
+ ALOGD("NAT64 prefix: %s", buf);
+ inet_ntop(AF_INET6, ia6, buf, sizeof(buf));
+ ALOGD("DNS64 Synthesized AAAA record: %s", buf);
+ }
+ }
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = sizeof(in6_addr);
+
+ if (DBG) logDnsQueryResult(hp);
+ return true;
+}
+
+bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo* result) {
+ if (result == nullptr) return false;
+ if (!onlyNonSpecialUseIPv4Addresses(result)) return false;
+ if (!isValidNat64Prefix(prefix)) return false;
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ for (addrinfo* ai = result; ai; ai = ai->ai_next) {
+ struct sockaddr_in sinOriginal = *(struct sockaddr_in*) ai->ai_addr;
+ struct sockaddr_in6* sin6 = (struct sockaddr_in6*) ai->ai_addr;
+ memset(sin6, 0, sizeof(sockaddr_in6));
+
+ // Synthesize /96 NAT64 prefix in place. The space has reserved by get_ai() in
+ // system/netd/resolv/getaddrinfo.cpp.
+ sin6->sin6_addr = v6prefix->sin6_addr;
+ sin6->sin6_addr.s6_addr32[3] = sinOriginal.sin_addr.s_addr;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = sinOriginal.sin_port;
+ ai->ai_addrlen = sizeof(struct sockaddr_in6);
+ ai->ai_family = AF_INET6;
+
+ if (DBG) {
+ char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6
+ inet_ntop(AF_INET, &sinOriginal.sin_addr.s_addr, buf, sizeof(buf));
+ ALOGD("DNS A record: %s", buf);
+ inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf));
+ ALOGD("NAT64 prefix: %s", buf);
+ inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
+ ALOGD("DNS64 Synthesized AAAA record: %s", buf);
+ }
+ }
+ if (DBG) logDnsQueryResult(result);
+ return true;
+}
+
+bool getDns64Prefix(unsigned netId, netdutils::IPPrefix* prefix) {
+ in6_addr v6addr{};
+ uint8_t prefixLen = 0;
+ if (!gDnsProxyListener.mCallbacks.get_dns64_prefix(netId, &v6addr, &prefixLen)) {
+ return false;
+ }
+ const netdutils::IPAddress ipv6(v6addr);
+ *prefix = netdutils::IPPrefix(ipv6, static_cast<int>(prefixLen));
+ return true;
+}
+
+} // namespace
+
+bool DnsProxyListener::setCallbacks(const dnsproxylistener_callbacks& callbacks) {
+ mCallbacks = callbacks;
+ return mCallbacks.check_calling_permission && mCallbacks.get_network_context &&
+ mCallbacks.get_dns64_prefix;
+}
+
+DnsProxyListener::DnsProxyListener() : FrameworkListener(SOCKET_NAME) {
+ registerCmd(new GetAddrInfoCmd());
+ registerCmd(new GetHostByAddrCmd());
+ registerCmd(new GetHostByNameCmd());
+ registerCmd(new ResNSendCommand());
+}
+
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, char* host, char* service,
+ addrinfo* hints,
+ const android_net_context& netcontext)
+ : mClient(c), mHost(host), mService(service), mHints(hints), mNetContext(netcontext) {}
+
+DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
+ free(mHost);
+ free(mService);
+ free(mHints);
+}
+
+static bool sendBE32(SocketClient* c, uint32_t data) {
+ uint32_t be_data = htonl(data);
+ return c->sendData(&be_data, sizeof(be_data)) == 0;
+}
+
+// Sends 4 bytes of big-endian length, followed by the data.
+// Returns true on success.
+static bool sendLenAndData(SocketClient* c, const int len, const void* data) {
+ return sendBE32(c, len) && (len == 0 || c->sendData(data, len) == 0);
+}
+
+// Returns true on success
+static bool sendhostent(SocketClient* c, hostent* hp) {
+ bool success = true;
+ int i;
+ if (hp->h_name != nullptr) {
+ success &= sendLenAndData(c, strlen(hp->h_name)+1, hp->h_name);
+ } else {
+ success &= sendLenAndData(c, 0, "") == 0;
+ }
+
+ for (i=0; hp->h_aliases[i] != nullptr; i++) {
+ success &= sendLenAndData(c, strlen(hp->h_aliases[i])+1, hp->h_aliases[i]);
+ }
+ success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+
+ uint32_t buf = htonl(hp->h_addrtype);
+ success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+ buf = htonl(hp->h_length);
+ success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+ for (i=0; hp->h_addr_list[i] != nullptr; i++) {
+ success &= sendLenAndData(c, 16, hp->h_addr_list[i]);
+ }
+ success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+ return success;
+}
+
+static bool sendaddrinfo(SocketClient* c, addrinfo* ai) {
+ // struct addrinfo {
+ // int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ // int ai_family; /* PF_xxx */
+ // int ai_socktype; /* SOCK_xxx */
+ // int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ // socklen_t ai_addrlen; /* length of ai_addr */
+ // char *ai_canonname; /* canonical name for hostname */
+ // struct sockaddr *ai_addr; /* binary address */
+ // struct addrinfo *ai_next; /* next structure in linked list */
+ // };
+
+ // Write the struct piece by piece because we might be a 64-bit netd
+ // talking to a 32-bit process.
+ bool success =
+ sendBE32(c, ai->ai_flags) &&
+ sendBE32(c, ai->ai_family) &&
+ sendBE32(c, ai->ai_socktype) &&
+ sendBE32(c, ai->ai_protocol);
+ if (!success) {
+ return false;
+ }
+
+ // ai_addrlen and ai_addr.
+ if (!sendLenAndData(c, ai->ai_addrlen, ai->ai_addr)) {
+ return false;
+ }
+
+ // strlen(ai_canonname) and ai_canonname.
+ if (!sendLenAndData(c, ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0, ai->ai_canonname)) {
+ return false;
+ }
+
+ return true;
+}
+
+void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res) {
+ if (mHost == nullptr) return;
+
+ const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA);
+ const bool unspecWantedButNoIPv6 =
+ ((!mHints || mHints->ai_family == AF_UNSPEC) && *rv == 0 && onlyIPv4Answers(*res));
+
+ if (!ipv6WantedButNoData && !unspecWantedButNoIPv6) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ if (ipv6WantedButNoData) {
+ // 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)) {
+ 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);
+ queryLimiter.finish(uid);
+ if (*rv) {
+ *rv = EAI_NODATA; // return original error code
+ return;
+ }
+ } else {
+ ALOGE("getaddrinfo: from UID %d, max concurrent queries reached", uid);
+ return;
+ }
+ }
+
+ if (!synthesizeNat64PrefixWithARecord(prefix, *res)) {
+ if (ipv6WantedButNoData) {
+ // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers,
+ // don't return the IPv4 answers.
+ *rv = EAI_NODATA; // return original error code
+ if (*res) {
+ freeaddrinfo(*res);
+ *res = nullptr;
+ }
+ }
+ }
+}
+
+void DnsProxyListener::GetAddrInfoHandler::run() {
+ if (DBG) {
+ ALOGD("GetAddrInfoHandler, now for %s / %s / {%u,%u,%u,%u,%u,%u}", mHost, mService,
+ mNetContext.app_netid, mNetContext.app_mark,
+ mNetContext.dns_netid, mNetContext.dns_mark,
+ mNetContext.uid, mNetContext.flags);
+ }
+
+ addrinfo* result = nullptr;
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ int32_t rv = 0;
+ if (queryLimiter.start(uid)) {
+ rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
+ queryLimiter.finish(uid);
+ } else {
+ // Note that this error code is currently not passed down to the client.
+ // android_getaddrinfo_proxy() returns EAI_NODATA on any error.
+ rv = EAI_MEMORY;
+ ALOGE("getaddrinfo: from UID %d, max concurrent queries reached", uid);
+ }
+
+ doDns64Synthesis(&rv, &result);
+ const int latencyUs = int(s.timeTakenUs());
+
+ if (rv) {
+ // getaddrinfo failed
+ mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
+ } else {
+ bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
+ addrinfo* ai = result;
+ while (ai && success) {
+ success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
+ ai = ai->ai_next;
+ }
+ success = success && sendBE32(mClient, 0);
+ if (!success) {
+ ALOGW("Error writing DNS result to client");
+ }
+ }
+ std::vector<std::string> ip_addrs;
+ const int total_ip_addr_count = extractGetAddrInfoAnswers(result, &ip_addrs);
+ reportDnsEvent(INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyUs, rv, mHost,
+ ip_addrs, total_ip_addr_count);
+ freeaddrinfo(result);
+ mClient->decRef();
+}
+
+namespace {
+
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
+ socklen_t addrlen) {
+ // ipAddresses array is limited to first INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT
+ // addresses for A and AAAA. Total count of addresses is provided, to be able to tell whether
+ // some addresses didn't get logged.
+ if (ip_addrs->size() < INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT) {
+ char ip_addr[INET6_ADDRSTRLEN];
+ if (getnameinfo(addr, addrlen, ip_addr, sizeof(ip_addr), nullptr, 0, NI_NUMERICHOST) == 0) {
+ ip_addrs->push_back(std::string(ip_addr));
+ }
+ }
+}
+
+} // namespace
+
+DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() : FrameworkCommand("getaddrinfo") {}
+
+int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (DBG) logArguments(argc, argv);
+
+ if (argc != 8) {
+ char* msg = nullptr;
+ asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
+ ALOGW("%s", msg);
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ char* name = argv[1];
+ if (strcmp("^", name) == 0) {
+ name = nullptr;
+ } else {
+ name = strdup(name);
+ }
+
+ char* service = argv[2];
+ if (strcmp("^", service) == 0) {
+ service = nullptr;
+ } else {
+ service = strdup(service);
+ }
+
+ addrinfo* hints = nullptr;
+ int ai_flags = strtol(argv[3], nullptr, 10);
+ int ai_family = strtol(argv[4], nullptr, 10);
+ int ai_socktype = strtol(argv[5], nullptr, 10);
+ int ai_protocol = strtol(argv[6], nullptr, 10);
+ unsigned netId = strtoul(argv[7], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+ const uid_t uid = cli->getUid();
+
+ android_net_context netcontext;
+ gDnsProxyListener.mCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ if (ai_flags != -1 || ai_family != -1 ||
+ ai_socktype != -1 || ai_protocol != -1) {
+ hints = (addrinfo*) calloc(1, sizeof(addrinfo));
+ hints->ai_flags = ai_flags;
+ hints->ai_family = ai_family;
+ hints->ai_socktype = ai_socktype;
+ hints->ai_protocol = ai_protocol;
+ }
+
+ if (DBG) {
+ ALOGD("GetAddrInfoHandler for %s / %s / {%u,%u,%u,%u,%u}",
+ name ? name : "[nullhost]",
+ service ? service : "[nullservice]",
+ netcontext.app_netid, netcontext.app_mark,
+ netcontext.dns_netid, netcontext.dns_mark,
+ netcontext.uid);
+ }
+
+ DnsProxyListener::GetAddrInfoHandler* handler =
+ new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+/*******************************************************
+ * ResNSendCommand *
+ *******************************************************/
+DnsProxyListener::ResNSendCommand::ResNSendCommand() : FrameworkCommand("resnsend") {}
+
+int DnsProxyListener::ResNSendCommand::runCommand(SocketClient* cli, int argc, char** argv) {
+ if (DBG) logArguments(argc, argv);
+
+ const uid_t uid = cli->getUid();
+ if (argc != 4) {
+ ALOGW("resnsend: from UID %d, invalid number of arguments to resnsend: %d", uid, argc);
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ unsigned netId;
+ if (!simpleStrtoul(argv[1], &netId)) {
+ ALOGW("resnsend: from UID %d, invalid netId", uid);
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ uint32_t flags;
+ if (!simpleStrtoul(argv[2], &flags)) {
+ ALOGW("resnsend: from UID %d, invalid flags", uid);
+ sendBE32(cli, -EINVAL);
+ return -1;
+ }
+
+ android_net_context netcontext;
+ gDnsProxyListener.mCallbacks.get_network_context(netId, uid, &netcontext);
+ if (checkAndClearUseLocalNameserversFlag(&netId)) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::ResNSendHandler* handler =
+ new DnsProxyListener::ResNSendHandler(cli, argv[3], flags, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::ResNSendHandler::ResNSendHandler(SocketClient* c, std::string msg, uint32_t flags,
+ const android_net_context& netcontext)
+ : mClient(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {}
+
+DnsProxyListener::ResNSendHandler::~ResNSendHandler() {
+ mClient->decRef();
+}
+
+void DnsProxyListener::ResNSendHandler::run() {
+ if (DBG) {
+ ALOGD("ResNSendHandler, now for %s %u/ {%u,%u,%u,%u,%u,%u}", mMsg.c_str(), mFlags,
+ mNetContext.app_netid, mNetContext.app_mark, mNetContext.dns_netid,
+ mNetContext.dns_mark, mNetContext.uid, mNetContext.flags);
+ }
+
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+
+ // Decode
+ std::vector<uint8_t> msg(MAXPACKET, 0);
+
+ // Max length of mMsg is less than 1024 since the CMD_BUF_SIZE in FrameworkListener is 1024
+ int msgLen = b64_pton(mMsg.c_str(), msg.data(), MAXPACKET);
+ if (msgLen == -1) {
+ // Decode fail
+ sendBE32(mClient, -EILSEQ);
+ return;
+ }
+
+ const uid_t uid = mClient->getUid();
+ int rr_type = 0;
+ std::string rr_name;
+
+ // TODO: Handle the case which is msg contains more than one query
+ // Parse and store query type/name
+ if (!parseQuery(msg.data(), msgLen, &rr_type, &rr_name)) {
+ // If the query couldn't be parsed, block the request.
+ ALOGW("resnsend: from UID %d, invalid query", uid);
+ sendBE32(mClient, -EINVAL);
+ return;
+ }
+
+ // Send DNS query
+ std::vector<uint8_t> ansBuf(MAXPACKET, 0);
+ int arcode, nsendAns = -1;
+ if (queryLimiter.start(uid)) {
+ nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
+ &arcode, static_cast<ResNsendFlags>(mFlags));
+ queryLimiter.finish(uid);
+ } else {
+ ALOGW("resnsend: from UID %d, max concurrent queries reached", uid);
+ nsendAns = -EBUSY;
+ }
+
+ const int latencyUs = int(s.timeTakenUs());
+
+ // 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), rr_name);
+ }
+ return;
+ }
+
+ // Send rcode
+ if (!sendBE32(mClient, arcode)) {
+ ALOGW("resnsend: failed to send rcode to uid %d: %s", uid, strerror(errno));
+ return;
+ }
+
+ // Send answer
+ if (!sendLenAndData(mClient, nsendAns, ansBuf.data())) {
+ ALOGW("resnsend: failed to send answer to uid %d: %s", uid, strerror(errno));
+ return;
+ }
+
+ if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
+ std::vector<std::string> ip_addrs;
+ 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), rr_name, ip_addrs, total_ip_addr_count);
+ }
+}
+
+/*******************************************************
+ * GetHostByName *
+ *******************************************************/
+DnsProxyListener::GetHostByNameCmd::GetHostByNameCmd() : FrameworkCommand("gethostbyname") {}
+
+int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (DBG) logArguments(argc, argv);
+
+ if (argc != 4) {
+ char* msg = nullptr;
+ asprintf(&msg, "Invalid number of arguments to gethostbyname: %i", argc);
+ ALOGW("%s", msg);
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ uid_t uid = cli->getUid();
+ unsigned netId = strtoul(argv[1], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+ char* name = argv[2];
+ int af = strtol(argv[3], nullptr, 10);
+
+ if (strcmp(name, "^") == 0) {
+ name = nullptr;
+ } else {
+ name = strdup(name);
+ }
+
+ android_net_context netcontext;
+ gDnsProxyListener.mCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::GetHostByNameHandler* handler =
+ new DnsProxyListener::GetHostByNameHandler(cli, name, af, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, char* name, int af,
+ const android_net_context& netcontext)
+ : mClient(c), mName(name), mAf(af), mNetContext(netcontext) {}
+
+DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() {
+ free(mName);
+}
+
+void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, struct hostent** hpp) {
+ // 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);
+
+ if (!ipv6WantedButNoData) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ // 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);
+ queryLimiter.finish(uid);
+ if (*rv) {
+ *rv = EAI_NODATA; // return original error code
+ return;
+ }
+ } else {
+ ALOGE("gethostbyname: from UID %d, max concurrent queries reached", uid);
+ return;
+ }
+
+ if (!synthesizeNat64PrefixWithARecord(prefix, *hpp)) {
+ // If caller wants IPv6 answers but no data and failed to synthesize IPv4 answers,
+ // don't return the IPv4 answers.
+ *hpp = nullptr;
+ }
+}
+
+void DnsProxyListener::GetHostByNameHandler::run() {
+ if (DBG) {
+ ALOGD("DnsProxyListener::GetHostByNameHandler::run");
+ }
+
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ hostent* hp = nullptr;
+ int32_t rv = 0;
+ if (queryLimiter.start(uid)) {
+ rv = android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp);
+ queryLimiter.finish(uid);
+ } else {
+ rv = EAI_MEMORY;
+ ALOGE("gethostbyname: from UID %d, max concurrent queries reached", uid);
+ }
+
+ doDns64Synthesis(&rv, &hp);
+ const int latencyUs = lround(s.timeTakenUs());
+
+ if (DBG) {
+ ALOGD("GetHostByNameHandler::run gethostbyname errno: %s hp->h_name = %s, name_len = %zu",
+ hp ? "success" : strerror(errno),
+ (hp && hp->h_name) ? hp->h_name : "null",
+ (hp && hp->h_name) ? strlen(hp->h_name) + 1 : 0);
+ }
+
+ bool success = true;
+ if (hp) {
+ // hp is not nullptr iff. rv is 0.
+ success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+ success &= sendhostent(mClient, hp);
+ } else {
+ success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, nullptr, 0) == 0;
+ }
+
+ if (!success) {
+ ALOGW("GetHostByNameHandler: Error writing DNS result to client");
+ }
+
+ std::vector<std::string> ip_addrs;
+ const int total_ip_addr_count = extractGetHostByNameAnswers(hp, &ip_addrs);
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext, latencyUs, rv, mName,
+ ip_addrs, total_ip_addr_count);
+ mClient->decRef();
+}
+
+
+/*******************************************************
+ * GetHostByAddr *
+ *******************************************************/
+DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() : FrameworkCommand("gethostbyaddr") {}
+
+int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (DBG) logArguments(argc, argv);
+
+ if (argc != 5) {
+ char* msg = nullptr;
+ asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);
+ ALOGW("%s", msg);
+ cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+ free(msg);
+ return -1;
+ }
+
+ char* addrStr = argv[1];
+ int addrLen = strtol(argv[2], nullptr, 10);
+ int addrFamily = strtol(argv[3], nullptr, 10);
+ uid_t uid = cli->getUid();
+ unsigned netId = strtoul(argv[4], nullptr, 10);
+ const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
+
+ void* addr = malloc(sizeof(in6_addr));
+ errno = 0;
+ int result = inet_pton(addrFamily, addrStr, addr);
+ if (result <= 0) {
+ char* msg = nullptr;
+ asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno));
+ ALOGW("%s", msg);
+ cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+ free(addr);
+ free(msg);
+ return -1;
+ }
+
+ android_net_context netcontext;
+ gDnsProxyListener.mCallbacks.get_network_context(netId, uid, &netcontext);
+
+ if (useLocalNameservers) {
+ netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
+ }
+
+ DnsProxyListener::GetHostByAddrHandler* handler = new DnsProxyListener::GetHostByAddrHandler(
+ cli, addr, addrLen, addrFamily, netcontext);
+ tryThreadOrError(cli, handler);
+ return 0;
+}
+
+DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, void* address,
+ int addressLen, int addressFamily,
+ const android_net_context& netcontext)
+ : mClient(c),
+ mAddress(address),
+ mAddressLen(addressLen),
+ mAddressFamily(addressFamily),
+ mNetContext(netcontext) {}
+
+DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() {
+ free(mAddress);
+}
+
+void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp) {
+ if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) {
+ return;
+ }
+
+ netdutils::IPPrefix prefix{};
+ if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {
+ return;
+ }
+
+ if (!isValidNat64Prefix(prefix)) {
+ return;
+ }
+
+ struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());
+ struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;
+ struct in6_addr v6addr = *(in6_addr*) mAddress;
+ // Check if address has NAT64 prefix. Only /96 IPv6 NAT64 prefixes are supported
+ if ((v6addr.s6_addr32[0] != v6prefix->sin6_addr.s6_addr32[0]) ||
+ (v6addr.s6_addr32[1] != v6prefix->sin6_addr.s6_addr32[1]) ||
+ (v6addr.s6_addr32[2] != v6prefix->sin6_addr.s6_addr32[2])) {
+ return;
+ }
+
+ const uid_t uid = mClient->getUid();
+ 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);
+ queryLimiter.finish(uid);
+ if (*hpp) {
+ // Replace IPv4 address with original queried IPv6 address in place. The space has
+ // reserved by _dns_gethtbyaddr() and netbsd_gethostent_r() in
+ // system/netd/resolv/gethnamaddr.cpp.
+ // Note that android_gethostbyaddrfornetcontext returns only one entry in result.
+ memcpy((*hpp)->h_addr_list[0], &v6addr, sizeof(v6addr));
+ (*hpp)->h_addrtype = AF_INET6;
+ (*hpp)->h_length = sizeof(struct in6_addr);
+ }
+ } else {
+ ALOGE("gethostbyaddr: from UID %d, max concurrent queries reached", uid);
+ }
+}
+
+void DnsProxyListener::GetHostByAddrHandler::run() {
+ if (DBG) {
+ ALOGD("DnsProxyListener::GetHostByAddrHandler::run");
+ }
+
+ Stopwatch s;
+ maybeFixupNetContext(&mNetContext);
+ const uid_t uid = mClient->getUid();
+ hostent* hp = nullptr;
+ int32_t rv = 0;
+ if (queryLimiter.start(uid)) {
+ rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,
+ &mNetContext, &hp);
+ queryLimiter.finish(uid);
+ } else {
+ rv = EAI_MEMORY;
+ ALOGE("gethostbyaddr: from UID %d, max concurrent queries reached", uid);
+ }
+
+ doDns64ReverseLookup(&hp);
+ const int latencyUs = int(s.timeTakenUs());
+
+ if (DBG) {
+ ALOGD("GetHostByAddrHandler::run gethostbyaddr result: %s hp->h_name = %s, name_len = %zu",
+ hp ? "success" : gai_strerror(rv), (hp && hp->h_name) ? hp->h_name : "null",
+ (hp && hp->h_name) ? strlen(hp->h_name) + 1 : 0);
+ }
+
+ bool success = true;
+ if (hp) {
+ success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+ success &= sendhostent(mClient, hp);
+ } else {
+ success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, nullptr, 0) == 0;
+ }
+
+ if (!success) {
+ ALOGW("GetHostByAddrHandler: Error writing DNS result to client");
+ }
+
+ reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv,
+ (hp && hp->h_name) ? hp->h_name : "null", {}, 0);
+ mClient->decRef();
+}
+
+} // namespace net
+} // namespace android
diff --git a/include/netd_resolv/DnsProxyListener.h b/include/netd_resolv/DnsProxyListener.h
new file mode 100644
index 0000000..ef9a3da
--- /dev/null
+++ b/include/netd_resolv/DnsProxyListener.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 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 _DNSPROXYLISTENER_H__
+#define _DNSPROXYLISTENER_H__
+
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/FrameworkListener.h>
+
+#include "resolv.h" // android_net_context
+
+namespace android {
+namespace net {
+
+class DnsProxyListener : public FrameworkListener {
+ public:
+ DnsProxyListener();
+ virtual ~DnsProxyListener() {}
+
+ bool setCallbacks(const dnsproxylistener_callbacks& callbacks);
+
+ static constexpr const char* SOCKET_NAME = "dnsproxyd";
+
+ // TODO: Considering putting this callbacks structure in its own file.
+ dnsproxylistener_callbacks mCallbacks{};
+
+ private:
+ class GetAddrInfoCmd : public FrameworkCommand {
+ public:
+ GetAddrInfoCmd();
+ virtual ~GetAddrInfoCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ /* ------ getaddrinfo ------*/
+ class GetAddrInfoHandler {
+ public:
+ // Note: All of host, service, and hints may be NULL
+ GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints,
+ const android_net_context& netcontext);
+ ~GetAddrInfoHandler();
+
+ void run();
+
+ private:
+ void doDns64Synthesis(int32_t* rv, addrinfo** res);
+
+ SocketClient* mClient; // ref counted
+ char* mHost; // owned. TODO: convert to std::string.
+ char* mService; // owned. TODO: convert to std::string.
+ addrinfo* mHints; // owned
+ android_net_context mNetContext;
+ };
+
+ /* ------ gethostbyname ------*/
+ class GetHostByNameCmd : public FrameworkCommand {
+ public:
+ GetHostByNameCmd();
+ virtual ~GetHostByNameCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class GetHostByNameHandler {
+ public:
+ GetHostByNameHandler(SocketClient* c, char* name, int af,
+ const android_net_context& netcontext);
+ ~GetHostByNameHandler();
+
+ void run();
+
+ private:
+ void doDns64Synthesis(int32_t* rv, hostent** hpp);
+
+ SocketClient* mClient; //ref counted
+ char* mName; // owned. TODO: convert to std::string.
+ int mAf;
+ android_net_context mNetContext;
+ };
+
+ /* ------ gethostbyaddr ------*/
+ class GetHostByAddrCmd : public FrameworkCommand {
+ public:
+ GetHostByAddrCmd();
+ virtual ~GetHostByAddrCmd() {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class GetHostByAddrHandler {
+ public:
+ GetHostByAddrHandler(SocketClient* c, void* address, int addressLen, int addressFamily,
+ const android_net_context& netcontext);
+ ~GetHostByAddrHandler();
+
+ void run();
+
+ private:
+ void doDns64ReverseLookup(hostent** hpp);
+
+ SocketClient* mClient; // ref counted
+ void* mAddress; // address to lookup; owned
+ int mAddressLen; // length of address to look up
+ int mAddressFamily; // address family
+ android_net_context mNetContext;
+ };
+
+ /* ------ resnsend ------*/
+ class ResNSendCommand : public FrameworkCommand {
+ public:
+ ResNSendCommand();
+ ~ResNSendCommand() override {}
+ int runCommand(SocketClient* c, int argc, char** argv) override;
+ };
+
+ class ResNSendHandler {
+ public:
+ ResNSendHandler(SocketClient* c, std::string msg, uint32_t flags,
+ const android_net_context& netcontext);
+ ~ResNSendHandler();
+
+ void run();
+
+ private:
+ SocketClient* mClient; // ref counted
+ std::string mMsg;
+ uint32_t mFlags;
+ android_net_context mNetContext;
+ };
+};
+
+} // namespace net
+} // namespace android
+
+#endif
diff --git a/include/netd_resolv/resolv.h b/include/netd_resolv/resolv.h
index 4f43107..dde600d 100644
--- a/include/netd_resolv/resolv.h
+++ b/include/netd_resolv/resolv.h
@@ -45,7 +45,7 @@
} sockaddr_union;
/*
- * Passing NETID_UNSET as the netId causes system/netd/server/DnsProxyListener.cpp to
+ * Passing NETID_UNSET as the netId causes system/netd/resolv/DnsProxyListener.cpp to
* fill in the appropriate default netId for the query.
*/
#define NETID_UNSET 0u
@@ -101,9 +101,31 @@
} serverStatus[MAXNS];
};
+/*
+ * Some of functions (e.g. checkCallingPermission()) require the dependency on libbinder.so,
+ * but we can't include the library since it's not stable. Move the functions to netd and use
+ * these function pointers pointing to them.
+ */
+typedef void (*get_network_context_callback)(unsigned netid, uid_t uid,
+ android_net_context* netcontext);
+
+// TODO: investigate having the resolver check permissions itself, either by adding support to
+// libbinder_ndk or by converting IPermissionController into a stable AIDL interface.
+typedef bool (*check_calling_permission_callback)(const char* permission);
+
+// TODO: Remove the callback.
typedef void (*private_dns_validated_callback)(unsigned netid, const char* server,
const char* hostname, bool success);
+// TODO: Remove the callback after moving NAT64 prefix discovery out of netd to libnetd_resolv.
+typedef bool (*get_dns64_prefix_callback)(unsigned netid, in6_addr* prefix, uint8_t* prefix_len);
+
+struct dnsproxylistener_callbacks {
+ check_calling_permission_callback check_calling_permission;
+ get_network_context_callback get_network_context;
+ get_dns64_prefix_callback get_dns64_prefix;
+};
+
LIBNETD_RESOLV_PUBLIC int android_gethostbyaddrfornetcontext(const void*, socklen_t, int,
const android_net_context*, hostent**);
LIBNETD_RESOLV_PUBLIC int android_gethostbynamefornetcontext(const char*, int,
@@ -136,10 +158,14 @@
ExternalPrivateDnsStatus* status);
// Register callback to listen whether private DNS validated
+// TODO: Remove it. Use ResolverEventReporter instead.
LIBNETD_RESOLV_PUBLIC void resolv_register_private_dns_callback(
private_dns_validated_callback callback);
// Delete the cache associated with a certain network
LIBNETD_RESOLV_PUBLIC void resolv_delete_cache_for_net(unsigned netid);
+// Set callbacks to DnsProxyListener, and bring it up.
+LIBNETD_RESOLV_PUBLIC bool resolv_init(const dnsproxylistener_callbacks& callbacks);
+
#endif // NETD_RESOLV_RESOLV_H
diff --git a/include/netd_resolv/resolv_stub.h b/include/netd_resolv/resolv_stub.h
index b4364ba..9e09b44 100644
--- a/include/netd_resolv/resolv_stub.h
+++ b/include/netd_resolv/resolv_stub.h
@@ -63,6 +63,8 @@
bool (*resolv_has_nameservers)(unsigned netid);
+ bool (*resolv_init)(const dnsproxylistener_callbacks& callbacks);
+
void (*resolv_register_private_dns_callback)(private_dns_validated_callback callback);
int (*resolv_res_nsend)(const android_net_context* netContext, const u_char* msg, int msgLen,
diff --git a/libnetd_resolv.map.txt b/libnetd_resolv.map.txt
index 175e3d8..336a7a9 100644
--- a/libnetd_resolv.map.txt
+++ b/libnetd_resolv.map.txt
@@ -30,6 +30,7 @@
resolv_delete_private_dns_for_net;
resolv_get_private_dns_status_for_net;
resolv_has_nameservers;
+ resolv_init;
resolv_register_private_dns_callback;
resolv_res_nsend;
resolv_set_nameservers_for_net;