Loading res_send.cpp +7 −9 Original line number Diff line number Diff line Loading @@ -483,11 +483,8 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* mDnsQueryEvent->set_linux_errno(static_cast<LinuxErrno>(terrno)); resolv_stats_add(statp->netid, receivedMdnsAddr, mDnsQueryEvent); if (resplen <= 0) { _resolv_cache_query_failed(statp->netid, msg, flags); return -terrno; } LOG(DEBUG) << __func__ << ": got answer:"; if (resplen > 0) { LOG(DEBUG) << __func__ << ": got answer from mDNS:"; res_pquery(ans.first(resplen)); if (cache_status == RESOLV_CACHE_NOTFOUND) { Loading @@ -495,6 +492,7 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* } return resplen; } } if (statp->nameserverCount() == 0) { // We have no nameservers configured and it's not a MDNS resolution, so there's no Loading tests/resolv_integration_test.cpp +59 −0 Original line number Diff line number Diff line Loading @@ -6519,6 +6519,65 @@ TEST_F(ResolverTest, MdnsGetAddrInfo_cnamesIllegalRdata) { EXPECT_TRUE(result == nullptr); } // Test if .local resolution will try unicast when multicast is failed. TEST_F(ResolverTest, MdnsGetAddrInfo_fallback) { constexpr char v6addr[] = "::1.2.3.4"; constexpr char v4addr[] = "1.2.3.4"; constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); // Set unresponsive on multicast. mdnsv4.setResponseProbability(0.0); mdnsv6.setResponseProbability(0.0); ASSERT_TRUE(mdnsv4.startServer()); ASSERT_TRUE(mdnsv6.startServer()); const std::vector<DnsRecord> records = { {host_name, ns_type::ns_t_a, v4addr}, {host_name, ns_type::ns_t_aaaa, v6addr}, }; test::DNSResponder dns("127.0.0.3"); StartDns(dns, records); ASSERT_TRUE(mDnsClient.SetResolversForNetwork()); static const struct TestConfig { int ai_family; const std::vector<std::string> expected_addr; } testConfigs[]{ {AF_INET, {v4addr}}, {AF_INET6, {v6addr}}, {AF_UNSPEC, {v4addr, v6addr}}, }; for (const auto& config : testConfigs) { SCOPED_TRACE(fmt::format("family: {}", config.ai_family)); addrinfo hints = {.ai_family = config.ai_family, .ai_socktype = SOCK_DGRAM}; ScopedAddrinfo result = safe_getaddrinfo("hello.local", nullptr, &hints); EXPECT_TRUE(result != nullptr); if (config.ai_family == AF_INET) { EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(0U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); } else if (config.ai_family == AF_INET6) { EXPECT_EQ(0U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); } else { EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(2U, GetNumQueries(dns, host_name)); } std::string result_str = ToString(result); EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray(config.expected_addr)); mdnsv4.clearQueries(); mdnsv6.clearQueries(); dns.clearQueries(); ASSERT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk()); } } // ResolverMultinetworkTest is used to verify multinetwork functionality. Here's how it works: // The resolver sends queries to address A, and then there will be a TunForwarder helping forward // the packets to address B, which is the address on which the testing server is listening. The Loading tests/resolv_private_dns_test.cpp +77 −2 Original line number Diff line number Diff line Loading @@ -307,9 +307,9 @@ class BasePrivateDnsTest : public BaseTest { BaseTest::TearDown(); } void sendQueryAndCheckResult() { void sendQueryAndCheckResult(const char* host_name = kQueryHostname) { const addrinfo hints = {.ai_socktype = SOCK_DGRAM}; ScopedAddrinfo result = safe_getaddrinfo(kQueryHostname, nullptr, &hints); ScopedAddrinfo result = safe_getaddrinfo(host_name, nullptr, &hints); EXPECT_THAT(ToStrings(result), testing::ElementsAreArray({kQueryAnswerAAAA, kQueryAnswerA})); }; Loading Loading @@ -380,6 +380,12 @@ class TransportParameterizedTest : public BasePrivateDnsTest, ASSERT_TRUE(doh_backend.startServer()); ASSERT_TRUE(doh.startServer()); } SetMdnsRoute(); } void TearDown() override { RemoveMdnsRoute(); BasePrivateDnsTest::TearDown(); } bool testParamHasDot() { return GetParam() & kDotBit; } Loading Loading @@ -437,6 +443,75 @@ TEST_P(TransportParameterizedTest, GetAddrInfo) { } } TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) { constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); // Set unresponsive on multicast. mdnsv4.setResponseProbability(0.0); mdnsv6.setResponseProbability(0.0); ASSERT_TRUE(mdnsv4.startServer()); ASSERT_TRUE(mdnsv6.startServer()); const std::vector<DnsRecord> records = { {host_name, ns_type::ns_t_a, kQueryAnswerA}, {host_name, ns_type::ns_t_aaaa, kQueryAnswerAAAA}, }; for (const auto& r : records) { dns.addMapping(r.host_name, r.type, r.addr); dot_backend.addMapping(r.host_name, r.type, r.addr); doh_backend.addMapping(r.host_name, r.type, r.addr); } auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); // This waiting time is expected to avoid that the DoH validation event interferes other tests. if (!testParamHasDoh()) waitForDohValidationFailed(); // Have the test independent of the number of sent queries in private DNS validation, because // the DnsResolver can send either 1 or 2 queries in DoT validation. if (testParamHasDoh()) { doh.clearQueries(); } if (testParamHasDot()) { EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); } dns.clearQueries(); EXPECT_NO_FAILURE(sendQueryAndCheckResult("hello.local")); EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); if (testParamHasDoh()) { EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 0 /* dot */, 2 /* doh */)); } else { EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 2 /* dot */, 0 /* doh */)); } // Stop the private DNS servers. Since we are in opportunistic mode, queries will // fall back to the cleartext nameserver. flushCache(); dot.stopServer(); doh.stopServer(); mdnsv4.clearQueries(); mdnsv6.clearQueries(); EXPECT_NO_FAILURE(sendQueryAndCheckResult("hello.local")); EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); if (testParamHasDoh()) { EXPECT_NO_FAILURE(expectQueries(2 /* dns */, 0 /* dot */, 2 /* doh */)); } else { EXPECT_NO_FAILURE(expectQueries(2 /* dns */, 2 /* dot */, 0 /* doh */)); } } class PrivateDnsDohTest : public BasePrivateDnsTest { protected: void SetUp() override { Loading tests/resolv_unit_test.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1037,6 +1037,9 @@ TEST_F(ResolvGetAddrInfoTest, MdnsResponderTimeout) { 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)); Loading Loading @@ -1825,6 +1828,9 @@ TEST_F(GetHostByNameForNetContextTest, MdnsResponderTimeout) { 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)); Loading Loading
res_send.cpp +7 −9 Original line number Diff line number Diff line Loading @@ -483,11 +483,8 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* mDnsQueryEvent->set_linux_errno(static_cast<LinuxErrno>(terrno)); resolv_stats_add(statp->netid, receivedMdnsAddr, mDnsQueryEvent); if (resplen <= 0) { _resolv_cache_query_failed(statp->netid, msg, flags); return -terrno; } LOG(DEBUG) << __func__ << ": got answer:"; if (resplen > 0) { LOG(DEBUG) << __func__ << ": got answer from mDNS:"; res_pquery(ans.first(resplen)); if (cache_status == RESOLV_CACHE_NOTFOUND) { Loading @@ -495,6 +492,7 @@ int res_nsend(ResState* statp, span<const uint8_t> msg, span<uint8_t> ans, int* } return resplen; } } if (statp->nameserverCount() == 0) { // We have no nameservers configured and it's not a MDNS resolution, so there's no Loading
tests/resolv_integration_test.cpp +59 −0 Original line number Diff line number Diff line Loading @@ -6519,6 +6519,65 @@ TEST_F(ResolverTest, MdnsGetAddrInfo_cnamesIllegalRdata) { EXPECT_TRUE(result == nullptr); } // Test if .local resolution will try unicast when multicast is failed. TEST_F(ResolverTest, MdnsGetAddrInfo_fallback) { constexpr char v6addr[] = "::1.2.3.4"; constexpr char v4addr[] = "1.2.3.4"; constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); // Set unresponsive on multicast. mdnsv4.setResponseProbability(0.0); mdnsv6.setResponseProbability(0.0); ASSERT_TRUE(mdnsv4.startServer()); ASSERT_TRUE(mdnsv6.startServer()); const std::vector<DnsRecord> records = { {host_name, ns_type::ns_t_a, v4addr}, {host_name, ns_type::ns_t_aaaa, v6addr}, }; test::DNSResponder dns("127.0.0.3"); StartDns(dns, records); ASSERT_TRUE(mDnsClient.SetResolversForNetwork()); static const struct TestConfig { int ai_family; const std::vector<std::string> expected_addr; } testConfigs[]{ {AF_INET, {v4addr}}, {AF_INET6, {v6addr}}, {AF_UNSPEC, {v4addr, v6addr}}, }; for (const auto& config : testConfigs) { SCOPED_TRACE(fmt::format("family: {}", config.ai_family)); addrinfo hints = {.ai_family = config.ai_family, .ai_socktype = SOCK_DGRAM}; ScopedAddrinfo result = safe_getaddrinfo("hello.local", nullptr, &hints); EXPECT_TRUE(result != nullptr); if (config.ai_family == AF_INET) { EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(0U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); } else if (config.ai_family == AF_INET6) { EXPECT_EQ(0U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(1U, GetNumQueries(dns, host_name)); } else { EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); EXPECT_EQ(2U, GetNumQueries(dns, host_name)); } std::string result_str = ToString(result); EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray(config.expected_addr)); mdnsv4.clearQueries(); mdnsv6.clearQueries(); dns.clearQueries(); ASSERT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk()); } } // ResolverMultinetworkTest is used to verify multinetwork functionality. Here's how it works: // The resolver sends queries to address A, and then there will be a TunForwarder helping forward // the packets to address B, which is the address on which the testing server is listening. The Loading
tests/resolv_private_dns_test.cpp +77 −2 Original line number Diff line number Diff line Loading @@ -307,9 +307,9 @@ class BasePrivateDnsTest : public BaseTest { BaseTest::TearDown(); } void sendQueryAndCheckResult() { void sendQueryAndCheckResult(const char* host_name = kQueryHostname) { const addrinfo hints = {.ai_socktype = SOCK_DGRAM}; ScopedAddrinfo result = safe_getaddrinfo(kQueryHostname, nullptr, &hints); ScopedAddrinfo result = safe_getaddrinfo(host_name, nullptr, &hints); EXPECT_THAT(ToStrings(result), testing::ElementsAreArray({kQueryAnswerAAAA, kQueryAnswerA})); }; Loading Loading @@ -380,6 +380,12 @@ class TransportParameterizedTest : public BasePrivateDnsTest, ASSERT_TRUE(doh_backend.startServer()); ASSERT_TRUE(doh.startServer()); } SetMdnsRoute(); } void TearDown() override { RemoveMdnsRoute(); BasePrivateDnsTest::TearDown(); } bool testParamHasDot() { return GetParam() & kDotBit; } Loading Loading @@ -437,6 +443,75 @@ TEST_P(TransportParameterizedTest, GetAddrInfo) { } } TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) { constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); test::DNSResponder mdnsv6("::1", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); // Set unresponsive on multicast. mdnsv4.setResponseProbability(0.0); mdnsv6.setResponseProbability(0.0); ASSERT_TRUE(mdnsv4.startServer()); ASSERT_TRUE(mdnsv6.startServer()); const std::vector<DnsRecord> records = { {host_name, ns_type::ns_t_a, kQueryAnswerA}, {host_name, ns_type::ns_t_aaaa, kQueryAnswerAAAA}, }; for (const auto& r : records) { dns.addMapping(r.host_name, r.type, r.addr); dot_backend.addMapping(r.host_name, r.type, r.addr); doh_backend.addMapping(r.host_name, r.type, r.addr); } auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); if (testParamHasDoh()) EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); if (testParamHasDot()) EXPECT_TRUE(WaitForDotValidation(test::kDefaultListenAddr, true)); // This waiting time is expected to avoid that the DoH validation event interferes other tests. if (!testParamHasDoh()) waitForDohValidationFailed(); // Have the test independent of the number of sent queries in private DNS validation, because // the DnsResolver can send either 1 or 2 queries in DoT validation. if (testParamHasDoh()) { doh.clearQueries(); } if (testParamHasDot()) { EXPECT_TRUE(dot.waitForQueries(1)); dot.clearQueries(); } dns.clearQueries(); EXPECT_NO_FAILURE(sendQueryAndCheckResult("hello.local")); EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); if (testParamHasDoh()) { EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 0 /* dot */, 2 /* doh */)); } else { EXPECT_NO_FAILURE(expectQueries(0 /* dns */, 2 /* dot */, 0 /* doh */)); } // Stop the private DNS servers. Since we are in opportunistic mode, queries will // fall back to the cleartext nameserver. flushCache(); dot.stopServer(); doh.stopServer(); mdnsv4.clearQueries(); mdnsv6.clearQueries(); EXPECT_NO_FAILURE(sendQueryAndCheckResult("hello.local")); EXPECT_EQ(1U, GetNumQueries(mdnsv4, host_name)); EXPECT_EQ(1U, GetNumQueries(mdnsv6, host_name)); if (testParamHasDoh()) { EXPECT_NO_FAILURE(expectQueries(2 /* dns */, 0 /* dot */, 2 /* doh */)); } else { EXPECT_NO_FAILURE(expectQueries(2 /* dns */, 2 /* dot */, 0 /* doh */)); } } class PrivateDnsDohTest : public BasePrivateDnsTest { protected: void SetUp() override { Loading
tests/resolv_unit_test.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1037,6 +1037,9 @@ TEST_F(ResolvGetAddrInfoTest, MdnsResponderTimeout) { 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)); Loading Loading @@ -1825,6 +1828,9 @@ TEST_F(GetHostByNameForNetContextTest, MdnsResponderTimeout) { 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)); Loading