1953 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1953 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2018 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #define LOG_TAG "resolv"
 | |
| 
 | |
| #include <aidl/android/net/IDnsResolver.h>
 | |
| #include <android-base/format.h>
 | |
| #include <android-base/logging.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <gmock/gmock-matchers.h>
 | |
| #include <gtest/gtest.h>
 | |
| #include <netdb.h>
 | |
| #include <netdutils/InternetAddresses.h>
 | |
| #include <resolv_stats_test_utils.h>
 | |
| 
 | |
| #include "dns_responder.h"
 | |
| #include "getaddrinfo.h"
 | |
| #include "gethnamaddr.h"
 | |
| #include "resolv_cache.h"
 | |
| #include "stats.pb.h"
 | |
| #include "tests/resolv_test_base.h"
 | |
| #include "tests/resolv_test_utils.h"
 | |
| 
 | |
| #define NAME(variable) #variable
 | |
| 
 | |
| namespace android {
 | |
| namespace net {
 | |
| 
 | |
| using aidl::android::net::IDnsResolver;
 | |
| using android::net::NetworkDnsEventReported;
 | |
| using android::netdutils::ScopedAddrinfo;
 | |
| 
 | |
| // The buffer size of resolv_gethostbyname().
 | |
| constexpr unsigned int MAXPACKET = 8 * 1024;
 | |
| 
 | |
| // Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates
 | |
| // that any type or protocol can be returned by getaddrinfo().
 | |
| constexpr unsigned int ANY = 0;
 | |
| 
 | |
| class TestBase : public ResolvTestBase {
 | |
|   protected:
 | |
|     struct DnsMessage {
 | |
|         std::string host_name;   // host name
 | |
|         ns_type type;            // record type
 | |
|         test::DNSHeader header;  // dns header
 | |
|     };
 | |
| 
 | |
|     void SetUp() override {
 | |
|         // Create cache for test
 | |
|         resolv_create_cache_for_net(TEST_NETID);
 | |
|         SetMdnsRoute();
 | |
|     }
 | |
|     void TearDown() override {
 | |
|         // Delete cache for test
 | |
|         resolv_delete_cache_for_net(TEST_NETID);
 | |
|         RemoveMdnsRoute();
 | |
|     }
 | |
| 
 | |
|     test::DNSRecord MakeAnswerRecord(const std::string& name, unsigned rclass, unsigned rtype,
 | |
|                                      const std::string& rdata, unsigned ttl = kAnswerRecordTtlSec) {
 | |
|         test::DNSRecord record{
 | |
|                 .name = {.name = name},
 | |
|                 .rtype = rtype,
 | |
|                 .rclass = rclass,
 | |
|                 .ttl = ttl,
 | |
|         };
 | |
|         EXPECT_TRUE(test::DNSResponder::fillRdata(rdata, record));
 | |
|         return record;
 | |
|     }
 | |
| 
 | |
|     DnsMessage MakeDnsMessage(const std::string& qname, ns_type qtype,
 | |
|                               const std::vector<std::string>& rdata) {
 | |
|         const unsigned qclass = ns_c_in;
 | |
|         // Build a DNSHeader in the following format.
 | |
|         // Question
 | |
|         //   <qname>                IN      <qtype>
 | |
|         // Answer
 | |
|         //   <qname>                IN      <qtype>     <rdata[0]>
 | |
|         //   ..
 | |
|         //   <qname>                IN      <qtype>     <rdata[n]>
 | |
|         //
 | |
|         // Example:
 | |
|         // Question
 | |
|         //   hello.example.com.     IN      A
 | |
|         // Answer
 | |
|         //   hello.example.com.     IN      A           1.2.3.1
 | |
|         //   ..
 | |
|         //   hello.example.com.     IN      A           1.2.3.9
 | |
|         test::DNSHeader header(kDefaultDnsHeader);
 | |
| 
 | |
|         // Question section
 | |
|         test::DNSQuestion question{
 | |
|                 .qname = {.name = qname},
 | |
|                 .qtype = qtype,
 | |
|                 .qclass = qclass,
 | |
|         };
 | |
|         header.questions.push_back(std::move(question));
 | |
| 
 | |
|         // Answer section
 | |
|         for (const auto& r : rdata) {
 | |
|             test::DNSRecord record = MakeAnswerRecord(qname, qclass, qtype, r);
 | |
|             header.answers.push_back(std::move(record));
 | |
|         }
 | |
|         // TODO: Perhaps add support for authority RRs and additional RRs.
 | |
|         return {qname, qtype, header};
 | |
|     }
 | |
| 
 | |
|     void StartDns(test::DNSResponder& dns, const std::vector<DnsMessage>& messages) {
 | |
|         for (const auto& m : messages) {
 | |
|             dns.addMappingDnsHeader(m.host_name, m.type, m.header);
 | |
|         }
 | |
|         ASSERT_TRUE(dns.startServer());
 | |
|         dns.clearQueries();
 | |
|     }
 | |
| 
 | |
|     int SetResolvers() { return SetResolvers(servers); }
 | |
| 
 | |
|     int SetResolvers(std::vector<std::string> servers) {
 | |
|         return resolv_set_nameservers(TEST_NETID, servers, domains, params, std::nullopt);
 | |
|     }
 | |
| 
 | |
|     const android_net_context mNetcontext = {
 | |
|             .app_netid = TEST_NETID,
 | |
|             .app_mark = MARK_UNSET,
 | |
|             .dns_netid = TEST_NETID,
 | |
|             .dns_mark = MARK_UNSET,
 | |
|             .uid = NET_CONTEXT_INVALID_UID,
 | |
|     };
 | |
|     const std::vector<std::string> servers = {test::kDefaultListenAddr};
 | |
|     const std::vector<std::string> domains = {"example.com"};
 | |
|     const res_params params = {
 | |
|             .sample_validity = 300,
 | |
|             .success_threshold = 25,
 | |
|             .min_samples = 8,
 | |
|             .max_samples = 8,
 | |
|             .base_timeout_msec = 1000,
 | |
|             .retry_count = 2,
 | |
|     };
 | |
| };
 | |
| 
 | |
| class ResolvGetAddrInfoTest : public TestBase {};
 | |
| class GetHostByNameForNetContextTest : public TestBase {};
 | |
| class ResolvCommonFunctionTest : public TestBase {};
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, InvalidParameters) {
 | |
|     // Both null "netcontext" and null "res" of resolv_getaddrinfo() are not tested
 | |
|     // here because they are checked by assert() without returning any error number.
 | |
| 
 | |
|     // Invalid hostname and servname.
 | |
|     // Both hostname and servname are null pointers. Expect error number EAI_NONAME.
 | |
|     {
 | |
|         addrinfo* result = nullptr;
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo(nullptr /*hostname*/, nullptr /*servname*/, nullptr /*hints*/,
 | |
|                                     &mNetcontext, &result, &event);
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(EAI_NONAME, rv);
 | |
|     }
 | |
| 
 | |
|     // Invalid hints.
 | |
|     // These place holders are used to test function call with unrequired parameters.
 | |
|     // The content is not important because function call returns error directly if
 | |
|     // there have any unrequired parameter.
 | |
|     char placeholder_cname[] = "invalid_cname";
 | |
|     sockaddr placeholder_addr = {};
 | |
|     addrinfo placeholder_next = {};
 | |
|     static const struct TestConfig {
 | |
|         int ai_flags;
 | |
|         socklen_t ai_addrlen;
 | |
|         char* ai_canonname;
 | |
|         sockaddr* ai_addr;
 | |
|         addrinfo* ai_next;
 | |
| 
 | |
|         int expected_eai_error;
 | |
| 
 | |
|         std::string asParameters() const {
 | |
|             return fmt::format("{:#x}/{}/{}/{}/{}", ai_flags, ai_addrlen,
 | |
|                                ai_canonname ? ai_canonname : "(null)", (void*)ai_addr,
 | |
|                                (void*)ai_next);
 | |
|         }
 | |
|     } testConfigs[]{
 | |
|             {0, sizeof(in_addr) /*bad*/, nullptr, nullptr, nullptr, EAI_BADHINTS},
 | |
|             {0, 0, placeholder_cname /*bad*/, nullptr, nullptr, EAI_BADHINTS},
 | |
|             {0, 0, nullptr, &placeholder_addr /*bad*/, nullptr, EAI_BADHINTS},
 | |
|             {0, 0, nullptr, nullptr, &placeholder_next /*bad*/, EAI_BADHINTS},
 | |
|             {AI_ALL /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
 | |
|             {AI_V4MAPPED_CFG /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
 | |
|             {AI_V4MAPPED /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
 | |
|             {AI_DEFAULT /*bad*/, 0, nullptr, nullptr, nullptr, EAI_BADFLAGS},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(config.asParameters());
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         // In current test configuration set, ai_family, ai_protocol and ai_socktype are not
 | |
|         // checked because other fields cause hints error check failed first.
 | |
|         const addrinfo hints = {
 | |
|                 .ai_flags = config.ai_flags,
 | |
|                 .ai_family = AF_UNSPEC,
 | |
|                 .ai_socktype = ANY,
 | |
|                 .ai_protocol = ANY,
 | |
|                 .ai_addrlen = config.ai_addrlen,
 | |
|                 .ai_canonname = config.ai_canonname,
 | |
|                 .ai_addr = config.ai_addr,
 | |
|                 .ai_next = config.ai_next,
 | |
|         };
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("localhost", nullptr /*servname*/, &hints, &mNetcontext,
 | |
|                                     &result, &event);
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(config.expected_eai_error, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, InvalidParameters_Family) {
 | |
|     for (int family = 0; family < AF_MAX; ++family) {
 | |
|         if (family == AF_UNSPEC || family == AF_INET || family == AF_INET6) {
 | |
|             continue;  // skip supported family
 | |
|         }
 | |
|         SCOPED_TRACE(fmt::format("family: {}", family));
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {
 | |
|                 .ai_family = family,  // unsupported family
 | |
|         };
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("localhost", nullptr /*servname*/, &hints, &mNetcontext,
 | |
|                                     &result, &event);
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(EAI_FAMILY, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, InvalidParameters_SocketType) {
 | |
|     for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|         for (int protocol = 0; protocol < IPPROTO_MAX; ++protocol) {
 | |
|             // Socket types which are not in explore_options.
 | |
|             for (const auto& socktype : {SOCK_RDM, SOCK_SEQPACKET, SOCK_DCCP, SOCK_PACKET}) {
 | |
|                 const addrinfo hints = {
 | |
|                         .ai_family = family,
 | |
|                         .ai_socktype = socktype,
 | |
|                         .ai_protocol = protocol,
 | |
|                 };
 | |
|                 for (const char* service : {static_cast<const char*>(nullptr),  // service is null
 | |
|                                             "80",
 | |
|                                             "",  // empty service name
 | |
|                                             "ftp",
 | |
|                                             "65536",  // out of valid port range from 0 to 65535
 | |
|                                             "invalid"}) {
 | |
|                     SCOPED_TRACE(fmt::format("family: {}, socktype: {}, protocol: {}, service: {}",
 | |
|                                              family, socktype, protocol,
 | |
|                                              service ? service : "service is nullptr"));
 | |
|                     addrinfo* result = nullptr;
 | |
|                     NetworkDnsEventReported event;
 | |
|                     int rv = resolv_getaddrinfo("localhost", service, &hints, &mNetcontext, &result,
 | |
|                                                 &event);
 | |
|                     ScopedAddrinfo result_cleanup(result);
 | |
|                     EXPECT_EQ(EAI_SOCKTYPE, rv);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, InvalidParameters_MeaningfulSocktypeAndProtocolCombination) {
 | |
|     static const int families[] = {PF_INET, PF_INET6, PF_UNSPEC};
 | |
|     // Skip to test socket type SOCK_RAW in meaningful combination (explore_options[]) of
 | |
|     // system\netd\resolv\getaddrinfo.cpp. In explore_options[], the socket type SOCK_RAW always
 | |
|     // comes with protocol ANY which causes skipping meaningful socktype/protocol combination
 | |
|     // check. So it nerver returns error number EAI_BADHINTS which we want to test in this test
 | |
|     // case.
 | |
|     static const int socktypes[] = {SOCK_STREAM, SOCK_DGRAM};
 | |
| 
 | |
|     // If both socktype/protocol are specified, check non-meaningful combination returns
 | |
|     // expected error number EAI_BADHINTS. See meaningful combination in explore_options[] of
 | |
|     // system\netd\resolv\getaddrinfo.cpp.
 | |
|     for (const auto& family : families) {
 | |
|         for (const auto& socktype : socktypes) {
 | |
|             for (int protocol = 0; protocol < IPPROTO_MAX; ++protocol) {
 | |
|                 SCOPED_TRACE(fmt::format("family: {}, socktype: {}, protocol: {}", family, socktype,
 | |
|                                          protocol));
 | |
| 
 | |
|                 // Both socktype/protocol need to be specified.
 | |
|                 if (!socktype || !protocol) continue;
 | |
| 
 | |
|                 // Skip meaningful combination in explore_options[] of
 | |
|                 // system\netd\resolv\getaddrinfo.cpp.
 | |
|                 if ((family == AF_INET6 && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
 | |
|                     (family == AF_INET6 && socktype == SOCK_STREAM && protocol == IPPROTO_TCP) ||
 | |
|                     (family == AF_INET && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
 | |
|                     (family == AF_INET && socktype == SOCK_STREAM && protocol == IPPROTO_TCP) ||
 | |
|                     (family == AF_UNSPEC && socktype == SOCK_DGRAM && protocol == IPPROTO_UDP) ||
 | |
|                     (family == AF_UNSPEC && socktype == SOCK_STREAM && protocol == IPPROTO_TCP)) {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 addrinfo* result = nullptr;
 | |
|                 const addrinfo hints = {
 | |
|                         .ai_family = family,
 | |
|                         .ai_socktype = socktype,
 | |
|                         .ai_protocol = protocol,
 | |
|                 };
 | |
|                 NetworkDnsEventReported event;
 | |
|                 int rv = resolv_getaddrinfo("localhost", nullptr /*servname*/, &hints, &mNetcontext,
 | |
|                                             &result, &event);
 | |
|                 ScopedAddrinfo result_cleanup(result);
 | |
|                 EXPECT_EQ(EAI_BADHINTS, rv);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // The test configs are used for verifying the error path of get_port().
 | |
| // Note that the EAI_SOCKTYPE verification are moved to an independent
 | |
| // test case because validateHints() verify invalid socket type early now.
 | |
| // See also InvalidParameters_SocketType.
 | |
| TEST_F(ResolvGetAddrInfoTest, InvalidParameters_PortNameAndNumber) {
 | |
|     constexpr char http_portno[] = "80";
 | |
|     constexpr char invalid_portno[] = "65536";  // out of valid port range from 0 to 65535
 | |
|     constexpr char http_portname[] = "http";
 | |
|     constexpr char invalid_portname[] = "invalid_portname";
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_flags;
 | |
|         int ai_family;
 | |
|         int ai_socktype;
 | |
|         const char* servname;
 | |
| 
 | |
|         int expected_eai_error;
 | |
| 
 | |
|         std::string asParameters() const {
 | |
|             return fmt::format("{:#x}/{}/{}/{}", ai_flags, ai_family, ai_socktype,
 | |
|                                servname ? servname : "(null)");
 | |
|         }
 | |
|     } testConfigs[]{
 | |
|             {0, AF_INET, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
 | |
|             {0, AF_INET6, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, SOCK_RAW /*bad*/, http_portno, EAI_SERVICE},
 | |
|             {0, AF_INET, ANY, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, ANY, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, ANY, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, SOCK_STREAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, SOCK_DGRAM, invalid_portno /*bad*/, EAI_SERVICE},
 | |
|             {AI_NUMERICSERV, AF_INET, ANY, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_INET, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_INET, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_INET6, ANY, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_INET6, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_INET6, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_UNSPEC, ANY, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_UNSPEC, SOCK_STREAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {AI_NUMERICSERV, AF_UNSPEC, SOCK_DGRAM, http_portname /*bad*/, EAI_NONAME},
 | |
|             {0, AF_INET, ANY, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, ANY, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_INET6, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, ANY, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, SOCK_STREAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|             {0, AF_UNSPEC, SOCK_DGRAM, invalid_portname /*bad*/, EAI_SERVICE},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         const std::string testParameters = config.asParameters();
 | |
|         SCOPED_TRACE(testParameters);
 | |
| 
 | |
|         const addrinfo hints = {
 | |
|                 .ai_flags = config.ai_flags,
 | |
|                 .ai_family = config.ai_family,
 | |
|                 .ai_socktype = config.ai_socktype,
 | |
|         };
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("localhost", config.servname, &hints, &mNetcontext, &result,
 | |
|                                     &event);
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(config.expected_eai_error, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname_NoData) {
 | |
|     constexpr char v4_host_name[] = "v4only.example.com.";
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     // Want AAAA answer but DNS server has A answer only.
 | |
|     addrinfo* result = nullptr;
 | |
|     const addrinfo hints = {.ai_family = AF_INET6};
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_getaddrinfo("v4only", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|     EXPECT_THAT(event, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event_ipv6)));
 | |
|     ScopedAddrinfo result_cleanup(result);
 | |
|     EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
 | |
|     EXPECT_EQ(nullptr, result);
 | |
|     EXPECT_EQ(EAI_NODATA, rv);
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname) {
 | |
|     constexpr char host_name[] = "sawadee.example.com.";
 | |
|     constexpr char v4addr[] = "1.2.3.4";
 | |
|     constexpr char v6addr[] = "::1.2.3.4";
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     constexpr char event_ipv4[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 2,
 | |
|                  ip_version: 0,
 | |
|                  protocol: 0,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 2,
 | |
|                  ip_version: 0,
 | |
|                  protocol: 0,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
 | |
|     dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_family;
 | |
|         const std::string expected_addr;
 | |
|         const std::string expected_event;
 | |
|     } testConfigs[]{
 | |
|             {AF_INET, v4addr, event_ipv4},
 | |
|             {AF_INET6, v6addr, event_ipv6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", config.ai_family));
 | |
|         dns.clearQueries();
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = config.ai_family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("sawadee", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         EXPECT_THAT(event,
 | |
|                     NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(0, rv);
 | |
|         EXPECT_TRUE(result != nullptr);
 | |
|         EXPECT_EQ(1U, GetNumQueries(dns, host_name));
 | |
|         EXPECT_EQ(config.expected_addr, ToString(result));
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, IllegalHostname) {
 | |
|     test::DNSResponder dns;
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp.
 | |
|     static constexpr char const* illegalHostnames[] = {
 | |
|             kBadCharAfterPeriodHost,
 | |
|             kBadCharBeforePeriodHost,
 | |
|             kBadCharAtTheEndHost,
 | |
|             kBadCharInTheMiddleOfLabelHost,
 | |
|     };
 | |
| 
 | |
|     for (const auto& hostname : illegalHostnames) {
 | |
|         // Expect to get no address because hostname format is illegal.
 | |
|         //
 | |
|         // Ex:
 | |
|         // ANSWER SECTION:
 | |
|         // a.ex^ample.com.      IN  A       1.2.3.3
 | |
|         // a.ex^ample.com.      IN  AAAA    2001:db8::42
 | |
|         //
 | |
|         // In this example, querying "a.ex^ample.com" should get no address because
 | |
|         // "a.ex^ample.com" has an illegal char '^' in the middle of label.
 | |
|         dns.addMapping(hostname, ns_type::ns_t_a, "1.2.3.3");
 | |
|         dns.addMapping(hostname, ns_type::ns_t_aaaa, "2001:db8::42");
 | |
| 
 | |
|         for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|             SCOPED_TRACE(fmt::format("family: {}, config.name: {}", family, hostname));
 | |
| 
 | |
|             addrinfo* res = nullptr;
 | |
|             const addrinfo hints = {.ai_family = family};
 | |
|             NetworkDnsEventReported event;
 | |
|             int rv = resolv_getaddrinfo(hostname, nullptr, &hints, &mNetcontext, &res, &event);
 | |
|             ScopedAddrinfo result(res);
 | |
|             EXPECT_EQ(nullptr, result);
 | |
|             EXPECT_EQ(EAI_FAIL, rv);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, ServerResponseError) {
 | |
|     constexpr char host_name[] = "hello.example.com.";
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         ns_rcode rcode;
 | |
|         int expected_eai_error;
 | |
| 
 | |
|         // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
 | |
|         // which means no error.
 | |
|     } testConfigs[]{
 | |
|             // clang-format off
 | |
|             {ns_rcode::ns_r_formerr,  EAI_FAIL},
 | |
|             {ns_rcode::ns_r_servfail, EAI_AGAIN},
 | |
|             {ns_rcode::ns_r_nxdomain, EAI_NODATA},
 | |
|             {ns_rcode::ns_r_notimpl,  EAI_FAIL},
 | |
|             {ns_rcode::ns_r_refused,  EAI_FAIL},
 | |
|             // clang-format on
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("rcode: {}", config.rcode));
 | |
| 
 | |
|         test::DNSResponder dns(config.rcode);
 | |
|         dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
 | |
|         dns.setResponseProbability(0.0);  // always ignore requests and response preset rcode
 | |
|         ASSERT_TRUE(dns.startServer());
 | |
|         ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = AF_UNSPEC};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo(host_name, nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         EXPECT_EQ(config.expected_eai_error, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TODO: Add private DNS server timeout test.
 | |
| TEST_F(ResolvGetAddrInfoTest, ServerTimeout) {
 | |
|     constexpr char host_name[] = "hello.example.com.";
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     // Expected_event is 16 DNS queries and only "type" and "retry_times" fields are changed.
 | |
|     // 2(T_AAAA + T_A) * 2(w/ retry) * 2(query w/ and w/o domain) * 2(SOCK_DGRAM and SOCK_STREAM)
 | |
|     constexpr char expected_event[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 }
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 }
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 }
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 }
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 255,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 1,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 110,
 | |
|                 },
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
|     test::DNSResponder dns(static_cast<ns_rcode>(-1) /*no response*/);
 | |
|     dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
 | |
|     dns.setResponseProbability(0.0);  // always ignore requests and don't respond
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     addrinfo* result = nullptr;
 | |
|     const addrinfo hints = {.ai_family = AF_UNSPEC};
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|     EXPECT_THAT(event, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(expected_event)));
 | |
|     EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, MdnsAlphabeticalHostname) {
 | |
|     constexpr char v4addr[] = "127.0.0.3";
 | |
|     constexpr char v6addr[] = "::127.0.0.3";
 | |
|     constexpr char host_name[] = "hello.local.";
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
| 
 | |
|     constexpr char event_ipv4[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 2,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv4v6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 2,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService);
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService);
 | |
|     mdnsv4.addMapping(host_name, ns_type::ns_t_a, v4addr);
 | |
|     mdnsv6.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_family;
 | |
|         const std::vector<std::string> expected_addr;
 | |
|         const std::string expected_event;
 | |
|     } testConfigs[]{
 | |
|             {AF_UNSPEC, {v4addr, v6addr}, event_ipv4v6},
 | |
|             {AF_INET, {v4addr}, event_ipv4},
 | |
|             {AF_INET6, {v6addr}, event_ipv6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", config.ai_family));
 | |
|         mdnsv4.clearQueries();
 | |
|         mdnsv6.clearQueries();
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = config.ai_family, .ai_socktype = SOCK_DGRAM};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello.local", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         EXPECT_THAT(event,
 | |
|                     NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
| 
 | |
|         if (config.ai_family == AF_UNSPEC) {
 | |
|             EXPECT_EQ(0, rv);
 | |
|             EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name));
 | |
|             EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name));
 | |
|             const std::vector<std::string> result_strs = ToStrings(result);
 | |
|             EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.expected_addr));
 | |
|         } else if (config.ai_family == AF_INET) {
 | |
|             EXPECT_EQ(0, rv);
 | |
|             EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name));
 | |
|             const std::vector<std::string> result_strs = ToStrings(result);
 | |
|             EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.expected_addr));
 | |
|         } else if (config.ai_family == AF_INET6) {
 | |
|             EXPECT_EQ(0, rv);
 | |
|             EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name));
 | |
|             const std::vector<std::string> result_strs = ToStrings(result);
 | |
|             EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.expected_addr));
 | |
|         }
 | |
|         resolv_flush_cache_for_net(TEST_NETID);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, MdnsIllegalHostname) {
 | |
|     constexpr char v6addr[] = "::127.0.0.3";
 | |
|     constexpr char v4addr[] = "127.0.0.3";
 | |
| 
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService);
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService);
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     mdnsv4.clearQueries();
 | |
|     mdnsv6.clearQueries();
 | |
| 
 | |
|     constexpr char illegalHostname[] = "hello^.local.";
 | |
|     // Expect to get no address because hostname format is illegal.
 | |
|     //
 | |
|     // Ex:
 | |
|     // ANSWER SECTION:
 | |
|     // hello^.local.      IN  A       127.0.0.3
 | |
|     // hello^.local.      IN  AAAA    ::127.0.0.3
 | |
|     //
 | |
|     // In this example, querying "hello^.local" should get no address because
 | |
|     // "hello^.local" has an illegal char '^' in the middle of label.
 | |
|     mdnsv4.addMapping(illegalHostname, ns_type::ns_t_a, v4addr);
 | |
|     mdnsv6.addMapping(illegalHostname, ns_type::ns_t_aaaa, v6addr);
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}, illegalHostname: {}", family, illegalHostname));
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello^.local", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(nullptr, result);
 | |
|         EXPECT_EQ(EAI_FAIL, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, MdnsResponderTimeout) {
 | |
|     constexpr char host_name[] = "hello.local.";
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService,
 | |
|                               static_cast<ns_rcode>(-1));
 | |
|     mdnsv4.setResponseProbability(0.0);  // always ignore requests and don't respond
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1));
 | |
|     mdnsv6.setResponseProbability(0.0);
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     test::DNSResponder dns("127.0.0.3", test::kDefaultListenService, static_cast<ns_rcode>(-1));
 | |
|     dns.setResponseProbability(0.0);
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}, host_name: {}", family, host_name));
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello.local", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, CnamesNoIpAddress) {
 | |
|     constexpr char ACNAME[] = "acname";  // expect a cname in answer
 | |
|     constexpr char CNAMES[] = "cnames";  // expect cname chain in answer
 | |
| 
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com.");
 | |
|     dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com.");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         const char* name;
 | |
|         int family;
 | |
|     } testConfigs[]{
 | |
|             // clang-format off
 | |
|             {ACNAME, AF_INET},
 | |
|             {ACNAME, AF_INET6},
 | |
|             {ACNAME, AF_UNSPEC},
 | |
|             {CNAMES, AF_INET},
 | |
|             {CNAMES, AF_INET6},
 | |
|             {CNAMES, AF_UNSPEC},
 | |
|             // clang-format on
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("config.family: {}, config.name: {}", config.family, config.name));
 | |
| 
 | |
|         addrinfo* res = nullptr;
 | |
|         const addrinfo hints = {.ai_family = config.family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo(config.name, nullptr, &hints, &mNetcontext, &res, &event);
 | |
|         ScopedAddrinfo result(res);
 | |
|         EXPECT_EQ(nullptr, result);
 | |
|         EXPECT_EQ(EAI_FAIL, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, CnamesBrokenChainByIllegalCname) {
 | |
|     test::DNSResponder dns;
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         const char* name;
 | |
|         const char* cname;
 | |
|         std::string asHostName() const { return fmt::format("{}.example.com.", name); }
 | |
| 
 | |
|         // Illegal cname is verified by res_hnok() in system/netd/resolv/res_comp.cpp.
 | |
|     } testConfigs[]{
 | |
|             // clang-format off
 | |
|             {NAME(kBadCharAfterPeriodHost),        kBadCharAfterPeriodHost},
 | |
|             {NAME(kBadCharBeforePeriodHost),       kBadCharBeforePeriodHost},
 | |
|             {NAME(kBadCharAtTheEndHost),           kBadCharAtTheEndHost},
 | |
|             {NAME(kBadCharInTheMiddleOfLabelHost), kBadCharInTheMiddleOfLabelHost},
 | |
|             // clang-format on
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         const std::string testHostName = config.asHostName();
 | |
| 
 | |
|         // Expect to get no address because the cname chain is broken by an illegal cname format.
 | |
|         //
 | |
|         // Ex:
 | |
|         // ANSWER SECTION:
 | |
|         // hello.example.com.   IN  CNAME   a.ex^ample.com.
 | |
|         // a.ex^ample.com.      IN  A       1.2.3.3
 | |
|         // a.ex^ample.com.      IN  AAAA    2001:db8::42
 | |
|         //
 | |
|         // In this example, querying hello.example.com should get no address because
 | |
|         // "a.ex^ample.com" has an illegal char '^' in the middle of label.
 | |
|         dns.addMapping(testHostName.c_str(), ns_type::ns_t_cname, config.cname);
 | |
|         dns.addMapping(config.cname, ns_type::ns_t_a, "1.2.3.3");
 | |
|         dns.addMapping(config.cname, ns_type::ns_t_aaaa, "2001:db8::42");
 | |
| 
 | |
|         for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|             SCOPED_TRACE(fmt::format("family: {}, testHostName: {}", family, testHostName));
 | |
| 
 | |
|             addrinfo* res = nullptr;
 | |
|             const addrinfo hints = {.ai_family = family};
 | |
|             NetworkDnsEventReported event;
 | |
|             int rv = resolv_getaddrinfo(config.name, nullptr, &hints, &mNetcontext, &res, &event);
 | |
|             ScopedAddrinfo result(res);
 | |
|             EXPECT_EQ(nullptr, result);
 | |
|             EXPECT_EQ(EAI_FAIL, rv);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, CnamesInfiniteLoop) {
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com.");
 | |
|     dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com.");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", family));
 | |
| 
 | |
|         addrinfo* res = nullptr;
 | |
|         const addrinfo hints = {.ai_family = family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &res, &event);
 | |
|         ScopedAddrinfo result(res);
 | |
|         EXPECT_EQ(nullptr, result);
 | |
|         EXPECT_EQ(EAI_FAIL, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, MultiAnswerSections) {
 | |
|     test::DNSResponder dns(test::DNSResponder::MappingType::DNS_HEADER);
 | |
|     // Answer section for query type {A, AAAA}
 | |
|     // Type A:
 | |
|     //   hello.example.com.   IN    A       1.2.3.1
 | |
|     //   hello.example.com.   IN    A       1.2.3.2
 | |
|     // Type AAAA:
 | |
|     //   hello.example.com.   IN    AAAA    2001:db8::41
 | |
|     //   hello.example.com.   IN    AAAA    2001:db8::42
 | |
|     StartDns(dns, {MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_a, {"1.2.3.1", "1.2.3.2"}),
 | |
|                    MakeDnsMessage(kHelloExampleCom, ns_type::ns_t_aaaa,
 | |
|                                   {"2001:db8::41", "2001:db8::42"})});
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6, AF_UNSPEC}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", family));
 | |
