Add two Netd binder calls to set/get resolver config.

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
diff --git a/server/ResolverController.cpp b/server/ResolverController.cpp
index 16cfd53..20c9302 100644
--- a/server/ResolverController.cpp
+++ b/server/ResolverController.cpp
@@ -17,25 +17,33 @@
 #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 @@
 
     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;
+}