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"};