| 
 | |
|         addrinfo* res = nullptr;
 | |
|         // If the socket type is not specified, every address will appear twice, once for
 | |
|         // SOCK_STREAM and one for SOCK_DGRAM. Just pick one because the addresses for
 | |
|         // the second query of different socket type are responded by the cache.
 | |
|         const addrinfo hints = {.ai_family = family, .ai_socktype = SOCK_STREAM};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &res, &event);
 | |
|         ScopedAddrinfo result(res);
 | |
|         ASSERT_NE(nullptr, result);
 | |
|         ASSERT_EQ(0, rv);
 | |
| 
 | |
|         const std::vector<std::string> result_strs = ToStrings(result);
 | |
|         if (family == AF_INET) {
 | |
|             EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
 | |
|             EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray({"1.2.3.1", "1.2.3.2"}));
 | |
|         } else if (family == AF_INET6) {
 | |
|             EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
 | |
|             EXPECT_THAT(result_strs,
 | |
|                         testing::UnorderedElementsAreArray({"2001:db8::41", "2001:db8::42"}));
 | |
|         } else if (family == AF_UNSPEC) {
 | |
|             EXPECT_EQ(0U, GetNumQueries(dns, kHelloExampleCom));  // no query because of the cache
 | |
|             EXPECT_THAT(result_strs,
 | |
|                         testing::UnorderedElementsAreArray(
 | |
|                                 {"1.2.3.1", "1.2.3.2", "2001:db8::41", "2001:db8::42"}));
 | |
|         }
 | |
