Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70931aa0 authored by Luke Huang's avatar Luke Huang
Browse files

Refine asynchronous DNS API flag NO_RETRY behavior

Previously, query with NO_RETRY flag would try to query all the ns once.
Fix it to try only one DNS server per query.
The server is selected randomly from available nameserver list.

Bug: 122564854

Test: built, flashed, booted
      system/netd/tests/runtests.sh pass
      atest CtsNativeNetDnsTestCases
      atest DnsResolverTest

Change-Id: I489c1762b26dd39014ede85d6b6f0e16051ec5e9
parent b0cbe354
Loading
Loading
Loading
Loading
+20 −7
Original line number Diff line number Diff line
@@ -295,14 +295,25 @@ bool simpleStrtoul(const char* input, IntegralType* output, int base = 10) {
    return true;
}

bool parseQuery(const uint8_t* msg, size_t msgLen, int* rr_type, std::string* rr_name) {
bool setQueryId(uint8_t* msg, size_t msgLen, uint16_t query_id) {
    if (msgLen < sizeof(HEADER)) {
        errno = EINVAL;
        return false;
    }
    auto hp = reinterpret_cast<HEADER*>(msg);
    hp->id = htons(query_id);
    return true;
}

bool parseQuery(const uint8_t* msg, size_t msgLen, uint16_t* query_id, 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;
    }

    *query_id = ns_msg_id(handle);
    *rr_name = ns_rr_name(rr);
    *rr_type = ns_rr_type(rr);
    return true;
@@ -850,10 +861,11 @@ void DnsProxyListener::ResNSendHandler::run() {
    const uid_t uid = mClient->getUid();
    int rr_type = 0;
    std::string rr_name;
    uint16_t original_query_id = 0;

    // 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 (!parseQuery(msg.data(), msgLen, &original_query_id, &rr_type, &rr_name) ||
        !setQueryId(msg.data(), msgLen, arc4random_uniform(65536))) {
        // If the query couldn't be parsed, block the request.
        ALOGW("resnsend: from UID %d, invalid query", uid);
        sendBE32(mClient, -EINVAL);
@@ -890,8 +902,9 @@ void DnsProxyListener::ResNSendHandler::run() {
        return;
    }

    // Send answer
    if (!sendLenAndData(mClient, nsendAns, ansBuf.data())) {
    // Restore query id and send answer
    if (!setQueryId(ansBuf.data(), nsendAns, original_query_id) ||
        !sendLenAndData(mClient, nsendAns, ansBuf.data())) {
        ALOGW("resnsend: failed to send answer to uid %d: %s", uid, strerror(errno));
        return;
    }
+2 −2
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ extern struct ResolvStub {
                                                  res_params* params, res_stats stats[MAXNS],
                                                  int* wait_for_pending_req_timeout_count);

    void (*android_net_res_stats_get_usable_servers)(const res_params* params, res_stats stats[],
    int (*android_net_res_stats_get_usable_servers)(const res_params* params, res_stats stats[],
                                                    int nscount, bool valid_servers[]);

    void (*resolv_delete_cache_for_net)(unsigned netid);
+3 −3
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_info_for_net(
        int* wait_for_pending_req_timeout_count);

// Returns an array of bools indicating which servers are considered good
LIBNETD_RESOLV_PUBLIC void android_net_res_stats_get_usable_servers(const res_params* params,
LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_usable_servers(const res_params* params,
                                                                   res_stats stats[], int nscount,
                                                                   bool valid_servers[]);

+19 −1
Original line number Diff line number Diff line
@@ -246,6 +246,15 @@ static int random_bind(int s, int family) {
}
/* BIONIC-END */

// Disables all nameservers other than selectedServer
static void res_set_usable_server(int selectedServer, int nscount, bool usable_servers[]) {
    int usableIndex = 0;
    for (int ns = 0; ns < nscount; ns++) {
        if (usable_servers[ns]) ++usableIndex;
        if (usableIndex != selectedServer) usable_servers[ns] = false;
    }
}

/* int
 * res_isourserver(ina)
 *	looks up "ina" in _res.ns_addr_list[]
@@ -493,7 +502,16 @@ int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int a
        return -ESRCH;
    }
    bool usable_servers[MAXNS];
    android_net_res_stats_get_usable_servers(&params, stats, statp->nscount, usable_servers);
    int usableServersCount = android_net_res_stats_get_usable_servers(
            &params, stats, statp->nscount, usable_servers);

    if ((flags & ANDROID_RESOLV_NO_RETRY) && usableServersCount > 1) {
        auto hp = reinterpret_cast<const HEADER*>(buf);

        // Select a random server based on the query id
        int selectedServer = (hp->id % usableServersCount) + 1;
        res_set_usable_server(selectedServer, statp->nscount, usable_servers);
    }
    if (params.retry_count != 0) statp->retry = params.retry_count;

    /*
+3 −2
Original line number Diff line number Diff line
@@ -144,7 +144,7 @@ static bool res_stats_usable_server(const res_params* params, res_stats* stats)
    return 1;
}

void android_net_res_stats_get_usable_servers(const res_params* params, res_stats stats[],
int android_net_res_stats_get_usable_servers(const res_params* params, res_stats stats[],
                                             int nscount, bool usable_servers[]) {
    unsigned usable_servers_found = 0;
    for (int ns = 0; ns < nscount; ns++) {
@@ -161,4 +161,5 @@ void android_net_res_stats_get_usable_servers(const res_params* params, res_stat
            usable_servers[ns] = true;
        }
    }
    return (usable_servers_found == 0) ? nscount : usable_servers_found;
}
Loading