OSDN Git Service

Add two Netd binder calls to set/get resolver config.
authorPierre Imai <imaipi@google.com>
Tue, 12 Apr 2016 21:44:51 +0000 (06:44 +0900)
committerPierre Imai <imaipi@google.com>
Thu, 28 Apr 2016 07:08:06 +0000 (16:08 +0900)
setResolverConfiguration() sets the name servers, search domains,
and resolver parameters.
getResolverInfo() returns the configured information and also the
statistics for each server.
Also includes tests for the new functionality.

BUG: 25731675

Change-Id: Idde486f36bb731f9edd240d62dc1795f8e621fe6

server/NetdNativeService.cpp
server/NetdNativeService.h
server/ResolverController.cpp
server/ResolverController.h
server/ResolverStats.h [new file with mode: 0644]
server/binder/android/net/INetd.aidl
tests/Android.mk
tests/dns_responder.cpp
tests/dns_responder.h
tests/netd_test.cpp

index a8f5c3b..5ee8202 100644 (file)
@@ -21,6 +21,7 @@
 #include <android-base/stringprintf.h>
 #include <cutils/log.h>
 #include <utils/Errors.h>
+#include <utils/String16.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -169,7 +170,34 @@ binder::Status NetdNativeService::socketDestroy(const std::vector<UidRange>& uid
         return binder::Status::fromServiceSpecificError(-err,
                 String8::format("destroySockets: %s", strerror(-err)));
     }
+    return binder::Status::ok();
+}
+
+binder::Status NetdNativeService::setResolverConfiguration(int32_t netId,
+        const std::vector<std::string>& servers, const std::vector<std::string>& domains,
+        const std::vector<int32_t>& params) {
+    // This function intentionally does not lock within Netd, as Bionic is thread-safe.
+    ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL);
 
+    int err = gCtls->resolverCtrl.setResolverConfiguration(netId, servers, domains, params);
+    if (err != 0) {
+        return binder::Status::fromServiceSpecificError(-err,
+                String8::format("ResolverController error: %s", strerror(-err)));
+    }
+    return binder::Status::ok();
+}
+
+binder::Status NetdNativeService::getResolverInfo(int32_t netId,
+        std::vector<std::string>* servers, std::vector<std::string>* domains,
+        std::vector<int32_t>* params, std::vector<int32_t>* stats) {
+    // This function intentionally does not lock within Netd, as Bionic is thread-safe.
+    ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL);
+
+    int err = gCtls->resolverCtrl.getResolverInfo(netId, servers, domains, params, stats);
+    if (err != 0) {
+        return binder::Status::fromServiceSpecificError(-err,
+                String8::format("ResolverController error: %s", strerror(-err)));
+    }
     return binder::Status::ok();
 }
 
index 22c81fc..b5c8f69 100644 (file)
@@ -42,6 +42,11 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd
             override;
     binder::Status socketDestroy(const std::vector<UidRange>& uids,
             const std::vector<int32_t>& skipUids) override;
+    binder::Status setResolverConfiguration(int32_t netId, const std::vector<std::string>& servers,
+            const std::vector<std::string>& domains, const std::vector<int32_t>& params) override;
+    binder::Status getResolverInfo(int32_t netId, std::vector<std::string>* servers,
+            std::vector<std::string>* domains, std::vector<int32_t>* params,
+            std::vector<int32_t>* stats) override;
 };
 
 }  // namespace net
index 16cfd53..20c9302 100644 (file)
 #define LOG_TAG "ResolverController"
 #define DBG 0
 
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#include <vector>
 #include <cutils/log.h>
-
 #include <net/if.h>
+#include <sys/socket.h>
+#include <netdb.h>
 
 // NOTE: <resolv_netid.h> is a private C library header that provides
 //       declarations for _resolv_set_nameservers_for_net and
 //       _resolv_flush_cache_for_net
 #include <resolv_netid.h>
 #include <resolv_params.h>
+#include <resolv_stats.h>
+
+#include <android/net/INetd.h>
 
 #include "ResolverController.h"