|         dns.clearQueries();
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvGetAddrInfoTest, TruncatedResponse) {
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     constexpr char event_ipv4[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 254,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 7,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 2,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 2,
 | |
|                  ip_version: 0,
 | |
|                  protocol: 0,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 254,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 7,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 2,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 },
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 2,
 | |
|                  ip_version: 0,
 | |
|                  protocol: 0,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(kHelloExampleCom, ns_type::ns_t_cname, kCnameA);
 | |
|     dns.addMapping(kCnameA, ns_type::ns_t_cname, kCnameB);
 | |
|     dns.addMapping(kCnameB, ns_type::ns_t_cname, kCnameC);
 | |
|     dns.addMapping(kCnameC, ns_type::ns_t_cname, kCnameD);
 | |
|     dns.addMapping(kCnameD, ns_type::ns_t_a, kHelloExampleComAddrV4);
 | |
|     dns.addMapping(kCnameD, ns_type::ns_t_aaaa, kHelloExampleComAddrV6);
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_family;
 | |
|         const std::string expected_addr;
 | |
|         const std::string expected_event;
 | |
|     } testConfigs[]{
 | |
|             {AF_INET, kHelloExampleComAddrV4, event_ipv4},
 | |
|             {AF_INET6, kHelloExampleComAddrV6, event_ipv6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", config.ai_family));
 | |
|         dns.clearQueries();
 | |
| 
 | |
|         addrinfo* result = nullptr;
 | |
|         const addrinfo hints = {.ai_family = config.ai_family};
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|         EXPECT_THAT(event,
 | |
|                     NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
 | |
|         ScopedAddrinfo result_cleanup(result);
 | |
|         EXPECT_EQ(rv, 0);
 | |
|         EXPECT_TRUE(result != nullptr);
 | |
|         // Expect UDP response is truncated. The resolver retries over TCP. See RFC 1035
 | |
|         // section 4.2.1.
 | |
|         EXPECT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom), 1U);
 | |
|         EXPECT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom), 1U);
 | |
