Implementation of control flags in asynchronous DNS query API
Flags definitions are in multinetwork.h
Test: built, flashed, booted
system/netd/tests/runtests.sh passes
Change-Id: Iab1983b783d1470bc1cf23489abbef7a2d88e860
diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp
index 860eb98..4a46c1a 100644
--- a/resolv/getaddrinfo.cpp
+++ b/resolv/getaddrinfo.cpp
@@ -1635,7 +1635,7 @@
return n;
}
- n = res_nsend(res, buf, n, answer, anslen, &rcode);
+ n = res_nsend(res, buf, n, answer, anslen, &rcode, 0);
if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
// Record rcode from DNS response header only if no timeout.
// Keep rcode timeout for reporting later if any.
diff --git a/resolv/include/netd_resolv/resolv.h b/resolv/include/netd_resolv/resolv.h
index e30f31a..4f43107 100644
--- a/resolv/include/netd_resolv/resolv.h
+++ b/resolv/include/netd_resolv/resolv.h
@@ -32,6 +32,8 @@
* This header contains declarations related to per-network DNS server selection.
* They are used by system/netd/ and should not be exposed by the public NDK headers.
*/
+#include <android/multinetwork.h> // ResNsendFlags
+
#include <netinet/in.h>
#include "params.h"
@@ -113,9 +115,9 @@
LIBNETD_RESOLV_PUBLIC bool resolv_has_nameservers(unsigned netid);
// Query dns with raw msg
-// TODO: Add a way to control query parameter, like flags, or maybe res_options or even res_state.
-LIBNETD_RESOLV_PUBLIC int resolv_res_nsend(const android_net_context* netContext, const u_char* msg,
- int msgLen, u_char* ans, int ansLen, int* rcode);
+LIBNETD_RESOLV_PUBLIC int resolv_res_nsend(const android_net_context* netContext,
+ const uint8_t* msg, int msgLen, uint8_t* ans, int ansLen,
+ int* rcode, uint32_t flags);
// Set name servers for a network
LIBNETD_RESOLV_PUBLIC int resolv_set_nameservers_for_net(unsigned netid, const char** servers,
diff --git a/resolv/include/netd_resolv/resolv_stub.h b/resolv/include/netd_resolv/resolv_stub.h
index fddabcf..b4364ba 100644
--- a/resolv/include/netd_resolv/resolv_stub.h
+++ b/resolv/include/netd_resolv/resolv_stub.h
@@ -66,7 +66,7 @@
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,
- u_char* ans, int ansLen, int* rcode);
+ u_char* ans, int ansLen, int* rcode, uint32_t flags);
int (*resolv_set_nameservers_for_net)(unsigned netid, const char** servers, unsigned numservers,
const char* domains, const __res_params* params);
diff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp
index 60a60df..e7a3b9b 100644
--- a/resolv/res_cache.cpp
+++ b/resolv/res_cache.cpp
@@ -49,7 +49,6 @@
#include <android-base/logging.h>
-#include "netd_resolv/resolv.h"
#include "res_state_ext.h"
#include "resolv_cache.h"
#include "resolv_private.h"
@@ -1248,7 +1247,11 @@
}
/* notify the cache that the query failed */
-void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen) {
+void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags) {
+ // We should not notify with these flags.
+ if (flags & (ANDROID_RESOLV_NO_CACHE_STORE | ANDROID_RESOLV_NO_CACHE_LOOKUP)) {
+ return;
+ }
Entry key[1];
Cache* cache;
@@ -1463,7 +1466,11 @@
}
ResolvCacheStatus _resolv_cache_lookup(unsigned netid, const void* query, int querylen,
- void* answer, int answersize, int* answerlen) {
+ void* answer, int answersize, int* answerlen,
+ uint32_t flags) {
+ if (flags & ANDROID_RESOLV_NO_CACHE_LOOKUP) {
+ return RESOLV_CACHE_SKIP;
+ }
Entry key[1];
Entry** lookup;
Entry* e;
@@ -1498,6 +1505,11 @@
if (e == NULL) {
VLOG << "NOT IN CACHE";
+ // If it is no-cache-store mode, we won't wait for possible query.
+ if (flags & ANDROID_RESOLV_NO_CACHE_STORE) {
+ result = RESOLV_CACHE_SKIP;
+ goto Exit;
+ }
// calling thread will wait if an outstanding request is found
// that matching this query
if (!_cache_check_pending_request_locked(&cache, key, netid) || cache == NULL) {
diff --git a/resolv/res_query.cpp b/resolv/res_query.cpp
index f81b33e..4ae13bf 100644
--- a/resolv/res_query.cpp
+++ b/resolv/res_query.cpp
@@ -140,7 +140,7 @@
*herrno = NO_RECOVERY;
return n;
}
- n = res_nsend(statp, buf, n, answer, anslen, &rcode);
+ n = res_nsend(statp, buf, n, answer, anslen, &rcode, 0);
if (n < 0) {
/* if the query choked with EDNS0, retry without EDNS0 */
if ((statp->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0U &&
diff --git a/resolv/res_send.cpp b/resolv/res_send.cpp
index a8b2aeb..21017ac 100644
--- a/resolv/res_send.cpp
+++ b/resolv/res_send.cpp
@@ -388,7 +388,8 @@
return (1);
}
-int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode) {
+int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode,
+ uint32_t flags) {
int gotsomewhere, terrno, v_circuit, resplen, n;
ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
@@ -404,7 +405,7 @@
terrno = ETIMEDOUT;
int anslen = 0;
- cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen);
+ cache_status = _resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
if (cache_status == RESOLV_CACHE_FOUND) {
return anslen;
@@ -417,7 +418,7 @@
// We have no nameservers configured, so there's no point trying.
// Tell the cache the query failed, or any retries and anyone else asking the same
// question will block for PENDING_REQUEST_TIMEOUT seconds instead of failing fast.
- _resolv_cache_query_failed(statp->netid, buf, buflen);
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
// TODO: Remove errno once callers stop using it
errno = ESRCH;
@@ -507,7 +508,9 @@
/*
* Send request, RETRY times, or until successful.
*/
- for (int attempt = 0; attempt < statp->retry; ++attempt) {
+ int retryTimes = (flags & ANDROID_RESOLV_NO_RETRY) ? 1 : statp->retry;
+
+ for (int attempt = 0; attempt < retryTimes; ++attempt) {
struct res_stats stats[MAXNS];
struct __res_params params;
int revision_id = resolv_cache_get_resolver_stats(statp->netid, ¶ms, stats);
@@ -539,7 +542,7 @@
return resplen;
}
if (!fallback) {
- _resolv_cache_query_failed(statp->netid, buf, buflen);
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
res_nclose(statp);
return -terrno;
}
@@ -554,7 +557,8 @@
if (v_circuit) {
/* Use VC; at most one attempt per server. */
- attempt = statp->retry;
+ bool shouldRecordStats = (attempt == 0);
+ attempt = retryTimes;
n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode,
&delay);
@@ -564,7 +568,7 @@
* queries that deterministically fail (e.g., a name that always returns
* SERVFAIL or times out) do not unduly affect the stats.
*/
- if (attempt == 0) {
+ if (shouldRecordStats) {
res_sample sample;
_res_stats_set_sample(&sample, now, *rcode, delay);
_resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
@@ -574,7 +578,7 @@
VLOG << "used send_vc " << n;
if (n < 0) {
- _resolv_cache_query_failed(statp->netid, buf, buflen);
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
res_nclose(statp);
return -terrno;
};
@@ -598,7 +602,7 @@
VLOG << "used send_dg " << n;
if (n < 0) {
- _resolv_cache_query_failed(statp->netid, buf, buflen);
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
res_nclose(statp);
return -terrno;
};
@@ -645,7 +649,7 @@
} else {
errno = terrno;
}
- _resolv_cache_query_failed(statp->netid, buf, buflen);
+ _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
return -terrno;
}
@@ -1314,11 +1318,11 @@
}
}
-int resolv_res_nsend(const android_net_context* netContext, const u_char* msg, int msgLen,
- u_char* ans, int ansLen, int* rcode) {
+int resolv_res_nsend(const android_net_context* netContext, const uint8_t* msg, int msgLen,
+ uint8_t* ans, int ansLen, int* rcode, uint32_t flags) {
res_state res = res_get_state();
res_setnetcontext(res, netContext);
_resolv_populate_res_for_net(res);
*rcode = NOERROR;
- return res_nsend(res, msg, msgLen, ans, ansLen, rcode);
-}
+ return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags);
+}
\ No newline at end of file
diff --git a/resolv/resolv_cache.h b/resolv/resolv_cache.h
index dd8aa80..4cfa119 100644
--- a/resolv/resolv_cache.h
+++ b/resolv/resolv_cache.h
@@ -28,6 +28,8 @@
#ifndef NETD_RESOLV_CACHE_H
#define NETD_RESOLV_CACHE_H
+#include "netd_resolv/resolv.h"
+
#include <stddef.h>
struct __res_state;
@@ -41,12 +43,13 @@
RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */
/* or the answer buffer is too small */
RESOLV_CACHE_NOTFOUND, /* the cache doesn't know about this query */
- RESOLV_CACHE_FOUND /* the cache found the answer */
+ RESOLV_CACHE_FOUND, /* the cache found the answer */
+ RESOLV_CACHE_SKIP /* Don't do anything on cache */
} ResolvCacheStatus;
ResolvCacheStatus _resolv_cache_lookup(unsigned netid, const void* query, int querylen,
- void* answer, int answersize,
- int* answerlen);
+ void* answer, int answersize, int* answerlen,
+ uint32_t flags);
/* add a (query,answer) to the cache, only call if _resolv_cache_lookup
* did return RESOLV_CACHE_NOTFOUND
@@ -55,6 +58,6 @@
int answerlen);
/* Notify the cache a request failed */
-void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen);
+void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags);
#endif // NETD_RESOLV_CACHE_H
diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h
index d38fb43..3dfb999 100644
--- a/resolv/resolv_private.h
+++ b/resolv/resolv_private.h
@@ -239,7 +239,7 @@
int res_nquerydomain(res_state, const char*, const char*, int, int, u_char*, int, int*);
int res_nmkquery(res_state, int, const char*, int, int, const u_char*, int, const u_char*, u_char*,
int);
-int res_nsend(res_state, const u_char*, int, u_char*, int, int*);
+int res_nsend(res_state, const u_char*, int, u_char*, int, int*, uint32_t);
void res_nclose(res_state);
int res_nopt(res_state, int, u_char*, int, int);
int res_vinit(res_state, int);
diff --git a/resolv/resolver_test.cpp b/resolv/resolver_test.cpp
index 5c5d833..959b1ed 100644
--- a/resolv/resolver_test.cpp
+++ b/resolv/resolver_test.cpp
@@ -37,6 +37,7 @@
#include <thread>
#include <android-base/stringprintf.h>
+#include <android/multinetwork.h> // ResNsendFlags
#include <cutils/sockets.h>
#include <gtest/gtest.h>
#include <openssl/base64.h>
@@ -1577,6 +1578,10 @@
revents = wait_fd[0].revents;
if (revents & POLLIN) {
int n = resNetworkResult(fd, rcode, buf, bufLen);
+ // Verify that resNetworkResult() closed the fd
+ char dummy;
+ EXPECT_EQ(-1, read(fd, &dummy, sizeof dummy));
+ EXPECT_EQ(EBADF, errno);
return n;
}
return -1;
@@ -1622,6 +1627,23 @@
return s;
}
+void expectAnswersValid(int fd, int ipType, const std::string& expectedAnswer) {
+ int rcode = -1;
+ uint8_t buf[MAXPACKET] = {};
+
+ int res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_GT(res, 0);
+ EXPECT_EQ(expectedAnswer, toString(buf, res, ipType));
+}
+
+void expectAnswersNotValid(int fd, int expectedErrno) {
+ int rcode = -1;
+ uint8_t buf[MAXPACKET] = {};
+
+ int res = getAsyncResponse(fd, &rcode, buf, MAXPACKET);
+ EXPECT_EQ(expectedErrno, res);
+}
+
} // namespace
TEST_F(ResolverTest, Async_NormalQueryV4V6) {
@@ -1636,8 +1658,8 @@
ASSERT_TRUE(SetResolversForNetwork(servers, mDefaultSearchDomains, mDefaultParams_Binder));
dns.clearQueries();
- int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0); // Type A 1
- int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0); // Type AAAA 28
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
EXPECT_TRUE(fd1 != -1);
EXPECT_TRUE(fd2 != -1);
@@ -1654,8 +1676,8 @@
EXPECT_EQ(2U, GetNumQueries(dns, host_name));
// Re-query verify cache works
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0); // Type A 1
- fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0); // Type AAAA 28
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
EXPECT_TRUE(fd1 != -1);
EXPECT_TRUE(fd2 != -1);
@@ -1689,16 +1711,16 @@
const int queryType;
const int expectRcode;
} kTestData[] = {
- {-1, "", T_AAAA, 0},
- {-1, "as65ass46", T_AAAA, 0},
- {-1, "454564564564", T_AAAA, 0},
- {-1, "h645235", T_A, 0},
- {-1, "www.google.com", T_A, 0},
+ {-1, "", ns_t_aaaa, 0},
+ {-1, "as65ass46", ns_t_aaaa, 0},
+ {-1, "454564564564", ns_t_aaaa, 0},
+ {-1, "h645235", ns_t_a, 0},
+ {-1, "www.google.com", ns_t_a, 0},
};
for (auto& td : kTestData) {
SCOPED_TRACE(td.dname);
- td.fd = resNetworkQuery(TEST_NETID, td.dname, 1, td.queryType, 0);
+ td.fd = resNetworkQuery(TEST_NETID, td.dname, ns_c_in, td.queryType, 0);
EXPECT_TRUE(td.fd != -1);
}
@@ -1732,18 +1754,17 @@
// Wait on the condition variable to ensure that the DNS server has handled our first query.
{
std::unique_lock lk(cvMutex);
- // A 1 AAAA 28
- fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0);
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
EXPECT_TRUE(fd1 != -1);
EXPECT_EQ(std::cv_status::no_timeout, cv.wait_for(lk, std::chrono::seconds(1)));
}
dns.setResponseProbability(0.0);
- int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd2 != -1);
- int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
+ int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd3 != -1);
uint8_t buf[MAXPACKET] = {};
@@ -1760,7 +1781,7 @@
dns.setResponseProbability(1.0);
- int fd4 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
+ int fd4 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
EXPECT_TRUE(fd4 != -1);
memset(buf, 0, MAXPACKET);
@@ -1794,12 +1815,12 @@
const std::string cmd;
const int expectErr;
} kTestData[] = {
- // Less arguement
+ // Too few arguments
{"resnsend " + badMsg + '\0', -EINVAL},
// Bad netId
- {"resnsend " + badMsg + " badnetId" + '\0', -EINVAL},
+ {"resnsend badnetId 0 " + badMsg + '\0', -EINVAL},
// Bad raw data
- {"resnsend " + badMsg + " " + std::to_string(TEST_NETID) + '\0', -EILSEQ},
+ {"resnsend " + std::to_string(TEST_NETID) + " 0 " + badMsg + '\0', -EILSEQ},
};
for (unsigned int i = 0; i < std::size(kTestData); i++) {
@@ -1816,14 +1837,14 @@
// Normal query with answer buffer
// This is raw data of query "howdy.example.com" type 1 class 1
std::string query = "81sBAAABAAAAAAAABWhvd2R5B2V4YW1wbGUDY29tAAABAAE=";
- std::string cmd = "resnsend " + query + " " + std::to_string(TEST_NETID) + '\0';
+ std::string cmd = "resnsend " + std::to_string(TEST_NETID) + " 0 " + query + '\0';
ssize_t rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size()));
EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size()));
u_char smallBuf[1] = {};
int rcode;
rc = getAsyncResponse(fd, &rcode, smallBuf, 1);
- EXPECT_EQ(rc, -EMSGSIZE);
+ EXPECT_EQ(-EMSGSIZE, rc);
// Do the normal test with large buffer again
fd = dns_open_proxy();
@@ -1835,6 +1856,116 @@
EXPECT_EQ("1.2.3.4", toString(buf, rc, AF_INET));
}
+TEST_F(ResolverTest, Async_CacheFlags) {
+ const char listen_addr[] = "127.0.0.4";
+ const char listen_srv[] = "53";
+ const char host_name[] = "howdy.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.addMapping(host_name, ns_type::ns_t_aaaa, "::1.2.3.4");
+ ASSERT_TRUE(dns.startServer());
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(SetResolversForNetwork(servers, mDefaultSearchDomains, mDefaultParams_Binder));
+ dns.clearQueries();
+
+ // ANDROID_RESOLV_NO_CACHE_STORE
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd1 != -1);
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd2 != -1);
+ int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd3 != -1);
+
+ expectAnswersValid(fd3, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd2, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // No cache exists, expect 3 queries
+ EXPECT_EQ(3U, GetNumQueries(dns, host_name));
+
+ // Re-query and cache
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+
+ EXPECT_TRUE(fd1 != -1);
+
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Now we have cache, expect 4 queries
+ EXPECT_EQ(4U, GetNumQueries(dns, host_name));
+
+ // ANDROID_RESOLV_NO_CACHE_LOOKUP
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_LOOKUP);
+
+ EXPECT_TRUE(fd1 != -1);
+ EXPECT_TRUE(fd2 != -1);
+
+ expectAnswersValid(fd2, AF_INET, "1.2.3.4");
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Skip cache, expect 6 queries
+ EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+
+ // Re-query verify cache works
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_CACHE_STORE);
+ EXPECT_TRUE(fd1 != -1);
+ expectAnswersValid(fd1, AF_INET, "1.2.3.4");
+
+ // Cache hits, expect still 6 queries
+ EXPECT_EQ(6U, GetNumQueries(dns, host_name));
+}
+
+TEST_F(ResolverTest, Async_NoRetryFlag) {
+ const char listen_addr[] = "127.0.0.4";
+ const char listen_srv[] = "53";
+ const char host_name[] = "howdy.example.com.";
+ test::DNSResponder dns(listen_addr, listen_srv, 250, static_cast<ns_rcode>(-1));
+ dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+ dns.addMapping(host_name, ns_type::ns_t_aaaa, "::1.2.3.4");
+ ASSERT_TRUE(dns.startServer());
+ std::vector<std::string> servers = {listen_addr};
+ ASSERT_TRUE(SetResolversForNetwork(servers, mDefaultSearchDomains, mDefaultParams_Binder));
+ dns.clearQueries();
+
+ dns.setResponseProbability(0.0);
+
+ int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+ ANDROID_RESOLV_NO_RETRY);
+ EXPECT_TRUE(fd1 != -1);
+
+ int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa,
+ ANDROID_RESOLV_NO_RETRY);
+ EXPECT_TRUE(fd2 != -1);
+
+ // expect no response
+ expectAnswersNotValid(fd1, -ETIMEDOUT);
+ expectAnswersNotValid(fd2, -ETIMEDOUT);
+
+ // No retry case, expect 2 queries
+ EXPECT_EQ(2U, GetNumQueries(dns, host_name));
+
+ dns.clearQueries();
+
+ fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+ EXPECT_TRUE(fd1 != -1);
+
+ fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_aaaa, 0);
+ EXPECT_TRUE(fd2 != -1);
+
+ // expect no response
+ expectAnswersNotValid(fd1, -ETIMEDOUT);
+ expectAnswersNotValid(fd2, -ETIMEDOUT);
+
+ // Retry case, expect 4 queries
+ EXPECT_EQ(4U, GetNumQueries(dns, host_name));
+}
+
// This test checks that the resolver should not generate the request containing OPT RR when using
// cleartext DNS. If we query the DNS server not supporting EDNS0 and it reponds with FORMERR, we
// will fallback to no EDNS0 and try again. If the server does no response, we won't retry so that