// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/dns/dns_config_service_android.h" #include #include #include #include #include "base/android/build_info.h" #include "base/functional/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_refptr.h" #include "base/test/task_environment.h" #include "net/android/network_library.h" #include "net/base/ip_endpoint.h" #include "net/base/mock_network_change_notifier.h" #include "net/base/network_change_notifier.h" #include "net/dns/dns_config.h" #include "net/test/test_with_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net::internal { namespace { const IPEndPoint kNameserver1(IPAddress(1, 2, 3, 4), 53); const IPEndPoint kNameserver2(IPAddress(1, 2, 3, 8), 53); // DnsConfigServiceAndroid uses a simplified implementation for Android versions // before relevant APIs were added in Android M. Most of these tests are // targeting the logic used in M and beyond. #define SKIP_ANDROID_VERSIONS_BEFORE_M() \ { \ if (base::android::BuildInfo::GetInstance()->sdk_int() < \ base::android::SDK_VERSION_MARSHMALLOW) { \ GTEST_SKIP() << "Test not necessary or compatible with pre-M."; \ } \ } // RefCountedThreadSafe to allow safe usage and reference storage in // DnsConfigServiceAndroid's off-sequence utility classes. class MockDnsServerGetter : public base::RefCountedThreadSafe { public: void set_retval(bool retval) { retval_ = retval; } void set_dns_servers(std::vector dns_servers) { dns_servers_ = std::move(dns_servers); } void set_dns_over_tls_active(bool dns_over_tls_active) { dns_over_tls_active_ = dns_over_tls_active; } void set_dns_over_tls_hostname(std::string dns_over_tls_hostname) { dns_over_tls_hostname_ = std::move(dns_over_tls_hostname); } void set_search_suffixes(std::vector search_suffixes) { search_suffixes_ = std::move(search_suffixes); } android::DnsServerGetter ConstructGetter() { return base::BindRepeating(&MockDnsServerGetter::GetDnsServers, this); } private: friend base::RefCountedThreadSafe; ~MockDnsServerGetter() = default; bool GetDnsServers(std::vector* dns_servers, bool* dns_over_tls_active, std::string* dns_over_tls_hostname, std::vector* search_suffixes) { if (retval_) { *dns_servers = dns_servers_; *dns_over_tls_active = dns_over_tls_active_; *dns_over_tls_hostname = dns_over_tls_hostname_; *search_suffixes = search_suffixes_; } return retval_; } bool retval_ = false; std::vector dns_servers_; bool dns_over_tls_active_ = false; std::string dns_over_tls_hostname_; std::vector search_suffixes_; }; class DnsConfigServiceAndroidTest : public testing::Test, public WithTaskEnvironment { public: DnsConfigServiceAndroidTest() : WithTaskEnvironment( base::test::TaskEnvironment::TimeSource::MOCK_TIME) { service_->set_dns_server_getter_for_testing( mock_dns_server_getter_->ConstructGetter()); } ~DnsConfigServiceAndroidTest() override = default; void OnConfigChanged(const DnsConfig& config) { EXPECT_TRUE(config.IsValid()); seen_config_ = true; real_config_ = config; } protected: bool seen_config_ = false; std::unique_ptr service_ = std::make_unique(); DnsConfig real_config_; scoped_refptr mock_dns_server_getter_ = base::MakeRefCounted(); test::ScopedMockNetworkChangeNotifier mock_notifier_; }; TEST_F(DnsConfigServiceAndroidTest, HandlesNetworkChangeNotifications) { service_->WatchConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); // Cannot validate any behavior other than not crashing because this test runs // on Android versions with unmocked behavior. } TEST_F(DnsConfigServiceAndroidTest, NewConfigReadOnNetworkChange) { SKIP_ANDROID_VERSIONS_BEFORE_M(); mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); service_->WatchConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1)); mock_dns_server_getter_->set_dns_servers({kNameserver2}); seen_config_ = false; NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( NetworkChangeNotifier::CONNECTION_WIFI); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver2)); } TEST_F(DnsConfigServiceAndroidTest, NoConfigNotificationWhenUnchanged) { SKIP_ANDROID_VERSIONS_BEFORE_M(); mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); service_->WatchConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1)); seen_config_ = false; NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( NetworkChangeNotifier::CONNECTION_WIFI); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); // Because the DNS config hasn't changed, no new config should be seen. EXPECT_FALSE(seen_config_); } TEST_F(DnsConfigServiceAndroidTest, IgnoresConnectionNoneChangeNotifications) { SKIP_ANDROID_VERSIONS_BEFORE_M(); mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); service_->WatchConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1)); // Change the DNS config to ensure the lack of notification is due to not // being checked for. mock_dns_server_getter_->set_dns_servers({kNameserver2}); seen_config_ = false; NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( NetworkChangeNotifier::CONNECTION_NONE); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); // Expect no new config read for network change to NONE. EXPECT_FALSE(seen_config_); } // Regression test for https://crbug.com/704662. TEST_F(DnsConfigServiceAndroidTest, ChangeConfigMultipleTimes) { SKIP_ANDROID_VERSIONS_BEFORE_M(); mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); service_->WatchConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1)); for (int i = 0; i < 5; i++) { mock_dns_server_getter_->set_dns_servers({kNameserver2}); seen_config_ = false; NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( NetworkChangeNotifier::CONNECTION_WIFI); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver2)); mock_dns_server_getter_->set_dns_servers({kNameserver1}); seen_config_ = false; NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( NetworkChangeNotifier::CONNECTION_WIFI); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1)); } } TEST_F(DnsConfigServiceAndroidTest, ReadsSearchSuffixes) { SKIP_ANDROID_VERSIONS_BEFORE_M(); const std::vector kSuffixes{"name1.test", "name2.test"}; mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); mock_dns_server_getter_->set_search_suffixes(kSuffixes); service_->ReadConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_EQ(real_config_.search, kSuffixes); } TEST_F(DnsConfigServiceAndroidTest, ReadsEmptySearchSuffixes) { SKIP_ANDROID_VERSIONS_BEFORE_M(); mock_dns_server_getter_->set_retval(true); mock_dns_server_getter_->set_dns_servers({kNameserver1}); service_->ReadConfig(base::BindRepeating( &DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this))); FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay); RunUntilIdle(); ASSERT_TRUE(seen_config_); EXPECT_TRUE(real_config_.search.empty()); } } // namespace } // namespace net::internal