|         EXPECT_EQ(ToString(result), config.expected_addr);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Audit if Resolver read out of bounds, which needs HWAddressSanitizer build to trigger SIGABRT.
 | |
| TEST_F(ResolvGetAddrInfoTest, OverlengthResp) {
 | |
|     std::vector<std::string> nameList;
 | |
|     // Construct a long enough record that exceeds 8192 bytes (the maximum buffer size):
 | |
|     // Header: (Transaction ID, Flags, ...)                                        = 12   bytes
 | |
|     // Query: 19(Name)+2(Type)+2(Class)                                            = 23   bytes
 | |
|     // The 1st answer RR: 19(Name)+2(Type)+2(Class)+4(TTL)+2(Len)+77(CNAME)        = 106  bytes
 | |
|     // 2nd-50th answer RRs: 49*(77(Name)+2(Type)+2(Class)+4(TTL)+2(Len)+77(CNAME)) = 8036 bytes
 | |
|     // The last answer RR: 77(Name)+2(Type)+2(Class)+4(TTL)+2(Len)+4(Address)      = 91   bytes
 | |
|     // ----------------------------------------------------------------------------------------
 | |
|     // Sum:                                                                          8268 bytes
 | |
|     for (int i = 0; i < 10; i++) {
 | |
|         std::string domain(kMaxmiumLabelSize / 2, 'a' + i);
 | |
|         for (int j = 0; j < 5; j++) {
 | |
|             nameList.push_back(domain + std::string(kMaxmiumLabelSize / 2 + 1, '0' + j) +
 | |
|                                kExampleComDomain + ".");
 | |
|         }
 | |
|     }
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(kHelloExampleCom, ns_type::ns_t_cname, nameList[0]);
 | |
|     for (size_t i = 0; i < nameList.size() - 1; i++) {
 | |
|         dns.addMapping(nameList[i], ns_type::ns_t_cname, nameList[i + 1]);
 | |
|     }
 | |
|     dns.addMapping(nameList[nameList.size() - 1], ns_type::ns_t_a, kHelloExampleComAddrV4);
 | |
| 
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     addrinfo* result = nullptr;
 | |
|     const addrinfo hints = {.ai_family = AF_INET};
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &result, &event);
 | |
