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