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/NetdNativeService.cpp b/server/NetdNativeService.cpp
index a8f5c3b..5ee8202 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -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 @@
         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();
 }
 
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 22c81fc..b5c8f69 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -42,6 +42,11 @@
             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
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;
+}
diff --git a/server/ResolverController.h b/server/ResolverController.h
index 048ff3f..a3810d4 100644
--- a/server/ResolverController.h
+++ b/server/ResolverController.h
@@ -17,20 +17,44 @@
 #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
index 0000000..be63d88
--- /dev/null
+++ b/server/ResolverStats.h
@@ -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_ */
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index 616842b..097f98a 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -85,4 +85,64 @@
      * 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);
 }