|     ScopedAddrinfo result_cleanup(result);
 | |
|     EXPECT_EQ(rv, EAI_FAIL);
 | |
|     EXPECT_TRUE(result == nullptr);
 | |
|     EXPECT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom), 2U);
 | |
|     EXPECT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom), 2U);
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) {
 | |
|     constexpr char host_name[] = "jiababuei.example.com.";
 | |
|     constexpr char v4addr[] = "1.2.3.4";
 | |
|     constexpr char v6addr[] = "::1.2.3.4";
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     constexpr char event_ipv4[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 1,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
 | |
|     dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_family;
 | |
|         const std::string expected_addr;
 | |
|         const std::string expected_event;
 | |
|     } testConfigs[]{
 | |
|             {AF_INET, v4addr, event_ipv4},
 | |
|             {AF_INET6, v6addr, event_ipv6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", config.ai_family));
 | |
|         dns.clearQueries();
 | |
| 
 | |
|         hostent* hp = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname("jiababuei", config.ai_family, &hbuf, tmpbuf, sizeof(tmpbuf),
 | |
|                                       &mNetcontext, &hp, &event);
 | |
|         EXPECT_THAT(event,
 | |
|                     NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
 | |
|         EXPECT_EQ(0, rv);
 | |
|         EXPECT_TRUE(hp != nullptr);
 | |
|         EXPECT_EQ(1U, GetNumQueries(dns, host_name));
 | |
|         EXPECT_EQ(config.expected_addr, ToString(hp));
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, IllegalHostname) {
 | |
|     test::DNSResponder dns;
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     // Illegal hostname is verified by res_hnok() in system/netd/resolv/res_comp.cpp.
 | |
|     static constexpr char const* illegalHostnames[] = {
 | |
|             kBadCharAfterPeriodHost,
 | |
|             kBadCharBeforePeriodHost,
 | |
|             kBadCharAtTheEndHost,
 | |
|             kBadCharInTheMiddleOfLabelHost,
 | |
|     };
 | |
| 
 | |
|     for (const auto& hostname : illegalHostnames) {
 | |
|         // Expect to get no address because hostname format is illegal.
 | |
|         //
 | |
|         // Ex:
 | |
|         // ANSWER SECTION:
 | |
|         // a.ex^ample.com.      IN  A       1.2.3.3
 | |
|         // a.ex^ample.com.      IN  AAAA    2001:db8::42
 | |
|         //
 | |
|         // In this example, querying "a.ex^ample.com" should get no address because
 | |
|         // "a.ex^ample.com" has an illegal char '^' in the middle of label.
 | |
|         dns.addMapping(hostname, ns_type::ns_t_a, "1.2.3.3");
 | |
|         dns.addMapping(hostname, ns_type::ns_t_aaaa, "2001:db8::42");
 | |
| 
 | |
|         for (const auto& family : {AF_INET, AF_INET6}) {
 | |
|             SCOPED_TRACE(fmt::format("family: {}, config.name: {}", family, hostname));
 | |
| 
 | |
|             struct hostent* hp = nullptr;
 | |
|             hostent hbuf;
 | |
|             char tmpbuf[MAXPACKET];
 | |
|             NetworkDnsEventReported event;
 | |
|             int rv = resolv_gethostbyname(hostname, family, &hbuf, tmpbuf, sizeof(tmpbuf),
 | |
|                                           &mNetcontext, &hp, &event);
 | |
|             EXPECT_EQ(nullptr, hp);
 | |
|             EXPECT_EQ(EAI_FAIL, rv);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, NoData) {
 | |
|     constexpr char v4_host_name[] = "v4only.example.com.";
 | |
| 
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     dns.clearQueries();
 | |
| 
 | |
|     // Want AAAA answer but DNS server has A answer only.
 | |
|     hostent* hp = nullptr;
 | |
|     hostent hbuf;
 | |
|     char tmpbuf[MAXPACKET];
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_gethostbyname("v4only", AF_INET6, &hbuf, tmpbuf, sizeof tmpbuf, &mNetcontext,
 | |
|                                   &hp, &event);
 | |
|     EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
 | |
|     EXPECT_EQ(nullptr, hp);
 | |
|     EXPECT_EQ(EAI_NODATA, rv);
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, ServerResponseError) {
 | |
|     constexpr char host_name[] = "hello.example.com.";
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         ns_rcode rcode;
 | |
|         int expected_eai_error;  // expected result
 | |
| 
 | |
|         // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
 | |
|         // which means no error. Note that the return error codes aren't mapped by rcode in the
 | |
|         // test case SERVFAIL, NOTIMP and REFUSED. See the comment of res_nsend()
 | |
|         // in system\netd\resolv\res_query.cpp for more detail.
 | |
|     } testConfigs[]{
 | |
|             // clang-format off
 | |
|             {ns_rcode::ns_r_formerr, EAI_FAIL},
 | |
|             {ns_rcode::ns_r_servfail, EAI_AGAIN},  // Not mapped by rcode.
 | |
|             {ns_rcode::ns_r_nxdomain, EAI_NODATA},
 | |
|             {ns_rcode::ns_r_notimpl, EAI_AGAIN},  // Not mapped by rcode.
 | |
|             {ns_rcode::ns_r_refused, EAI_AGAIN},  // Not mapped by rcode.
 | |
|             // clang-format on
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("rcode: {}", config.rcode));
 | |
| 
 | |
|         test::DNSResponder dns(config.rcode);
 | |
|         dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
 | |
|         dns.setResponseProbability(0.0);  // always ignore requests and response preset rcode
 | |
|         ASSERT_TRUE(dns.startServer());
 | |
|         ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|         hostent* hp = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname(host_name, AF_INET, &hbuf, tmpbuf, sizeof tmpbuf,
 | |
|                                       &mNetcontext, &hp, &event);
 | |
|         EXPECT_EQ(nullptr, hp);
 | |
|         EXPECT_EQ(config.expected_eai_error, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TODO: Add private DNS server timeout test.
 | |
| TEST_F(GetHostByNameForNetContextTest, ServerTimeout) {
 | |
|     constexpr char host_name[] = "hello.example.com.";
 | |
|     test::DNSResponder dns(static_cast<ns_rcode>(-1) /*no response*/);
 | |
|     dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
 | |
|     dns.setResponseProbability(0.0);  // always ignore requests and don't respond
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     hostent* hp = nullptr;
 | |
|     hostent hbuf;
 | |
|     char tmpbuf[MAXPACKET];
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_gethostbyname(host_name, AF_INET, &hbuf, tmpbuf, sizeof tmpbuf, &mNetcontext,
 | |
|                                   &hp, &event);
 | |
|     EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, CnamesNoIpAddress) {
 | |
|     constexpr char ACNAME[] = "acname";  // expect a cname in answer
 | |
|     constexpr char CNAMES[] = "cnames";  // expect cname chain in answer
 | |
| 
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping("cnames.example.com.", ns_type::ns_t_cname, "acname.example.com.");
 | |
|     dns.addMapping("acname.example.com.", ns_type::ns_t_cname, "hello.example.com.");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         const char* name;
 | |
|         int family;
 | |
|     } testConfigs[]{
 | |
|             {ACNAME, AF_INET},
 | |
|             {ACNAME, AF_INET6},
 | |
|             {CNAMES, AF_INET},
 | |
|             {CNAMES, AF_INET6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("config.family: {}, config.name: {}", config.family, config.name));
 | |
| 
 | |
|         struct hostent* hp = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname(config.name, config.family, &hbuf, tmpbuf, sizeof tmpbuf,
 | |
|                                       &mNetcontext, &hp, &event);
 | |
|         EXPECT_EQ(nullptr, hp);
 | |
|         EXPECT_EQ(EAI_FAIL, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, CnamesBrokenChainByIllegalCname) {
 | |
|     test::DNSResponder dns;
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         const char* name;
 | |
|         const char* cname;
 | |
|         std::string asHostName() const { return fmt::format("{}.example.com.", name); }
 | |
| 
 | |
|         // Illegal cname is verified by res_hnok() in system/netd/resolv/res_comp.cpp
 | |
|     } testConfigs[]{
 | |
|             // clang-format off
 | |
|             {NAME(kBadCharAfterPeriodHost),        kBadCharAfterPeriodHost},
 | |
|             {NAME(kBadCharBeforePeriodHost),       kBadCharBeforePeriodHost},
 | |
|             {NAME(kBadCharAtTheEndHost),           kBadCharAtTheEndHost},
 | |
|             {NAME(kBadCharInTheMiddleOfLabelHost), kBadCharInTheMiddleOfLabelHost},
 | |
|             // clang-format on
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         const std::string testHostName = config.asHostName();
 | |
| 
 | |
|         // Expect to get no address because the cname chain is broken by an illegal cname format.
 | |
|         //
 | |
|         // Ex:
 | |
|         // ANSWER SECTION:
 | |
|         // hello.example.com.   IN  CNAME   a.ex^ample.com.
 | |
|         // a.ex^ample.com.      IN  A       1.2.3.3
 | |
|         // a.ex^ample.com.      IN  AAAA    2001:db8::42
 | |
|         //
 | |
|         // In this example, querying hello.example.com should get no address because
 | |
|         // "a.ex^ample.com" has an illegal char '^' in the middle of label.
 | |
|         dns.addMapping(testHostName.c_str(), ns_type::ns_t_cname, config.cname);
 | |
|         dns.addMapping(config.cname, ns_type::ns_t_a, "1.2.3.3");
 | |
|         dns.addMapping(config.cname, ns_type::ns_t_aaaa, "2001:db8::42");
 | |
| 
 | |
|         for (const auto& family : {AF_INET, AF_INET6}) {
 | |
|             SCOPED_TRACE(fmt::format("family: {}, testHostName: {}", family, testHostName));
 | |
| 
 | |
|             struct hostent* hp = nullptr;
 | |
|             hostent hbuf;
 | |
|             char tmpbuf[MAXPACKET];
 | |
|             NetworkDnsEventReported event;
 | |
|             int rv = resolv_gethostbyname(config.name, family, &hbuf, tmpbuf, sizeof tmpbuf,
 | |
|                                           &mNetcontext, &hp, &event);
 | |
|             EXPECT_EQ(nullptr, hp);
 | |
|             EXPECT_EQ(EAI_FAIL, rv);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, CnamesInfiniteLoop) {
 | |
|     test::DNSResponder dns;
 | |
|     dns.addMapping("hello.example.com.", ns_type::ns_t_cname, "a.example.com.");
 | |
|     dns.addMapping("a.example.com.", ns_type::ns_t_cname, "hello.example.com.");
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", family));
 | |
| 
 | |
|         struct hostent* hp = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname("hello", family, &hbuf, tmpbuf, sizeof tmpbuf, &mNetcontext,
 | |
|                                       &hp, &event);
 | |
|         EXPECT_EQ(nullptr, hp);
 | |
|         EXPECT_EQ(EAI_FAIL, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, MdnsAlphabeticalHostname) {
 | |
|     constexpr char v4addr[] = "127.0.0.3";
 | |
|     constexpr char v6addr[] = "::127.0.0.3";
 | |
|     constexpr char host_name[] = "hello.local.";
 | |
| 
 | |
|     // Following fields will not be verified during the test in proto NetworkDnsEventReported.
 | |
|     // So don't need to config those values: event_type, return_code, latency_micros,
 | |
|     // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
 | |
|     constexpr char event_ipv4[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 1,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 1,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     constexpr char event_ipv6[] = R"Event(
 | |
|              NetworkDnsEventReported {
 | |
|              dns_query_events:
 | |
|              {
 | |
|                dns_query_event:[
 | |
|                 {
 | |
|                  rcode: 0,
 | |
|                  type: 28,
 | |
|                  cache_hit: 1,
 | |
|                  ip_version: 2,
 | |
|                  protocol: 5,
 | |
|                  retry_times: 0,
 | |
|                  dns_server_index: 0,
 | |
|                  connected: 0,
 | |
|                  latency_micros: 0,
 | |
|                  linux_errno: 0,
 | |
|                 }
 | |
|                ]
 | |
|              }
 | |
|         })Event";
 | |
| 
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService);
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService);
 | |
| 
 | |
|     mdnsv4.addMapping(host_name, ns_type::ns_t_a, v4addr);
 | |
|     mdnsv6.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
 | |
| 
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
| 
 | |
|     static const struct TestConfig {
 | |
|         int ai_family;
 | |
|         const std::vector<std::string> expected_addr;
 | |
|         const std::string expected_event;
 | |
|     } testConfigs[]{
 | |
|             {AF_INET, {v4addr}, event_ipv4},
 | |
|             {AF_INET6, {v6addr}, event_ipv6},
 | |
|     };
 | |
| 
 | |
|     for (const auto& config : testConfigs) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}", config.ai_family));
 | |
|         hostent* result = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname("hello.local", config.ai_family, &hbuf, tmpbuf,
 | |
|                                       sizeof(tmpbuf), &mNetcontext, &result, &event);
 | |
|         EXPECT_THAT(event,
 | |
|                     NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
 | |
|         EXPECT_EQ(0, rv);
 | |
|         test::DNSResponder& mdns = config.ai_family == AF_INET ? mdnsv4 : mdnsv6;
 | |
|         EXPECT_EQ(1U, GetNumQueries(mdns, host_name));
 | |
|         mdns.clearQueries();
 | |
|         std::vector<std::string> result_strs = ToStrings(result);
 | |
|         EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.expected_addr));
 | |
| 
 | |
|         // Ensure the query result is still cached.
 | |
|         rv = resolv_gethostbyname("hello.local", config.ai_family, &hbuf, tmpbuf, sizeof(tmpbuf),
 | |
|                                   &mNetcontext, &result, &event);
 | |
|         EXPECT_EQ(0, rv);
 | |
|         EXPECT_EQ(0U, GetNumQueries(mdns, host_name));
 | |
|         result_strs = ToStrings(result);
 | |
|         EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(config.expected_addr));
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, MdnsIllegalHostname) {
 | |
|     constexpr char v6addr[] = "::127.0.0.3";
 | |
|     constexpr char v4addr[] = "127.0.0.3";
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService);
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService);
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     mdnsv4.clearQueries();
 | |
|     mdnsv6.clearQueries();
 | |
| 
 | |
|     constexpr char illegalHostname[] = "hello^.local.";
 | |
|     // Expect to get no address because hostname format is illegal.
 | |
|     //
 | |
|     // Ex:
 | |
|     // ANSWER SECTION:
 | |
|     // hello^.local.      IN  A       127.0.0.3
 | |
|     // hello^.local.      IN  AAAA    ::127.0.0.3
 | |
|     //
 | |
|     // In this example, querying "hello^.local" should get no address because
 | |
|     // "hello^.local" has an illegal char '^' in the middle of label.
 | |
|     mdnsv4.addMapping(illegalHostname, ns_type::ns_t_a, v4addr);
 | |
|     mdnsv6.addMapping(illegalHostname, ns_type::ns_t_aaaa, v6addr);
 | |
| 
 | |
|     SCOPED_TRACE(fmt::format("family: {}, illegalHostname: {}", AF_INET6, illegalHostname));
 | |
|     struct hostent* result = nullptr;
 | |
|     hostent hbuf;
 | |
|     char tmpbuf[MAXPACKET];
 | |
|     NetworkDnsEventReported event;
 | |
|     int rv = resolv_gethostbyname("hello^.local", AF_INET6, &hbuf, tmpbuf, sizeof(tmpbuf),
 | |
|                                   &mNetcontext, &result, &event);
 | |
|     EXPECT_EQ(nullptr, result);
 | |
|     EXPECT_EQ(EAI_FAIL, rv);
 | |
| 
 | |
|     SCOPED_TRACE(fmt::format("family: {}, illegalHostname: {}", AF_INET, illegalHostname));
 | |
|     rv = resolv_gethostbyname("hello^.local", AF_INET, &hbuf, tmpbuf, sizeof(tmpbuf), &mNetcontext,
 | |
|                               &result, &event);
 | |
|     EXPECT_EQ(nullptr, result);
 | |
|     EXPECT_EQ(EAI_FAIL, rv);
 | |
| }
 | |
| 
 | |
| TEST_F(GetHostByNameForNetContextTest, MdnsResponderTimeout) {
 | |
|     constexpr char host_name[] = "hello.local.";
 | |
|     test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService,
 | |
|                               static_cast<ns_rcode>(-1));
 | |
|     mdnsv4.setResponseProbability(0.0);  // always ignore requests and don't respond
 | |
|     test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1));
 | |
