DnsResolver: Support case-insensitive domain name hash
- Support case-insensitive hashing i.e., convert domain names
into lowercase before hashing/comparison so that there is
no additional query when domain names are same.
Test: as follows
- build, and boot
- Perform DNS queries, and ensure they are not case
sensitive
- resolv_integration_test
- resolv_unit_test
Bug: 111586865
Change-Id: I75f2ef8d09314e2f89a4764ccfd0247675340271
diff --git a/res_cache.cpp b/res_cache.cpp
index ffa2929..aa96bda 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -264,6 +264,22 @@
const uint8_t* cursor;
};
+static uint8_t res_tolower(uint8_t c) {
+ return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
+}
+
+static int res_memcasecmp(const unsigned char *s1, const unsigned char *s2, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ int ch1 = *s1++;
+ int ch2 = *s2++;
+ int d = res_tolower(ch1) - res_tolower(ch2);
+ if (d != 0) {
+ return d;
+ }
+ }
+ return 0;
+}
+
static void _dnsPacket_init(DnsPacket* packet, const uint8_t* buff, int bufflen) {
packet->base = buff;
packet->end = buff + bufflen;
@@ -451,14 +467,12 @@
const uint8_t* end = packet->end;
for (;;) {
- int c;
-
if (p >= end) { /* should not happen */
LOG(INFO) << __func__ << ": INTERNAL_ERROR: read-overflow";
break;
}
- c = *p++;
+ int c = *p++;
if (c == 0) break;
@@ -470,9 +484,12 @@
LOG(INFO) << __func__ << ": INTERNAL_ERROR: simple label read-overflow";
break;
}
+
while (c > 0) {
- hash = hash * FNV_MULT ^ *p++;
- c -= 1;
+ uint8_t ch = *p++;
+ ch = res_tolower(ch);
+ hash = hash * FNV_MULT ^ ch;
+ c--;
}
}
packet->cursor = p;
@@ -548,14 +565,12 @@
const uint8_t* end2 = pack2->end;
for (;;) {
- int c1, c2;
-
if (p1 >= end1 || p2 >= end2) {
LOG(INFO) << __func__ << ": INTERNAL_ERROR: read-overflow";
break;
}
- c1 = *p1++;
- c2 = *p2++;
+ int c1 = *p1++;
+ int c2 = *p2++;
if (c1 != c2) break;
if (c1 == 0) {
@@ -571,7 +586,7 @@
LOG(INFO) << __func__ << ": INTERNAL_ERROR: simple label read-overflow";
break;
}
- if (memcmp(p1, p2, c1) != 0) break;
+ if (res_memcasecmp(p1, p2, c1) != 0) break;
p1 += c1;
p2 += c1;
/* we rely on the bound checks at the start of the loop */
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index e347126..2bb30af 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -893,6 +893,51 @@
EXPECT_TRUE(result == nullptr);
}
+TEST_F(ResolverTest, GetAddrInfoForCaseInSensitiveDomains) {
+ test::DNSResponder dns;
+ const char* host_name = "howdy.example.com.";
+ const char* host_name2 = "HOWDY.example.com.";
+ const std::vector<DnsRecord> records = {
+ {host_name, ns_type::ns_t_a, "1.2.3.4"},
+ {host_name, ns_type::ns_t_aaaa, "::1.2.3.4"},
+ {host_name2, ns_type::ns_t_a, "1.2.3.5"},
+ {host_name2, ns_type::ns_t_aaaa, "::1.2.3.5"},
+ };
+ StartDns(dns, records);
+ ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+ ScopedAddrinfo hostname_result = safe_getaddrinfo("howdy", nullptr, nullptr);
+ EXPECT_TRUE(hostname_result != nullptr);
+ const size_t hostname1_count_after_first_query = GetNumQueries(dns, host_name);
+ EXPECT_LE(1U, hostname1_count_after_first_query);
+ // Could be A or AAAA
+ std::string hostname_result_str = ToString(hostname_result);
+ EXPECT_TRUE(hostname_result_str == "1.2.3.4" || hostname_result_str == "::1.2.3.4");
+
+ // Verify that the name is cached.
+ ScopedAddrinfo hostname2_result = safe_getaddrinfo("HOWDY", nullptr, nullptr);
+ EXPECT_TRUE(hostname2_result != nullptr);
+ const size_t hostname1_count_after_second_query = GetNumQueries(dns, host_name);
+ EXPECT_LE(1U, hostname1_count_after_second_query);
+
+ // verify that there is no change in num of queries for howdy.example.com
+ EXPECT_EQ(hostname1_count_after_first_query, hostname1_count_after_second_query);
+
+ // Number of queries for HOWDY.example.com would be >= 1 if domain names
+ // are considered case-sensitive, else number of queries should be 0.
+ const size_t hostname2_count = GetNumQueries(dns, host_name2);
+ EXPECT_EQ(0U,hostname2_count);
+ std::string hostname2_result_str = ToString(hostname2_result);
+ EXPECT_TRUE(hostname2_result_str == "1.2.3.4" || hostname2_result_str == "::1.2.3.4");
+
+ // verify that the result is still the same address even though
+ // mixed-case string is not in the DNS
+ ScopedAddrinfo result = safe_getaddrinfo("HowDY", nullptr, nullptr);
+ EXPECT_TRUE(result != nullptr);
+ std::string result_str = ToString(result);
+ EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4");
+}
+
TEST_F(ResolverTest, MultidomainResolution) {
constexpr char host_name[] = "nihao.example2.com.";
std::vector<std::string> searchDomains = {"example1.com", "example2.com", "example3.com"};