+#include "ResolverStats.h"
 
 int ResolverController::setDnsServers(unsigned netId, const char* searchDomains,
         const char** servers, int numservers, const __res_params* params) {
     if (DBG) {
         ALOGD("setDnsServers netId = %u\n", netId);
     }
-    _resolv_set_nameservers_for_net(netId, servers, numservers, searchDomains, params);
-    return 0;
+    return -_resolv_set_nameservers_for_net(netId, servers, numservers, searchDomains, params);
 }
 
 int ResolverController::clearDnsServers(unsigned netId) {
@@ -55,3 +63,129 @@ int ResolverController::flushDnsCache(unsigned netId) {
 
     return 0;
 }
+
+int ResolverController::getDnsInfo(unsigned netId, std::vector<std::string>* servers,
+        std::vector<std::string>* domains, __res_params* params,
+        std::vector<android::net::ResolverStats>* stats) {
+    using android::net::ResolverStats;
+    using android::net::INetd;
+    static_assert(ResolverStats::STATS_SUCCESSES == INetd::RESOLVER_STATS_SUCCESSES &&
+            ResolverStats::STATS_ERRORS == INetd::RESOLVER_STATS_ERRORS &&
+            ResolverStats::STATS_TIMEOUTS == INetd::RESOLVER_STATS_TIMEOUTS &&
+            ResolverStats::STATS_INTERNAL_ERRORS == INetd::RESOLVER_STATS_INTERNAL_ERRORS &&
+            ResolverStats::STATS_RTT_AVG == INetd::RESOLVER_STATS_RTT_AVG &&
+            ResolverStats::STATS_LAST_SAMPLE_TIME == INetd::RESOLVER_STATS_LAST_SAMPLE_TIME &&
+            ResolverStats::STATS_USABLE == INetd::RESOLVER_STATS_USABLE &&
+            ResolverStats::STATS_COUNT == INetd::RESOLVER_STATS_COUNT,
+            "AIDL and ResolverStats.h out of sync");
+    int nscount = -1;
+    sockaddr_storage res_servers[MAXNS];
+    int dcount = -1;
+    char res_domains[MAXDNSRCH][MAXDNSRCHPATH];
+    __res_stats res_stats[MAXNS];
+    servers->clear();
+    domains->clear();
+    *params = __res_params{};
+    stats->clear();
+    int revision_id = android_net_res_stats_get_info_for_net(netId, &nscount, res_servers, &dcount,
+            res_domains, params, res_stats);
+
+    // If the netId is unknown (which can happen for valid net IDs for which no DNS servers have
+    // yet been configured), there is no revision ID. In this case there is no data to return.
+    if (revision_id < 0) {
+        return 0;
+    }
+
+    // Verify that the returned data is sane.
+    if (nscount < 0 || nscount > MAXNS || dcount < 0 || dcount > MAXDNSRCH) {
+        ALOGE("%s: nscount=%d, dcount=%d", __FUNCTION__, nscount, dcount);
+        return -ENOTRECOVERABLE;
+    }
+
+    // Determine which servers are considered usable by the resolver.
+    bool valid_servers[MAXNS];
+    std::fill_n(valid_servers, MAXNS, false);
+    android_net_res_stats_get_usable_servers(params, res_stats, nscount, valid_servers);
+
+    // Convert the server sockaddr structures to std::string.
+    stats->resize(nscount);
+    for (int i = 0 ; i < nscount ; ++i) {
+        char hbuf[NI_MAXHOST];
+        int rv = getnameinfo(reinterpret_cast<const sockaddr*>(&res_servers[i]),
+                sizeof(res_servers[i]), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST);
+        std::string server_str;
+        if (rv == 0) {
+            server_str.assign(hbuf);
+        } else {
+            ALOGE("getnameinfo() failed for server #%d: %s", i, gai_strerror(rv));
+            server_str.assign("<invalid>");
+        }
+        servers->push_back(std::move(server_str));
+        android::net::ResolverStats& cur_stats = (*stats)[i];
+        android_net_res_stats_aggregate(&res_stats[i], &cur_stats.successes, &cur_stats.errors,
+                &cur_stats.timeouts, &cur_stats.internal_errors, &cur_stats.rtt_avg,
+                &cur_stats.last_sample_time);
+        cur_stats.usable = valid_servers[i];
+    }
+
+    // Convert the stack-allocated search domain strings to std::string.
+    for (int i = 0 ; i < dcount ; ++i) {
+        domains->push_back(res_domains[i]);
+    }
+    return 0;
+}
+
+int ResolverController::setResolverConfiguration(int32_t netId,
+        const std::vector<std::string>& servers, const std::vector<std::string>& domains,
+        const std::vector<int32_t>& params) {
+    using android::net::INetd;
+    if (params.size() != INetd::RESOLVER_PARAMS_COUNT) {
+        ALOGE("%s: params.size()=%zu", __FUNCTION__, params.size());
+        return -EINVAL;
+    }
+
+    std::vector<const char*> server_ptrs;
+    for (const std::string& str : servers) {
+        server_ptrs.push_back(str.c_str());
+    }
+
+    std::string domains_str;
+    if (!domains.empty()) {
+        domains_str = domains[0];
+        for (size_t i = 1 ; i < domains.size() ; ++i) {
+            domains_str += " " + domains[i];
+        }
+    }
+
+    __res_params res_params;
+    res_params.sample_validity = params[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY];
+    res_params.success_threshold = params[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
+    res_params.min_samples = params[INetd::RESOLVER_PARAMS_MIN_SAMPLES];
+    res_params.max_samples = params[INetd::RESOLVER_PARAMS_MAX_SAMPLES];
+
+    return setDnsServers(netId, domains_str.c_str(), server_ptrs.data(), server_ptrs.size(),
+            &res_params);
+}
+
+int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers,
+        std::vector<std::string>* domains, std::vector<int32_t>* params,
+        std::vector<int32_t>* stats) {
+    using android::net::ResolverStats;
+    using android::net::INetd;
+    __res_params res_params;
+    std::vector<ResolverStats> res_stats;
+    int ret = getDnsInfo(netId, servers, domains, &res_params, &res_stats);
+    if (ret != 0) {
+        return ret;
+    }
+
+    // Serialize the information for binder.
+    ResolverStats::encodeAll(res_stats, stats);
+
+    params->resize(INetd::RESOLVER_PARAMS_COUNT);
+    (*params)[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY] = res_params.sample_validity;
+    (*params)[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD] = res_params.success_threshold;
+    (*params)[INetd::RESOLVER_PARAMS_MIN_SAMPLES] = res_params.min_samples;
+    (*params)[INetd::RESOLVER_PARAMS_MAX_SAMPLES] = res_params.max_samples;
+    return 0;
+}
index 048ff3f..a3810d4 100644 (file)
 #ifndef _RESOLVER_CONTROLLER_H_
 #define _RESOLVER_CONTROLLER_H_
 
+#include <vector>
 #include <netinet/in.h>
 #include <linux/in.h>
 
 struct __res_params;
 
+namespace android {
+namespace net {
+struct ResolverStats;
+}  // namespace net
+}  // namespace android
+
 class ResolverController {
 public:
     ResolverController() {};
+
     virtual ~ResolverController() {};
+
+    // TODO: delete this function
     int setDnsServers(unsigned netId, const char* searchDomains, const char** servers,
             int numservers, const __res_params* params);
+
     int clearDnsServers(unsigned netid);
+
     int flushDnsCache(unsigned netid);
-    // TODO: Add deleteDnsCache(unsigned netId)
+
+    int getDnsInfo(unsigned netId, std::vector<std::string>* servers,
+            std::vector<std::string>* domains, __res_params* params,
+            std::vector<android::net::ResolverStats>* stats);
+
+    // Binder specific functions, which convert between the binder int/string arrays and the
+    // actual data structures, and call setDnsServer() / getDnsInfo() for the actual processing.
+    int setResolverConfiguration(int32_t netId, const std::vector<std::string>& servers,
+            const std::vector<std::string>& domains, const std::vector<int32_t>& params);
+
+    int getResolverInfo(int32_t netId, std::vector<std::string>* servers,
+            std::vector<std::string>* domains, std::vector<int32_t>* params,
+            std::vector<int32_t>* stats);
 };
 
 #endif /* _RESOLVER_CONTROLLER_H_ */
diff --git a/server/ResolverStats.h b/server/ResolverStats.h
new file mode 100644 (file)
index 0000000..be63d88
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 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 _RESOLVER_STATS_H_
+#define _RESOLVER_STATS_H_
+
+#include <time.h>
+
+namespace android {
+namespace net {
+
+struct ResolverStats {
+    // Offsets into the per-server resolver stats as encoded in vector<int32_t> stats of
+    // getResolverInfo() of Netd's binder interface. The stats are based on data reported by
+    // android_net_res_stats_get_info_for_net(), the usability is calculated by applying
+    // android_net_res_stats_get_usable_servers() to this data.
+    enum ResolverStatsOffsets {
+        STATS_SUCCESSES = 0,    // # successes counted for this server
+        STATS_ERRORS,           // # errors
+        STATS_TIMEOUTS,         // # timeouts
+        STATS_INTERNAL_ERRORS,  // # internal errors
+        STATS_RTT_AVG,          // average round-trip-time
+        STATS_LAST_SAMPLE_TIME, // time in s when the last sample was recorded
+        STATS_USABLE,           // whether the server is considered usable
+        STATS_COUNT             // total count of integers in the per-server data
+    };
+
+    int successes {-1};
+    int errors {-1};
+    int timeouts {-1};
+    int internal_errors {-1};
+    int rtt_avg {-1};
+    time_t last_sample_time {0};
+    bool usable {false};
+
+    // Serialize the resolver stats to the end of |out|.
+    void encode(std::vector<int32_t>* out) const;
+
+    // Read the serialized resolverstats starting at |in[ofs]|.
+    ssize_t decode(const std::vector<int32_t>& in, ssize_t ofs);
+
+    // Serialize the contents of |stats| and append them to the end of |out|. Multiple arrays
+    // can be written to the same output vector in sequence, however, the corresponding call
+    // to decodeAll() will return the combined contents in one vector.
+    static void encodeAll(const std::vector<ResolverStats>& stats, std::vector<int32_t>* out);
+
+    // Decodes the serialized ResolverStats from |in| and appends them to stats.
+    static bool decodeAll(const std::vector<int32_t>& in, std::vector<ResolverStats>* stats);
+};
+
+
+inline void ResolverStats::encode(std::vector<int32_t>* out) const {
+    size_t ofs = out->size();
+    out->resize(ofs + STATS_COUNT);
+    int32_t* cur = &(*out)[ofs];
+    cur[STATS_SUCCESSES] = successes;
+    cur[STATS_ERRORS] = errors;
+    cur[STATS_TIMEOUTS] = timeouts;
+    cur[STATS_INTERNAL_ERRORS] = internal_errors;
+    cur[STATS_RTT_AVG] = rtt_avg;
+    cur[STATS_LAST_SAMPLE_TIME] = last_sample_time;
+    cur[STATS_USABLE] = usable;
+}
+
+    // Read the serialized resolverstats starting at |in[ofs]|.
+inline ssize_t ResolverStats::decode(const std::vector<int32_t>& in, ssize_t ofs) {
+    if (ofs < 0 || static_cast<size_t>(ofs) + STATS_COUNT > in.size()) {
+        return -1;
+    }
+    const int32_t* cur = &in[ofs];
+    successes = cur[STATS_SUCCESSES];
+    errors = cur[STATS_ERRORS];
+    timeouts = cur[STATS_TIMEOUTS];
+    internal_errors = cur[STATS_INTERNAL_ERRORS];
+    rtt_avg = cur[STATS_RTT_AVG];
+    last_sample_time = cur[STATS_LAST_SAMPLE_TIME];
+    usable = cur[STATS_USABLE];
+    return ofs + STATS_COUNT;
+}
+
+inline void ResolverStats::encodeAll(const std::vector<ResolverStats>& stats,
+        std::vector<int32_t>* out) {
+    for (const auto& s : stats) {
+        s.encode(out);
+    }
+}
+
+// TODO: Replace with a better representation, e.g. a Parcelable.
+inline bool ResolverStats::decodeAll(const std::vector<int32_t>& in,
+        std::vector<ResolverStats>* stats) {
+    ssize_t size = in.size();
+    if (size % STATS_COUNT) {
+        return false;
+    }
+    stats->resize(size / STATS_COUNT);
+    ssize_t ofs = 0;
+    for (auto& s : *stats) {
+        ofs = s.decode(in, ofs);
+        if (ofs < 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace net
+}  // namespace android
+
+#endif /* _RESOLVER_STATS_H_ */
index 616842b..097f98a 100644 (file)
@@ -85,4 +85,64 @@ interface INetd {
      * Administratively closes sockets belonging to the specified UIDs.
      */
     void socketDestroy(in UidRange[] uidRanges, in int[] exemptUids);
+
+    // Array indices for resolver parameters.
+    const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+    const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+    const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+    const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+    const int RESOLVER_PARAMS_COUNT = 4;
+
+    /**
+     * Sets the name servers, search domains and resolver params for the given network. Flushes the
+     * cache as needed (i.e. when the servers or the number of samples to store changes).
+     *
+     * @param netId the network ID of the network for which information should be configured.
+     * @param servers the DNS servers to configure for the network.
+     * @param domains the search domains to configure.
+     * @param params the params to set. This array contains RESOLVER_PARAMS_COUNT integers that
+     *   encode the contents of Bionic's __res_params struct, i.e. sample_validity is stored at
+     *   position RESOLVER_PARAMS_SAMPLE_VALIDITY, etc.
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    void setResolverConfiguration(int netId, in @utf8InCpp String[] servers,
+            in @utf8InCpp String[] domains, in int[] params);
+
+    // Array indices for resolver stats.
+    const int RESOLVER_STATS_SUCCESSES = 0;
+    const int RESOLVER_STATS_ERRORS = 1;
+    const int RESOLVER_STATS_TIMEOUTS = 2;
+    const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+    const int RESOLVER_STATS_RTT_AVG = 4;
+    const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+    const int RESOLVER_STATS_USABLE = 6;
+    const int RESOLVER_STATS_COUNT = 7;
+
+    /**
+     * Retrieves the name servers, search domains and resolver stats associated with the given
+     * network ID.
+     *
+     * @param netId the network ID of the network for which information should be retrieved.
+     * @param servers the DNS servers that are currently configured for the network.
+     * @param domains the search domains currently configured.
+     * @param params the resolver parameters configured, i.e. the contents of __res_params in order.
+     * @param stats the stats for each server in the order specified by RESOLVER_STATS_XXX
+     *         constants, serialized as an int array. The contents of this array are the number of
+     *         <ul>
+     *           <li> successes,
+     *           <li> errors,
+     *           <li> timeouts,
+     *           <li> internal errors,
+     *           <li> the RTT average,
+     *           <li> the time of the last recorded sample,
+     *           <li> and an integer indicating whether the server is usable (1) or broken (0).
+     *         </ul>
+     *         in this order. For example, the timeout counter for server N is stored at position
+     *         RESOLVER_STATS_COUNT*N + RESOLVER_STATS_TIMEOUTS
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    void getResolverInfo(int netId, out @utf8InCpp String[] servers,
+            out @utf8InCpp String[] domains, out int[] params, out int[] stats);
 }
index 6e191e1..0d15611 100644 (file)
@@ -21,7 +21,7 @@ LOCAL_MODULE := netd_integration_test
 LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
 EXTRA_LDLIBS := -lpthread
 LOCAL_SHARED_LIBRARIES += libbase libbinder libcutils liblog liblogwrap libnetdaidl libnetd_client \
-                         libutils
+                          libutils
 LOCAL_STATIC_LIBRARIES += libtestUtil
 LOCAL_AIDL_INCLUDES := system/netd/server/binder
 LOCAL_C_INCLUDES += system/netd/include system/extras/tests/include system/netd/binder/include \
@@ -36,4 +36,3 @@ LOCAL_SRC_FILES := binder_test.cpp \
                    ../server/NetdConstants.cpp
 LOCAL_MODULE_TAGS := eng tests
 include $(BUILD_NATIVE_TEST)
-
index 09d6379..4b77b62 100644 (file)
@@ -516,10 +516,10 @@ const char* DNSHeader::readHeader(const char* buffer, const char* buffer_end,
 
 /* DNS responder */
 
-DNSResponder::DNSResponder(const char* listen_address,
-                           const char* listen_service, int poll_timeout_ms,
+DNSResponder::DNSResponder(std::string listen_address,
+                           std::string listen_service, int poll_timeout_ms,
                            uint16_t error_rcode, double response_probability) :
-    listen_address_(listen_address), listen_service_(listen_service),
+    listen_address_(std::move(listen_address)), listen_service_(std::move(listen_service)),
     poll_timeout_ms_(poll_timeout_ms), error_rcode_(error_rcode),
     response_probability_(response_probability),
     socket_(-1), epoll_fd_(-1), terminate_(false) { }
index 4ed4bb2..ccb63d9 100644 (file)
@@ -42,7 +42,7 @@ struct DNSRecord;
  */
 class DNSResponder {
 public:
-    DNSResponder(const char* listen_address, const char* listen_service,
+    DNSResponder(std::string listen_address, std::string listen_service,
                  int poll_timeout_ms, uint16_t error_rcode,
                  double response_probability);
     ~DNSResponder();
@@ -52,6 +52,12 @@ public:
     bool running() const;
     bool startServer();
     bool stopServer();
+    const std::string& listen_address() const {
+        return listen_address_;
+    }
+    const std::string& listen_service() const {
+        return listen_service_;
+    }
     std::vector<std::pair<std::string, ns_type>> queries() const;
     void clearQueries();
 
index 55453f2..ef91266 100644 (file)
 #include <android-base/stringprintf.h>
 #include <private/android_filesystem_config.h>
 
+#include <algorithm>
+#include <chrono>
+#include <iterator>
+#include <numeric>
 #include <thread>
 
+#define LOG_TAG "netd_test"
+// TODO: make this dynamic and stop depending on implementation details.
+#define TEST_OEM_NETWORK "oem29"
+#define TEST_NETID 30
+
 #include "NetdClient.h"
 
 #include <gtest/gtest.h>
-#define LOG_TAG "resolverTest"
+
 #include <utils/Log.h>
+
 #include <testUtil.h>
 
 #include "dns_responder.h"
 #include "resolv_params.h"
+#include "ResolverStats.h"
+
+#include "android/net/INetd.h"
+#include "binder/IServiceManager.h"
 
 using android::base::StringPrintf;
 using android::base::StringAppendF;
-
-// TODO: make this dynamic and stop depending on implementation details.
-#define TEST_OEM_NETWORK "oem29"
-#define TEST_NETID 30
+using android::net::ResolverStats;
+
+// Emulates the behavior of UnorderedElementsAreArray, which currently cannot be used.
+// TODO: Use UnorderedElementsAreArray, which depends on being able to compile libgmock_host,
+// if that is not possible, improve this hacky algorithm, which is O(n**2)
+template <class A, class B>
+bool UnorderedCompareArray(const A& a, const B& b) {
+    if (a.size() != b.size()) return false;
+    for (const auto& a_elem : a) {
+        size_t a_count = 0;
+        for (const auto& a_elem2 : a) {
+            if (a_elem == a_elem2) {
+                ++a_count;
+            }
+        }
+        size_t b_count = 0;
+        for (const auto& b_elem : b) {
+            if (a_elem == b_elem) ++b_count;
+        }
+        if (a_count != b_count) return false;
+    }
+    return true;
+}
 
 // The only response code used in this test, see
 // frameworks/base/services/java/com/android/server/NetworkManagementService.java
@@ -80,7 +113,6 @@ int netdCommand(const char* sockname, const char* command) {
     return atoi(buffer);
 }
 
-
 bool expectNetdResult(int expected, const char* sockname, const char* format, ...) {
     char command[256];
     va_list args;
@@ -92,15 +124,26 @@ bool expectNetdResult(int expected, const char* sockname, const char* format, ..
     return (200 <= expected && expected < 300);
 }
 
-
 class ResolverTest : public ::testing::Test {
 protected:
+    struct Mapping {
+        std::string host;
+        std::string entry;
+        std::string ip4;
+        std::string ip6;
+    };
+
     virtual void SetUp() {
         // Ensure resolutions go via proxy.
         setenv("ANDROID_DNS_MODE", "", 1);
         uid = getuid();
         pid = getpid();
         SetupOemNetwork();
+
+        // binder setup
+        auto binder = android::defaultServiceManager()->getService(android::String16("netd"));
+        ASSERT_TRUE(binder != nullptr);
+        mNetdSrv = android::interface_cast<android::net::INetd>(binder);
     }
 
     virtual void TearDown() {
@@ -118,6 +161,53 @@ protected:
         ASSERT_EQ((unsigned) oemNetId, getNetworkForProcess());
     }
 
+    void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+            std::vector<Mapping>* mappings) const {
+        mappings->resize(num_hosts * domains.size());
+        auto mappings_it = mappings->begin();
+        for (unsigned i = 0 ; i < num_hosts ; ++i) {
+            for (const auto& domain : domains) {
+                ASSERT_TRUE(mappings_it != mappings->end());
+                mappings_it->host = StringPrintf("host%u", i);
+                mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(),
+                        domain.c_str());
+                mappings_it->ip4 = StringPrintf("192.0.2.%u", i%253 + 1);
+                mappings_it->ip6 = StringPrintf("2001:db8::%x", i%65534 + 1);
+                ++mappings_it;
+            }
+        }
+    }
+
+    void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+            std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+            std::vector<std::string>* servers) const {
+        ASSERT_TRUE(num_servers != 0 && num_servers < 100);
+        const char* listen_srv = "53";
+        dns->resize(num_servers);
+        servers->resize(num_servers);
+        for (unsigned i = 0 ; i < num_servers ; ++i) {
+            auto& server = (*servers)[i];
+            auto& d = (*dns)[i];
+            server = StringPrintf("127.0.0.%u", i + 100);
+            d = std::make_unique<test::DNSResponder>(server, listen_srv, 250,
+                    ns_rcode::ns_r_servfail, 1.0);
+            ASSERT_TRUE(d.get() != nullptr);
+            for (const auto& mapping : mappings) {
+                d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
+                d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
+            }
+            ASSERT_TRUE(d->startServer());
+        }
+    }
+
+    void ShutdownDNSServers(std::vector<std::unique_ptr<test::DNSResponder>>* dns) const {
+        for (const auto& d : *dns) {
+            ASSERT_TRUE(d.get() != nullptr);
+            d->stopServer();
+        }
+        dns->clear();
+    }
+
     void TearDownOemNetwork() {
         if (oemNetId != -1) {
             expectNetdResult(ResponseCodeOK, "netd",
@@ -125,23 +215,27 @@ protected:
         }
     }
 
+    bool SetResolversForNetwork(const std::vector<std::string>& servers,
+            const std::vector<std::string>& domains, const std::vector<int>& params) {
+        auto rv = mNetdSrv->setResolverConfiguration(TEST_NETID, servers, domains, params);
+        return rv.isOk();
+    }
+
     bool SetResolversForNetwork(const std::vector<std::string>& searchDomains,
             const std::vector<std::string>& servers, const std::string& params) {
-        // No use case for empty domains / servers (yet).
-        if (searchDomains.empty() || servers.empty()) return false;
-
-        std::string cmd = StringPrintf("resolver setnetdns %d \"%s", oemNetId,
-                searchDomains[0].c_str());
-        for (size_t i = 1 ; i < searchDomains.size() ; ++i) {
-            cmd += " ";
-            cmd += searchDomains[i];
+        std::string cmd = StringPrintf("resolver setnetdns %d \"", oemNetId);
+        if (!searchDomains.empty()) {
+            cmd += searchDomains[0].c_str();
+            for (size_t i = 1 ; i < searchDomains.size() ; ++i) {
+                cmd += " ";
+                cmd += searchDomains[i];
+            }
         }
-        cmd += "\" ";
+        cmd += "\"";
 
-        cmd += servers[0];
-        for (size_t i = 1 ; i < servers.size() ; ++i) {
+        for (const auto& str : servers) {
             cmd += " ";
-            cmd += servers[i];
+            cmd += str;
         }
 
         if (!params.empty()) {
@@ -157,6 +251,28 @@ protected:
         return true;
     }
 
+    bool GetResolverInfo(std::vector<std::string>* servers, std::vector<std::string>* domains,
+            __res_params* params, std::vector<ResolverStats>* stats) {
+        using android::net::INetd;
+        std::vector<int32_t> params32;
+        std::vector<int32_t> stats32;
+        auto rv = mNetdSrv->getResolverInfo(TEST_NETID, servers, domains, &params32, &stats32);
+        if (!rv.isOk() || params32.size() != INetd::RESOLVER_PARAMS_COUNT) {
+            return false;
+        }
+        *params = __res_params {
+            .sample_validity = static_cast<uint16_t>(
+                    params32[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
+            .success_threshold = static_cast<uint8_t>(
+                    params32[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
+            .min_samples = static_cast<uint8_t>(
+                    params32[INetd::RESOLVER_PARAMS_MIN_SAMPLES]),
+            .max_samples = static_cast<uint8_t>(
+                    params32[INetd::RESOLVER_PARAMS_MAX_SAMPLES])
+        };
+        return ResolverStats::decodeAll(stats32, stats);
+    }
+
     std::string ToString(const hostent* he) const {
         if (he == nullptr) return "<null>";
         char buffer[INET6_ADDRSTRLEN];
@@ -184,7 +300,6 @@ protected:
         auto queries = dns.queries();
         size_t found = 0;
         for (const auto& p : queries) {
-            std::cout << "query " << p.first << "\n";
             if (p.first == name) {
                 ++found;
             }
@@ -197,7 +312,6 @@ protected:
         auto queries = dns.queries();
         size_t found = 0;
         for (const auto& p : queries) {
-            std::cout << "query " << p.first << "\n";
             if (p.second == type && p.first == name) {
                 ++found;
             }
@@ -205,9 +319,56 @@ protected:
         return found;
     }
 
+    void RunGetAddrInfoStressTest_Binder(unsigned num_hosts, unsigned num_threads,
+            unsigned num_queries) {
+        std::vector<std::string> domains = { "example.com" };
+        std::vector<std::unique_ptr<test::DNSResponder>> dns;
+        std::vector<std::string> servers;
+        std::vector<Mapping> mappings;
+        ASSERT_NO_FATAL_FAILURE(SetupMappings(num_hosts, domains, &mappings));
+        ASSERT_NO_FATAL_FAILURE(SetupDNSServers(MAXNS, mappings, &dns, &servers));
+
+        std::vector<int> params = { 300, 25, 8, 8 };
+        ASSERT_TRUE(SetResolversForNetwork(servers, domains, params));
+
+        auto t0 = std::chrono::steady_clock::now();
+        std::vector<std::thread> threads(num_threads);
+        for (std::thread& thread : threads) {
+           thread = std::thread([this, &servers, &dns, &mappings, num_queries]() {
+                for (unsigned i = 0 ; i < num_queries ; ++i) {
+                    uint32_t ofs = arc4random_uniform(mappings.size());
+                    ASSERT_TRUE(ofs < mappings.size());
+                    auto& mapping = mappings[i];
+                    addrinfo* result = nullptr;
+                    int rv = getaddrinfo(mapping.host.c_str(), nullptr, nullptr, &result);
+                    EXPECT_EQ(0, rv) << "error [" << rv << "] " << gai_strerror(rv);
+                    if (rv == 0) {
+                        std::string result_str = ToString(result);
+                        EXPECT_TRUE(result_str == mapping.ip4 || result_str == mapping.ip6)
+                            << "result='" << result_str << "', ip4='" << mapping.ip4
+                            << "', ip6='" << mapping.ip6;
+                    }
+                    if (result) {
+                        freeaddrinfo(result);
+                        result = nullptr;
+                    }
+                }
+            });
+        }
+
+        for (std::thread& thread : threads) {
+            thread.join();
+        }
+        auto t1 = std::chrono::steady_clock::now();
+        ALOGI("%u hosts, %u threads, %u queries, %Es", num_hosts, num_threads, num_queries,
+                std::chrono::duration<double>(t1 - t0).count());
+        ASSERT_NO_FATAL_FAILURE(ShutdownDNSServers(&dns));
+    }
+
     int pid;
     int uid;
     int oemNetId = -1;
+    android::sp<android::net::INetd> mNetdSrv = nullptr;
     const std::vector<std::string> mDefaultSearchDomains = { "example.com" };
     // <sample validity in s> <success threshold in percent> <min samples> <max samples>
     const std::string mDefaultParams = "300 25 8 8";
@@ -234,13 +395,77 @@ TEST_F(ResolverTest, GetHostByName) {
     dns.stopServer();
 }
 
+TEST_F(ResolverTest, TestBinderSerialization) {
+    using android::net::INetd;
+    std::vector<int> params_offsets = {
+        INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY,
+        INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD,
+        INetd::RESOLVER_PARAMS_MIN_SAMPLES,
+        INetd::RESOLVER_PARAMS_MAX_SAMPLES
+    };
+    int size = static_cast<int>(params_offsets.size());
+    EXPECT_EQ(size, INetd::RESOLVER_PARAMS_COUNT);
+    std::sort(params_offsets.begin(), params_offsets.end());
+    for (int i = 0 ; i < size ; ++i) {
+        EXPECT_EQ(params_offsets[i], i);
+    }
+}
+
+TEST_F(ResolverTest, GetHostByName_Binder) {
+    using android::net::INetd;
+
+    std::vector<std::string> domains = { "example.com" };
+    std::vector<std::unique_ptr<test::DNSResponder>> dns;
+    std::vector<std::string> servers;
+    std::vector<Mapping> mappings;
+    ASSERT_NO_FATAL_FAILURE(SetupMappings(1, domains, &mappings));
+    ASSERT_NO_FATAL_FAILURE(SetupDNSServers(4, mappings, &dns, &servers));
+    ASSERT_EQ(1U, mappings.size());
+    const Mapping& mapping = mappings[0];
+
+    std::vector<int> params = { 300, 25, 8, 8 };
+    ASSERT_TRUE(SetResolversForNetwork(servers, domains, params));
+
+    const hostent* result = gethostbyname(mapping.host.c_str());
+    size_t total_queries = std::accumulate(dns.begin(), dns.end(), 0,
+            [this, &mapping](size_t total, auto& d) {
+                return total + GetNumQueriesForType(*d, ns_type::ns_t_a, mapping.entry.c_str());
+            });
+
+    EXPECT_LE(1U, total_queries);
+    ASSERT_FALSE(result == nullptr);
+    ASSERT_EQ(4, result->h_length);
+    ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+    EXPECT_EQ(mapping.ip4, ToString(result));
+    EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+
+    std::vector<std::string> res_servers;
+    std::vector<std::string> res_domains;
+    __res_params res_params;
+    std::vector<ResolverStats> res_stats;
+    ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_params, &res_stats));
+    EXPECT_EQ(servers.size(), res_servers.size());
+    EXPECT_EQ(domains.size(), res_domains.size());
+    ASSERT_EQ(INetd::RESOLVER_PARAMS_COUNT, params.size());
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY], res_params.sample_validity);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD], res_params.success_threshold);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_MIN_SAMPLES], res_params.min_samples);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_MAX_SAMPLES], res_params.max_samples);
+    EXPECT_EQ(servers.size(), res_stats.size());
+
+    EXPECT_TRUE(UnorderedCompareArray(res_servers, servers));
+    EXPECT_TRUE(UnorderedCompareArray(res_domains, domains));
+
+    ASSERT_NO_FATAL_FAILURE(ShutdownDNSServers(&dns));
+}
+
 TEST_F(ResolverTest, GetAddrInfo) {
     addrinfo* result = nullptr;
 
     const char* listen_addr = "127.0.0.4";
     const char* listen_addr2 = "127.0.0.5";
     const char* listen_srv = "53";
-    const char* host_name = "howdie.example.com.";
+    const char* host_name = "howdy.example.com.";
     test::DNSResponder dns(listen_addr, listen_srv, 250,
                            ns_rcode::ns_r_servfail, 1.0);
     dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
@@ -250,26 +475,30 @@ TEST_F(ResolverTest, GetAddrInfo) {
     ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, mDefaultParams));
 
     dns.clearQueries();
-    EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result));
+    EXPECT_EQ(0, getaddrinfo("howdy", nullptr, nullptr, &result));
     size_t found = GetNumQueries(dns, host_name);
     EXPECT_LE(1U, found);
     // Could be A or AAAA
     std::string result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
         << ", result_str='" << result_str << "'";
-    if (result) freeaddrinfo(result);
-    result = nullptr;
+    // TODO: Use ScopedAddrinfo or similar once it is available in a common header file.
+    if (result) {
+        freeaddrinfo(result);
+        result = nullptr;
+    }
 
     // Verify that the name is cached.
-    size_t old_found = found;
-    EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result));
+    EXPECT_EQ(0, getaddrinfo("howdy", nullptr, nullptr, &result));
     found = GetNumQueries(dns, host_name);
-    EXPECT_EQ(old_found, found);
+    EXPECT_LE(1U, found);
     result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
         << result_str;
-    if (result) freeaddrinfo(result);
-    result = nullptr;
+    if (result) {
+        freeaddrinfo(result);
+        result = nullptr;
+    }
 
     // Change the DNS resolver, ensure that queries are no longer cached.
     dns.clearQueries();
@@ -280,7 +509,7 @@ TEST_F(ResolverTest, GetAddrInfo) {
     ASSERT_TRUE(dns2.startServer());
     servers = { listen_addr2 };
     ASSERT_TRUE(SetResolversForNetwork(mDefaultSearchDomains, servers, mDefaultParams));
-    EXPECT_EQ(0, getaddrinfo("howdie", nullptr, nullptr, &result));
+    EXPECT_EQ(0, getaddrinfo("howdy", nullptr, nullptr, &result));
     found = GetNumQueries(dns, host_name);
     size_t found2 = GetNumQueries(dns2, host_name);
     EXPECT_EQ(0U, found);
@@ -290,8 +519,10 @@ TEST_F(ResolverTest, GetAddrInfo) {
     result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
         << ", result_str='" << result_str << "'";
-    if (result) freeaddrinfo(result);
-    result = nullptr;
+    if (result) {
+        freeaddrinfo(result);
+        result = nullptr;
+    }
     dns.stopServer();
     dns2.stopServer();
 }
@@ -315,7 +546,10 @@ TEST_F(ResolverTest, GetAddrInfoV4) {
     EXPECT_EQ(0, getaddrinfo("hola", nullptr, &hints, &result));
     EXPECT_EQ(1U, GetNumQueries(dns, host_name));
     EXPECT_EQ("1.2.3.5", ToString(result));
-    if (result) freeaddrinfo(result);
+    if (result) {
+        freeaddrinfo(result);
+        result = nullptr;
+    }
 }
 
 TEST_F(ResolverTest, MultidomainResolution) {
@@ -375,6 +609,10 @@ TEST_F(ResolverTest, GetAddrInfoV6_failing) {
     for (int i = 0 ; i < sample_count ; ++i) {
         std::string domain = StringPrintf("nonexistent%d", i);
         getaddrinfo(domain.c_str(), nullptr, &hints, &result);
+        if (result) {
+            freeaddrinfo(result);
+            result = nullptr;
+        }
     }
     // Due to 100% errors for all possible samples, the server should be ignored from now on and
     // only the second one used for all following queries, until NSSAMPLE_VALIDITY is reached.
@@ -383,7 +621,10 @@ TEST_F(ResolverTest, GetAddrInfoV6_failing) {
     EXPECT_EQ(0, getaddrinfo("ohayou", nullptr, &hints, &result));
     EXPECT_EQ(0U, GetNumQueries(dns0, host_name));
     EXPECT_EQ(1U, GetNumQueries(dns1, host_name));
-    if (result) freeaddrinfo(result);
+    if (result) {
+        freeaddrinfo(result);
+        result = nullptr;
+    }
 }
 
 TEST_F(ResolverTest, GetAddrInfoV6_concurrent) {
@@ -425,9 +666,47 @@ TEST_F(ResolverTest, GetAddrInfoV6_concurrent) {
             addrinfo* result = nullptr;
             int rv = getaddrinfo("konbanha", nullptr, &hints, &result);
             EXPECT_EQ(0, rv) << "error [" << rv << "] " << gai_strerror(rv);
+            if (result) {
+                freeaddrinfo(result);
+                result = nullptr;
+            }
         });
     }
     for (std::thread& thread : threads) {
         thread.join();
     }
 }
+
+TEST_F(ResolverTest, GetAddrInfoStressTest_Binder_100) {
+    const unsigned num_hosts = 100;
+    const unsigned num_threads = 100;
+    const unsigned num_queries = 100;
+    ASSERT_NO_FATAL_FAILURE(RunGetAddrInfoStressTest_Binder(num_hosts, num_threads, num_queries));
+}
+
+TEST_F(ResolverTest, GetAddrInfoStressTest_Binder_100000) {
+    const unsigned num_hosts = 100000;
+    const unsigned num_threads = 100;
+    const unsigned num_queries = 100;
+    ASSERT_NO_FATAL_FAILURE(RunGetAddrInfoStressTest_Binder(num_hosts, num_threads, num_queries));
+}
+
+TEST_F(ResolverTest, EmptySetup) {
+    using android::net::INetd;
+    std::vector<std::string> servers;
+    std::vector<std::string> domains;
+    std::vector<int> params = { 300, 25, 8, 8 };
+    ASSERT_TRUE(SetResolversForNetwork(servers, domains, params));
+    std::vector<std::string> res_servers;
+    std::vector<std::string> res_domains;
+    __res_params res_params;
+    std::vector<ResolverStats> res_stats;
+    ASSERT_TRUE(GetResolverInfo(&res_servers, &res_domains, &res_params, &res_stats));
+    EXPECT_EQ(0U, res_servers.size());
+    EXPECT_EQ(0U, res_domains.size());
+    ASSERT_EQ(INetd::RESOLVER_PARAMS_COUNT, params.size());
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_SAMPLE_VALIDITY], res_params.sample_validity);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_SUCCESS_THRESHOLD], res_params.success_threshold);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_MIN_SAMPLES], res_params.min_samples);
+    EXPECT_EQ(params[INetd::RESOLVER_PARAMS_MAX_SAMPLES], res_params.max_samples);
+}