|     mdnsv6.setResponseProbability(0.0);
 | |
|     ASSERT_TRUE(mdnsv4.startServer());
 | |
|     ASSERT_TRUE(mdnsv6.startServer());
 | |
|     ASSERT_EQ(0, SetResolvers());
 | |
|     test::DNSResponder dns("127.0.0.3", test::kDefaultListenService, static_cast<ns_rcode>(-1));
 | |
|     dns.setResponseProbability(0.0);
 | |
|     ASSERT_TRUE(dns.startServer());
 | |
| 
 | |
|     for (const auto& family : {AF_INET, AF_INET6}) {
 | |
|         SCOPED_TRACE(fmt::format("family: {}, host_name: {}", family, host_name));
 | |
|         hostent* result = nullptr;
 | |
|         hostent hbuf;
 | |
|         char tmpbuf[MAXPACKET];
 | |
|         NetworkDnsEventReported event;
 | |
|         int rv = resolv_gethostbyname("hello.local", family, &hbuf, tmpbuf, sizeof tmpbuf,
 | |
|                                       &mNetcontext, &result, &event);
 | |
|         EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvCommonFunctionTest, GetCustTableByName) {
 | |
|     const char custAddrV4[] = "1.2.3.4";
 | |
|     const char custAddrV6[] = "::1.2.3.4";
 | |
|     const char hostnameV4V6[] = "v4v6.example.com.";
 | |
|     const aidl::android::net::ResolverOptionsParcel& resolverOptions = {
 | |
|             {
 | |
|                     {custAddrV4, hostnameV4V6},
 | |
|                     {custAddrV6, hostnameV4V6},
 | |
|             },
 | |
|             aidl::android::net::IDnsResolver::TC_MODE_DEFAULT};
 | |
|     const std::vector<int32_t>& transportTypes = {IDnsResolver::TRANSPORT_WIFI};
 | |
|     EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID, servers, domains, params, resolverOptions,
 | |
|                                         transportTypes));
 | |
