resolv_integration_test: Test message compression

Per RFC 1035 section 4.1.4, the domain name could be preseneted as
either:
- a sequence of labels ending in a zero octet
- a pointer
- a sequence of labels ending with a pointer

The first presentation has been verified in existing test cases.
This commit verifies the remaining presentation which uses a pointer.

Test: cd packages/modules/DnsResolver && atest
Change-Id: I705a3f5c96920cc0431aae4f8537db07e280bd1b
diff --git a/resolv_integration_test.cpp b/resolv_integration_test.cpp
index 207dd05..5b8dd4f 100644
--- a/resolv_integration_test.cpp
+++ b/resolv_integration_test.cpp
@@ -33,6 +33,7 @@
 #include <netdutils/InternetAddresses.h>
 #include <netdutils/NetworkConstants.h>  // SHA256_SIZE
 #include <netdutils/ResponseCode.h>
+#include <netdutils/Slice.h>
 #include <netdutils/SocketOption.h>
 #include <netinet/in.h>
 #include <openssl/base64.h>
@@ -89,8 +90,10 @@
 using android::net::ResolverStats;
 using android::net::metrics::DnsMetricsListener;
 using android::netdutils::enableSockopt;
+using android::netdutils::makeSlice;
 using android::netdutils::ResponseCode;
 using android::netdutils::ScopedAddrinfo;
