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/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