|     EXPECT_THAT(getCustomizedTableByName(TEST_NETID, hostnameV4V6),
 | |
|                 testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
 | |
| 
 | |
|     // Query address by mismatch hostname.
 | |
|     ASSERT_TRUE(getCustomizedTableByName(TEST_NETID, "not.in.cust.table").empty());
 | |
| 
 | |
|     // Query address by different netid.
 | |
|     ASSERT_TRUE(getCustomizedTableByName(TEST_NETID + 1, hostnameV4V6).empty());
 | |
|     resolv_create_cache_for_net(TEST_NETID + 1);
 | |
|     EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID + 1, servers, domains, params, resolverOptions,
 | |
|                                         transportTypes));
 | |
|     EXPECT_THAT(getCustomizedTableByName(TEST_NETID + 1, hostnameV4V6),
 | |
|                 testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvCommonFunctionTest, GetNetworkTypesForNet) {
 | |
|     const aidl::android::net::ResolverOptionsParcel& resolverOptions = {
 | |
|             {} /* hosts */, aidl::android::net::IDnsResolver::TC_MODE_DEFAULT};
 | |
|     const std::vector<int32_t>& transportTypes = {IDnsResolver::TRANSPORT_WIFI,
 | |
|                                                   IDnsResolver::TRANSPORT_VPN};
 | |
|     EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID, servers, domains, params, resolverOptions,
 | |
|                                         transportTypes));
 | |