+using android::netdutils::toHex;
 
 // TODO: move into libnetdutils?
 namespace {
@@ -3492,23 +3495,39 @@
 // TODO: Perhaps move parameterized tests to an independent file.
 enum class CallType { GETADDRINFO, GETHOSTBYNAME };
 class ResolverParameterizedTest : public ResolverTest,
-                                  public testing::WithParamInterface<CallType> {};
+                                  public testing::WithParamInterface<CallType> {
+  protected:
+    void VerifyQueryHelloExampleComV4(const test::DNSResponder& dns, const CallType calltype) {
+        if (calltype == CallType::GETADDRINFO) {
+            const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+            ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+            ASSERT_TRUE(result != nullptr);
+            EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+        } else if (calltype == CallType::GETHOSTBYNAME) {
+            const hostent* result = gethostbyname("hello");
+            ASSERT_TRUE(result != nullptr);
+            ASSERT_EQ(4, result->h_length);
+            ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+            EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+            EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+        } else {
+            FAIL() << "Unsupported call type: " << static_cast<uint32_t>(calltype);
+        }
+        EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
+    }
+};
 
-INSTANTIATE_TEST_SUITE_P(TestQueryCall, ResolverParameterizedTest,
+INSTANTIATE_TEST_SUITE_P(QueryCallTest, ResolverParameterizedTest,
                          testing::Values(CallType::GETADDRINFO, CallType::GETHOSTBYNAME),
                          [](const testing::TestParamInfo<CallType>& info) {
-                             std::string name;
                              switch (info.param) {
                                  case CallType::GETADDRINFO:
-                                     name = "GetAddrInfo";
-                                     break;
+                                     return "GetAddrInfo";
                                  case CallType::GETHOSTBYNAME:
-                                     name = "GetHostByName";
-                                     break;
+                                     return "GetHostByName";
                                  default:
-                                     name = "InvalidParameter";  // Should not happen.
+                                     return "InvalidParameter";  // Should not happen.
                              }
-                             return name;
                          });
 
 TEST_P(ResolverParameterizedTest, AuthoritySectionAndAdditionalSection) {
@@ -3549,7 +3568,7 @@
             .rclass = ns_c_in,
             .ttl = 0,  // no cache
     };
-    EXPECT_TRUE(test::DNSResponder::fillRdata("1.2.3.4", recordAnswer));
+    EXPECT_TRUE(test::DNSResponder::fillRdata(kHelloExampleComAddrV4, recordAnswer));
     header.answers.push_back(std::move(recordAnswer));
 
     // Authority section.
@@ -3580,21 +3599,131 @@
     dns.clearQueries();
 
     // Expect that get the address and the resolver doesn't crash.
-    if (calltype == CallType::GETADDRINFO) {
-        const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
-        ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
-        EXPECT_TRUE(result != nullptr);
-        EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
-        EXPECT_EQ("1.2.3.4", ToString(result));
-    } else if (calltype == CallType::GETHOSTBYNAME) {
-        const hostent* result = gethostbyname("hello");
-        EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
-        ASSERT_FALSE(result == nullptr);
-        ASSERT_EQ(4, result->h_length);
-        ASSERT_FALSE(result->h_addr_list[0] == nullptr);
-        EXPECT_EQ("1.2.3.4", ToString(result));
-        EXPECT_TRUE(result->h_addr_list[1] == nullptr);
-    } else {
-        FAIL() << "Unsupported call type: " << static_cast<uint32_t>(calltype);
+    VerifyQueryHelloExampleComV4(dns, calltype);
+}
+
+TEST_P(ResolverParameterizedTest, MessageCompression) {
+    const auto& calltype = GetParam();
+
+    // The response with compressed domain name by a pointer. See RFC 1035 section 4.1.4.
+    //
+    // Ignoring the other fields of the message, the domain name of question section and answer
+    // section are presented as:
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 12 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 14 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 16 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 18 |           7           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 20 |           x           |           a           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 22 |           m           |           p           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 24 |           l           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 26 |           3           |           c           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 28 |           o           |           m           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 30 |           0           |          ...          |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 35 | 1  1|                12                       |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    const std::vector<uint8_t> kResponseAPointer = {
+            /* Header */
+            0x00, 0x00, /* Transaction ID: 0x0000 */
+            0x81, 0x80, /* Flags: qr rd ra */
+            0x00, 0x01, /* Questions: 1 */
+            0x00, 0x01, /* Answer RRs: 1 */
+            0x00, 0x00, /* Authority RRs: 0 */
+            0x00, 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            /* Answers */
+            0xc0, 0x0c,             /* Name: hello.example.com (a pointer) */
+            0x00, 0x01,             /* Type: A */
+            0x00, 0x01,             /* Class: IN */
+            0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+            0x00, 0x04,             /* Data length: 4 */
+            0x01, 0x02, 0x03, 0x04  /* Address: 1.2.3.4 */
+    };
+
+    // The response with compressed domain name by a sequence of labels ending with a pointer. See
+    // RFC 1035 section 4.1.4.
+    //
+    // Ignoring the other fields of the message, the domain name of question section and answer
+    // section are presented as:
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 12 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 14 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 16 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 18 |           7           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 20 |           x           |           a           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 22 |           m           |           p           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 24 |           l           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 26 |           3           |           c           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 28 |           o           |           m           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 30 |           0           |          ...          |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 35 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 37 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 39 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 41 | 1  1|                18                       |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    const std::vector<uint8_t> kResponseLabelEndingWithAPointer = {
+            /* Header */
+            0x00, 0x00, /* Transaction ID: 0x0000 */
+            0x81, 0x80, /* Flags: qr rd ra */
+            0x00, 0x01, /* Questions: 1 */
+            0x00, 0x01, /* Answer RRs: 1 */
+            0x00, 0x00, /* Authority RRs: 0 */
+            0x00, 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            /* Answers */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xc0,
+            0x12,                   /* Name: hello.example.com (a label ending with a pointer) */
+            0x00, 0x01,             /* Type: A */
+            0x00, 0x01,             /* Class: IN */
+            0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+            0x00, 0x04,             /* Data length: 4 */
+            0x01, 0x02, 0x03, 0x04  /* Address: 1.2.3.4 */
+    };
+
+    for (const auto& response : {kResponseAPointer, kResponseLabelEndingWithAPointer}) {
+        SCOPED_TRACE(StringPrintf("Hex dump: %s", toHex(makeSlice(response)).c_str()));
+
+        test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+        dns.addMappingBinaryPacket(kHelloExampleComQueryV4, response);
+        StartDns(dns, {});
+        ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+        // Expect no cache because the TTL of testing responses are 0.
+        VerifyQueryHelloExampleComV4(dns, calltype);
     }
 }
\ No newline at end of file