|     EXPECT_EQ(android::net::NT_WIFI_VPN, resolv_get_network_types_for_net(TEST_NETID));
 | |
| }
 | |
| 
 | |
| TEST_F(ResolvCommonFunctionTest, ConvertTransportsToNetworkType) {
 | |
|     static const struct TestConfig {
 | |
|         int32_t networkType;
 | |
|         std::vector<int32_t> transportTypes;
 | |
|     } testConfigs[] = {
 | |
|             {android::net::NT_CELLULAR, {IDnsResolver::TRANSPORT_CELLULAR}},
 | |
|             {android::net::NT_WIFI, {IDnsResolver::TRANSPORT_WIFI}},
 | |
|             {android::net::NT_BLUETOOTH, {IDnsResolver::TRANSPORT_BLUETOOTH}},
 | |
|             {android::net::NT_ETHERNET, {IDnsResolver::TRANSPORT_ETHERNET}},
 | |
|             {android::net::NT_VPN, {IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_WIFI_AWARE, {IDnsResolver::TRANSPORT_WIFI_AWARE}},
 | |
|             {android::net::NT_LOWPAN, {IDnsResolver::TRANSPORT_LOWPAN}},
 | |
|             {android::net::NT_CELLULAR_VPN,
 | |
|              {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_CELLULAR_VPN,
 | |
|              {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_CELLULAR}},
 | |
|             {android::net::NT_WIFI_VPN,
 | |
|              {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_WIFI_VPN,
 | |
|              {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI}},
 | |
|             {android::net::NT_BLUETOOTH_VPN,
 | |
|              {IDnsResolver::TRANSPORT_BLUETOOTH, IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_BLUETOOTH_VPN,
 | |
|              {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_BLUETOOTH}},
 | |
|             {android::net::NT_ETHERNET_VPN,
 | |
|              {IDnsResolver::TRANSPORT_ETHERNET, IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_ETHERNET_VPN,
 | |
|              {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_ETHERNET}},
 | |
|             {android::net::NT_UNKNOWN, {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_UNKNOWN,
 | |
|              {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_LOWPAN}},
 | |
|             {android::net::NT_UNKNOWN, {}},
 | |
|             {android::net::NT_UNKNOWN,
 | |
|              {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_BLUETOOTH,
 | |
|               IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_WIFI_CELLULAR_VPN,
 | |
|              {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_WIFI,
 | |
|               IDnsResolver::TRANSPORT_VPN}},
 | |
|             {android::net::NT_WIFI_CELLULAR_VPN,
 | |
|              {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI,
 | |
|               IDnsResolver::TRANSPORT_CELLULAR}},
 | |
|     };
 | |
|     for (const auto& config : testConfigs) {
 | |
|         EXPECT_EQ(config.networkType, convert_network_type(config.transportTypes));
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Note that local host file function, files_getaddrinfo(), of resolv_getaddrinfo()
 | |
| // is not tested because it only returns a boolean (success or failure) without any error number.
 | |
| 
 | |
| // TODO: Add test for resolv_getaddrinfo().
 | |
| //       - DNS response message parsing.
 | |
| //           - Unexpected type of resource record (RR).
 | |
| //           - Invalid length CNAME, or QNAME.
 | |
| //           - Unexpected amount of questions.
 | |
| //       - CNAME RDATA with the domain name which has null label(s).
 | |
| // TODO: Add test for resolv_gethostbyname().
 | |
| //       - Invalid parameters.
 | |
| //       - DNS response message parsing.
 | |
| //           - Unexpected type of resource record (RR).
 | |
| //           - Invalid length CNAME, or QNAME.
 | |
| //           - Unexpected amount of questions.
 | |
| //       - CNAME RDATA with the domain name which has null label(s).
 | |
| // TODO: Add test for resolv_gethostbyaddr().
 | |
| 
 | |
| }  // end of namespace net
 | |
| }  // end of namespace android
 |