| // Copyright 2021 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "dns-proxy/proxy.h" | |
| #include <fcntl.h> | |
| #include <linux/rtnetlink.h> | |
| #include <sys/stat.h> | |
| #include <memory> | |
| #include <optional> | |
| #include <utility> | |
| #include <vector> | |
| #include <base/functional/callback.h> | |
| #include <chromeos/patchpanel/dbus/fake_client.h> | |
| #include <chromeos/patchpanel/mock_message_dispatcher.h> | |
| #include <dbus/mock_bus.h> | |
| #include <dbus/mock_object_proxy.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include <net-base/ip_address.h> | |
| #include <net-base/byte_utils.h> | |
| #include <net-base/rtnl_message.h> | |
| #include <shill/dbus/client/fake_client.h> | |
| #include <shill/dbus-constants.h> | |
| #include <shill/dbus-proxy-mocks.h> | |
| #include "dns-proxy/ipc.pb.h" | |
| namespace dns_proxy { | |
| namespace { | |
| constexpr net_base::IPv4Address kNetnsPeerIPv4Addr(100, 115, 92, 130); | |
| constexpr net_base::IPv6Address kNetnsPeerIPv6Addr( | |
| 0xfd, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01); | |
| constexpr base::TimeDelta kRequestTimeout = base::Seconds(10000); | |
| constexpr base::TimeDelta kRequestRetryDelay = base::Milliseconds(200); | |
| constexpr int32_t kRequestMaxRetry = 1; | |
| int make_fd() { | |
| std::string fn( | |
| ::testing::UnitTest::GetInstance()->current_test_info()->name()); | |
| fn = "/tmp/" + fn; | |
| return open(fn.c_str(), O_CREAT, 0600); | |
| } | |
| // A helper function to convert a list of IP addresses from type std::string to | |
| // net_base::IPAddress. If |list2| is provided, its elements will be appended to | |
| // the results. This function assumes that all input strings are valid IP | |
| // addresses, otherwise it will crash directly. | |
| std::vector<net_base::IPAddress> StringsToIPAddressesChecked( | |
| const std::vector<std::string>& list1, | |
| const std::vector<std::string>& list2 = {}) { | |
| std::vector<net_base::IPAddress> ret; | |
| for (const auto& str : list1) { | |
| ret.push_back(*net_base::IPAddress::CreateFromString(str)); | |
| } | |
| for (const auto& str : list2) { | |
| ret.push_back(*net_base::IPAddress::CreateFromString(str)); | |
| } | |
| return ret; | |
| } | |
| } // namespace | |
| using org::chromium::flimflam::ManagerProxyInterface; | |
| using org::chromium::flimflam::ManagerProxyMock; | |
| using testing::_; | |
| using testing::ByMove; | |
| using testing::DoAll; | |
| using testing::ElementsAre; | |
| using testing::IsEmpty; | |
| using testing::Return; | |
| using testing::SetArgPointee; | |
| using testing::StrEq; | |
| MATCHER_P(EqualsProto, | |
| message, | |
| "Match a proto Message equal to the matcher's argument.") { | |
| std::string expected_serialized, actual_serialized; | |
| message.SerializeToString(&expected_serialized); | |
| arg.SerializeToString(&actual_serialized); | |
| return expected_serialized == actual_serialized; | |
| } | |
| patchpanel::Client::VirtualDevice virtualdev( | |
| patchpanel::Client::GuestType guest_type, | |
| const std::string& ifname, | |
| const std::string& phys_ifname) { | |
| patchpanel::Client::VirtualDevice device; | |
| device.ifname = ifname; | |
| device.phys_ifname = phys_ifname; | |
| device.guest_type = guest_type; | |
| return device; | |
| } | |
| class FakeShillClient : public shill::FakeClient { | |
| public: | |
| FakeShillClient(scoped_refptr<dbus::Bus> bus, | |
| ManagerProxyInterface* manager_proxy) | |
| : shill::FakeClient(bus), manager_proxy_(manager_proxy) {} | |
| std::unique_ptr<shill::Client::ManagerPropertyAccessor> ManagerProperties( | |
| const base::TimeDelta& timeout) const override { | |
| return std::make_unique<shill::Client::ManagerPropertyAccessor>( | |
| manager_proxy_); | |
| } | |
| std::unique_ptr<shill::Client::Device> DefaultDevice( | |
| bool exclude_vpn) override { | |
| return std::move(default_device_); | |
| } | |
| ManagerProxyInterface* GetManagerProxy() const override { | |
| return manager_proxy_; | |
| } | |
| std::unique_ptr<shill::Client::Device> default_device_; | |
| private: | |
| ManagerProxyInterface* manager_proxy_; | |
| }; | |
| class MockPatchpanelClient : public patchpanel::FakeClient { | |
| public: | |
| MockPatchpanelClient() = default; | |
| ~MockPatchpanelClient() override = default; | |
| MOCK_METHOD( | |
| (std::pair<base::ScopedFD, patchpanel::Client::ConnectedNamespace>), | |
| ConnectNamespace, | |
| (pid_t pid, | |
| const std::string& outbound_ifname, | |
| bool forward_user_traffic, | |
| bool route_on_vpn, | |
| patchpanel::Client::TrafficSource traffic_source, | |
| bool static_ipv6), | |
| (override)); | |
| MOCK_METHOD(base::ScopedFD, | |
| RedirectDns, | |
| (patchpanel::Client::DnsRedirectionRequestType, | |
| const std::string&, | |
| const std::string&, | |
| const std::vector<std::string>&, | |
| const std::string&), | |
| (override)); | |
| MOCK_METHOD(std::vector<patchpanel::Client::VirtualDevice>, | |
| GetDevices, | |
| (), | |
| (override)); | |
| }; | |
| class MockResolver : public Resolver { | |
| public: | |
| MockResolver() | |
| : Resolver(base::DoNothing(), | |
| kRequestTimeout, | |
| kRequestRetryDelay, | |
| kRequestMaxRetry) {} | |
| ~MockResolver() override = default; | |
| MOCK_METHOD(bool, ListenUDP, (struct sockaddr*), (override)); | |
| MOCK_METHOD(bool, ListenTCP, (struct sockaddr*), (override)); | |
| MOCK_METHOD(void, | |
| SetNameServers, | |
| (const std::vector<std::string>&), | |
| (override)); | |
| MOCK_METHOD(void, | |
| SetDoHProviders, | |
| (const std::vector<std::string>&, bool), | |
| (override)); | |
| }; | |
| class TestProxy : public Proxy { | |
| public: | |
| TestProxy(const Options& opts, | |
| std::unique_ptr<patchpanel::Client> patchpanel, | |
| std::unique_ptr<shill::Client> shill, | |
| std::unique_ptr<patchpanel::MessageDispatcher<ProxyAddrMessage>> | |
| msg_dispatcher) | |
| : Proxy(opts, | |
| std::move(patchpanel), | |
| std::move(shill), | |
| std::move(msg_dispatcher)) {} | |
| std::unique_ptr<Resolver> resolver; | |
| std::unique_ptr<Resolver> NewResolver(base::TimeDelta timeout, | |
| base::TimeDelta retry_delay, | |
| int max_num_retries) override { | |
| return std::move(resolver); | |
| } | |
| }; | |
| class ProxyTest : public ::testing::Test { | |
| protected: | |
| ProxyTest() | |
| : mock_bus_(new dbus::MockBus{dbus::Bus::Options{}}), | |
| mock_proxy_(new dbus::MockObjectProxy(mock_bus_.get(), | |
| shill::kFlimflamServiceName, | |
| dbus::ObjectPath("/"))) {} | |
| ~ProxyTest() override { mock_bus_->ShutdownAndBlock(); } | |
| void SetUp() override { | |
| EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _)) | |
| .WillRepeatedly(Return(mock_proxy_.get())); | |
| } | |
| void SetUpProxy(const Proxy::Options& opts, | |
| std::unique_ptr<shill::Client::Device> device = nullptr, | |
| bool set_resolver = true) { | |
| // Set up mocks and fakes. | |
| patchpanel_client_ = new MockPatchpanelClient(); | |
| shill_client_ = new FakeShillClient( | |
| mock_bus_, reinterpret_cast<ManagerProxyInterface*>( | |
| const_cast<ManagerProxyMock*>(&mock_manager_))); | |
| msg_dispatcher_ = new patchpanel::MockMessageDispatcher<ProxyAddrMessage>(); | |
| // Initialize Proxy instance. | |
| proxy_ = std::make_unique<TestProxy>( | |
| opts, base::WrapUnique(patchpanel_client_), | |
| base::WrapUnique(shill_client_), base::WrapUnique(msg_dispatcher_)); | |
| // Initialize default proxy behaviors. | |
| proxy_->shill_ready_ = true; | |
| proxy_->device_ = std::move(device); | |
| if (set_resolver) { | |
| resolver_ = new MockResolver(); | |
| proxy_->resolver_ = base::WrapUnique(resolver_); | |
| proxy_->doh_config_.set_resolver(resolver_); | |
| } | |
| // Initialize default mocks behavior. | |
| if (opts.type == Proxy::Type::kSystem) { | |
| ON_CALL(mock_manager_, SetDNSProxyAddresses(_, _, _)) | |
| .WillByDefault(Return(true)); | |
| ON_CALL(*msg_dispatcher_, SendMessage(_)).WillByDefault(Return(true)); | |
| } | |
| } | |
| std::unique_ptr<shill::Client::Device> ShillDevice( | |
| shill::Client::Device::ConnectionState state = | |
| shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type type = shill::Client::Device::Type::kEthernet, | |
| const std::string& ifname = "", | |
| std::vector<std::string> ipv4_nameservers = {"8.8.8.8"}, | |
| std::vector<std::string> ipv6_nameservers = {"2001:4860:4860::8888"}) { | |
| auto dev = std::make_unique<shill::Client::Device>(); | |
| dev->type = type; | |
| dev->state = state; | |
| dev->ifname = ifname; | |
| dev->network_config.dns_servers = | |
| StringsToIPAddressesChecked(ipv6_nameservers, ipv4_nameservers); | |
| return dev; | |
| } | |
| void SetNamespaceAddresses( | |
| const std::optional<net_base::IPv4Address>& ipv4_addr, | |
| const std::optional<net_base::IPv6Address>& ipv6_addr) { | |
| proxy_->ns_fd_ = base::ScopedFD(make_fd()); | |
| if (ipv4_addr) { | |
| proxy_->ns_.peer_ipv4_address = *ipv4_addr; | |
| } | |
| if (ipv6_addr) { | |
| proxy_->ns_peer_ipv6_address_ = *ipv6_addr; | |
| } | |
| } | |
| void SetNameServers(const std::vector<std::string>& ipv4_nameservers, | |
| const std::vector<std::string>& ipv6_nameservers) { | |
| EXPECT_TRUE(proxy_->device_); | |
| proxy_->device_->network_config.dns_servers = | |
| StringsToIPAddressesChecked(ipv6_nameservers, ipv4_nameservers); | |
| proxy_->UpdateNameServers(); | |
| } | |
| protected: | |
| scoped_refptr<dbus::MockBus> mock_bus_; | |
| scoped_refptr<dbus::MockObjectProxy> mock_proxy_; | |
| ManagerProxyMock mock_manager_; | |
| MockResolver* resolver_; | |
| patchpanel::MockMessageDispatcher<ProxyAddrMessage>* msg_dispatcher_; | |
| FakeShillClient* shill_client_; | |
| MockPatchpanelClient* patchpanel_client_; | |
| std::unique_ptr<TestProxy> proxy_; | |
| }; | |
| TEST_F(ProxyTest, SystemProxy_OnShutdownClearsAddressPropertyOnShill) { | |
| EXPECT_CALL(mock_manager_, ClearDNSProxyAddresses(_, _)); | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| int unused; | |
| proxy_->OnShutdown(&unused); | |
| } | |
| TEST_F(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) { | |
| EXPECT_CALL(mock_manager_, SetDNSProxyAddresses(_, _, _)).Times(0); | |
| EXPECT_CALL(mock_manager_, ClearDNSProxyAddresses(_, _)).Times(0); | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| int unused; | |
| proxy_->OnShutdown(&unused); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetShillDNSProxyAddressesDoesntCrashIfDieFalse) { | |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | |
| EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0); | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| proxy_->SetShillDNSProxyAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr, | |
| /*die_on_failure=*/false, | |
| /*num_retries=*/0); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetShillDNSProxyAddresses) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL(mock_manager_, | |
| SetDNSProxyAddresses(ElementsAre(kNetnsPeerIPv4Addr.ToString(), | |
| kNetnsPeerIPv6Addr.ToString()), | |
| _, _)) | |
| .WillOnce(Return(true)); | |
| proxy_->SetShillDNSProxyAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetShillDNSProxyAddressesEmptyNameserver) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| // Only IPv4 nameserver. | |
| SetNameServers({"8.8.8.8"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| mock_manager_, | |
| SetDNSProxyAddresses(ElementsAre(kNetnsPeerIPv4Addr.ToString()), _, _)) | |
| .WillOnce(Return(true)); | |
| proxy_->SetShillDNSProxyAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Only IPv6 nameserver. | |
| SetNameServers(/*ipv4_nameservers=*/{}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL( | |
| mock_manager_, | |
| SetDNSProxyAddresses(ElementsAre(kNetnsPeerIPv6Addr.ToString()), _, _)) | |
| .WillOnce(Return(true)); | |
| proxy_->SetShillDNSProxyAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_ClearShillDNSProxyAddresses) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| EXPECT_CALL(mock_manager_, ClearDNSProxyAddresses(_, _)); | |
| proxy_->ClearShillDNSProxyAddresses(); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SendIPAddressesToController) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| ProxyAddrMessage msg; | |
| msg.set_type(ProxyAddrMessage::SET_ADDRS); | |
| msg.add_addrs(kNetnsPeerIPv4Addr.ToString()); | |
| msg.add_addrs(kNetnsPeerIPv6Addr.ToString()); | |
| EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg))) | |
| .WillOnce(Return(true)); | |
| proxy_->SendIPAddressesToController(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SendIPAddressesToControllerEmptyNameserver) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| // Only IPv4 nameserver. | |
| SetNameServers({"8.8.8.8"}, /*ipv6_nameservers=*/{}); | |
| ProxyAddrMessage msg; | |
| msg.set_type(ProxyAddrMessage::SET_ADDRS); | |
| msg.add_addrs(kNetnsPeerIPv4Addr.ToString()); | |
| EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg))) | |
| .WillOnce(Return(true)); | |
| proxy_->SendIPAddressesToController(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Only IPv6 nameserver. | |
| SetNameServers(/*ipv4_nameservers=*/{}, {"2001:4860:4860::8888"}); | |
| msg.Clear(); | |
| msg.set_type(ProxyAddrMessage::SET_ADDRS); | |
| msg.add_addrs(kNetnsPeerIPv6Addr.ToString()); | |
| EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg))) | |
| .WillOnce(Return(true)); | |
| proxy_->SendIPAddressesToController(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_ClearIPAddressesInController) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| EXPECT_CALL(*msg_dispatcher_, SendMessage(_)).WillOnce(Return(true)); | |
| proxy_->ClearIPAddressesInController(); | |
| } | |
| TEST_F(ProxyTest, ShillInitializedWhenReady) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| // Test class defaults to make shill client ready. Reset to false. | |
| proxy_->shill_ready_ = false; | |
| proxy_->OnShillReady(true); | |
| EXPECT_TRUE(proxy_->shill_ready_); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_ConnectedNamedspace) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| ConnectNamespace(_, _, /*outbound_ifname=*/"", /*route_on_vpn=*/false, | |
| patchpanel::Client::TrafficSource::kSystem, _)) | |
| .WillOnce( | |
| Return(std::make_pair(base::ScopedFD(make_fd()), | |
| patchpanel::Client::ConnectedNamespace{}))); | |
| proxy_->OnPatchpanelReady(true); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_ConnectedNamedspace) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| ConnectNamespace(_, _, /*outbound_ifname=*/"", /*route_on_vpn=*/true, | |
| patchpanel::Client::TrafficSource::kUser, _)) | |
| .WillOnce( | |
| Return(std::make_pair(base::ScopedFD(make_fd()), | |
| patchpanel::Client::ConnectedNamespace{}))); | |
| proxy_->OnPatchpanelReady(true); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_ConnectedNamedspace) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}); | |
| EXPECT_CALL(*patchpanel_client_, | |
| ConnectNamespace(_, _, /*outbound_ifname=*/"eth0", | |
| /*route_on_vpn=*/false, | |
| patchpanel::Client::TrafficSource::kArc, _)) | |
| .WillOnce( | |
| Return(std::make_pair(base::ScopedFD(make_fd()), | |
| patchpanel::Client::ConnectedNamespace{}))); | |
| proxy_->OnPatchpanelReady(true); | |
| } | |
| TEST_F(ProxyTest, ShillResetRestoresAddressProperty) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| EXPECT_CALL(mock_manager_, | |
| SetDNSProxyAddresses(ElementsAre(kNetnsPeerIPv4Addr.ToString(), | |
| kNetnsPeerIPv6Addr.ToString()), | |
| _, _)) | |
| .WillOnce(Return(true)); | |
| proxy_->OnShillReset(true); | |
| } | |
| TEST_F(ProxyTest, StateClearedIfDefaultServiceDrops) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| proxy_->OnDefaultDeviceChanged(nullptr /* no service */); | |
| EXPECT_FALSE(proxy_->device_); | |
| EXPECT_FALSE(proxy_->resolver_); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| proxy_->OnDefaultDeviceChanged(nullptr /* no service */); | |
| EXPECT_TRUE(proxy_->device_); | |
| EXPECT_TRUE(proxy_->resolver_); | |
| } | |
| TEST_F(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kReady); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| EXPECT_FALSE(proxy_->device_); | |
| EXPECT_FALSE(proxy_->resolver_); | |
| } | |
| TEST_F(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, | |
| /*device=*/nullptr, /*set_resolver=*/false); | |
| auto* new_resolver = new MockResolver(); | |
| proxy_->resolver = base::WrapUnique(new_resolver); | |
| EXPECT_CALL(*new_resolver, ListenUDP(_)).WillOnce(Return(true)); | |
| EXPECT_CALL(*new_resolver, ListenTCP(_)).WillOnce(Return(true)); | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline); | |
| brillo::VariantDictionary props; | |
| EXPECT_CALL(mock_manager_, GetProperties(_, _, _)) | |
| .WillOnce(DoAll(SetArgPointee<0>(props), Return(true))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| EXPECT_TRUE(proxy_->resolver_); | |
| } | |
| TEST_F(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}); | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0", | |
| {"8.8.8.8", "8.8.4.4"}, | |
| {"2001:4860:4860::8888", "2001:4860:4860::8844"}); | |
| EXPECT_CALL(*resolver_, | |
| SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"), | |
| StrEq("2001:4860:4860::8888"), | |
| StrEq("2001:4860:4860::8844")))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_ShillPropertyUpdatedOnDefaultServiceComesOnline) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline); | |
| EXPECT_CALL(mock_manager_, SetDNSProxyAddresses(_, _, _)) | |
| .WillOnce(Return(true)); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_IgnoresVPN) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Expect default device changes to WiFi. | |
| auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kWifi); | |
| proxy_->OnDefaultDeviceChanged(wifi.get()); | |
| EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi); | |
| // Expect default device to still be WiFi even when a VPN is active. | |
| auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kVPN); | |
| proxy_->OnDefaultDeviceChanged(vpn.get()); | |
| EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_GetsPhysicalDeviceOnInitialVPN) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| shill_client_->default_device_ = | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kWifi); | |
| auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kVPN); | |
| proxy_->OnDefaultDeviceChanged(vpn.get()); | |
| EXPECT_TRUE(proxy_->device_); | |
| EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_UsesVPN) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}); | |
| auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kWifi); | |
| proxy_->OnDefaultDeviceChanged(wifi.get()); | |
| EXPECT_TRUE(proxy_->device_); | |
| EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi); | |
| auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kVPN); | |
| proxy_->OnDefaultDeviceChanged(vpn.get()); | |
| EXPECT_TRUE(proxy_->device_); | |
| EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kVPN); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_NameServersUpdatedOnDeviceChangeEvent) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"}); | |
| // Set name servers on device change event. | |
| auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kWifi, "wlan0", | |
| {"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL(*resolver_, | |
| SetNameServers(ElementsAre(StrEq("8.8.8.8"), | |
| StrEq("2001:4860:4860::8888")))); | |
| proxy_->OnDeviceChanged(wifi.get()); | |
| // Verify it only applies changes for the correct interface. | |
| auto eth = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0", | |
| {"8.8.8.8", "8.8.4.4"}, | |
| {"2001:4860:4860::8888", "2001:4860:4860::8844"}); | |
| EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0); | |
| proxy_->OnDeviceChanged(eth.get()); | |
| // Update WiFi device nameservers. | |
| wifi->network_config.dns_servers = StringsToIPAddressesChecked( | |
| {"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"}); | |
| EXPECT_CALL(*resolver_, | |
| SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"), | |
| StrEq("2001:4860:4860::8888"), | |
| StrEq("2001:4860:4860::8844")))); | |
| proxy_->OnDeviceChanged(wifi.get()); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_NameServersUpdatedOnDeviceChangeEvent) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Set name servers on device change event. | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0", | |
| {"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL(*resolver_, | |
| SetNameServers(ElementsAre(StrEq("8.8.8.8"), | |
| StrEq("2001:4860:4860::8888")))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| // Now trigger an NetworkConfig change. | |
| dev->network_config.dns_servers = StringsToIPAddressesChecked( | |
| {"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"}); | |
| EXPECT_CALL(*resolver_, | |
| SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"), | |
| StrEq("2001:4860:4860::8888"), | |
| StrEq("2001:4860:4860::8844")))); | |
| proxy_->OnDeviceChanged(dev.get()); | |
| } | |
| TEST_F(ProxyTest, DeviceChangeEventIgnored) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0"); | |
| // Set name servers on device change event. | |
| EXPECT_CALL(*resolver_, SetNameServers(_)).Times(1); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| // No change to NetworkConfig, no call to SetNameServers | |
| EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0); | |
| proxy_->OnDeviceChanged(dev.get()); | |
| // Different ifname, no call to SetNameServers | |
| EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0); | |
| dev->ifname = "eth1"; | |
| proxy_->OnDeviceChanged(dev.get()); | |
| } | |
| TEST_F(ProxyTest, BasicDoHDisable) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| brillo::VariantDictionary props; | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, BasicDoHAlwaysOn) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), true)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string(""); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, BasicDoHAutomatic) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, BasicDoHSecureWithFallback) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL(*resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://custom-provider.com")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://custom-provider.com"] = | |
| std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, RemovesDNSQueryParameterTemplate_AlwaysOn) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), true)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com{?dns}"] = std::string(""); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, RemovesDNSQueryParameterTemplate_Automatic) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com{?dns}"] = std::string("8.8.8.8, 8.8.4.4"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, RemovesDNSQueryParameterTemplate_SecureWithFallback) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL(*resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://custom-provider.com")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://custom-provider.com{?dns}"] = | |
| std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, NewResolverConfiguredWhenSet) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4"); | |
| props["https://chrome.cloudflare-dns.com/dns-query"] = | |
| std::string("1.1.1.1,2606:4700:4700::1111"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| SetNameServers({"1.0.0.1", "1.1.1.1"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL(*resolver_, SetNameServers(UnorderedElementsAre( | |
| StrEq("1.1.1.1"), StrEq("1.0.0.1")))); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders( | |
| ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")), | |
| false)); | |
| proxy_->doh_config_.set_resolver(resolver_); | |
| } | |
| TEST_F(ProxyTest, DoHModeChangingFixedNameServers) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| // Initially off. | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| SetNameServers({"1.1.1.1", "9.9.9.9"}, /*ipv6_nameservers=*/{}); | |
| // Automatic mode - matched cloudflare. | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders( | |
| ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4"); | |
| props["https://chrome.cloudflare-dns.com/dns-query"] = | |
| std::string("1.1.1.1,2606:4700:4700::1111"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| // Automatic mode - no match. | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| SetNameServers({"10.10.10.1"}, /*ipv6_nameservers=*/{}); | |
| // Automatic mode - matched google. | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false)); | |
| SetNameServers({"8.8.4.4", "10.10.10.1", "8.8.8.8"}, /*ipv6_nameservers=*/{}); | |
| // Automatic mode - secure DNS with fallback. | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders( | |
| ElementsAre(StrEq("https://custom-provider.com/dns-query")), false)); | |
| props["https://custom-provider.com/dns-query"] = | |
| std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress); | |
| proxy_->OnDoHProvidersChanged(props); | |
| // Explicitly turned off. | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| props.clear(); | |
| proxy_->OnDoHProvidersChanged(props); | |
| // Still off - even switching ns back. | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| SetNameServers({"8.8.4.4", "10.10.10.1", "8.8.8.8"}, /*ipv6_nameservers=*/{}); | |
| // Always-on mode. | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://doh.opendns.com/dns-query")), | |
| true)); | |
| props.clear(); | |
| props["https://doh.opendns.com/dns-query"] = std::string(""); | |
| proxy_->OnDoHProvidersChanged(props); | |
| // Back to automatic mode, though no matching ns. | |
| EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false)); | |
| props.clear(); | |
| props["https://doh.opendns.com/dns-query"] = std::string( | |
| "208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| // Automatic mode working on ns update. | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(ElementsAre(StrEq("https://doh.opendns.com/dns-query")), | |
| false)); | |
| SetNameServers({"8.8.8.8"}, {"2620:119:35::35"}); | |
| } | |
| TEST_F(ProxyTest, MultipleDoHProvidersForAlwaysOnMode) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| EXPECT_CALL(*resolver_, SetDoHProviders(UnorderedElementsAre( | |
| StrEq("https://dns.google.com"), | |
| StrEq("https://doh.opendns.com")), | |
| true)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string(""); | |
| props["https://doh.opendns.com"] = std::string(""); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, MultipleDoHProvidersForAutomaticMode) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders( | |
| ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4"); | |
| props["https://dns.quad9.net/dns-query"] = std::string("9.9.9.9,2620:fe::9"); | |
| props["https://chrome.cloudflare-dns.com/dns-query"] = | |
| std::string("1.1.1.1,2606:4700:4700::1111"); | |
| props["https://doh.opendns.com/dns-query"] = std::string( | |
| "208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| EXPECT_CALL(*resolver_, | |
| SetDoHProviders(UnorderedElementsAre( | |
| StrEq("https://dns.google.com"), | |
| StrEq("https://doh.opendns.com/dns-query"), | |
| StrEq("https://dns.quad9.net/dns-query")), | |
| false)); | |
| SetNameServers({"8.8.8.8", "10.10.10.10"}, {"2620:fe::9", "2620:119:53::53"}); | |
| } | |
| TEST_F(ProxyTest, MultipleDoHProvidersForSecureWithFallbackMode) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders(UnorderedElementsAre( | |
| StrEq("https://custom-provider-1.com"), | |
| StrEq("https://custom-provider-2.com/dns-query")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://custom-provider-1.com"] = | |
| std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress); | |
| props["https://custom-provider-2.com/dns-query"] = | |
| std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress); | |
| proxy_->OnDoHProvidersChanged(props); | |
| } | |
| TEST_F(ProxyTest, DoHBadAlwaysOnConfigSetsAutomaticMode) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline)); | |
| SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{}); | |
| EXPECT_CALL( | |
| *resolver_, | |
| SetDoHProviders( | |
| ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")), | |
| false)); | |
| brillo::VariantDictionary props; | |
| props["https://dns.opendns.com"] = std::string(""); | |
| props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4"); | |
| props["https://dns.quad9.net/dns-query"] = std::string("9.9.9.9,2620:fe::9"); | |
| props["https://chrome.cloudflare-dns.com/dns-query"] = | |
| std::string("1.1.1.1,2606:4700:4700::1111"); | |
| props["https://doh.opendns.com/dns-query"] = std::string( | |
| "208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53"); | |
| proxy_->OnDoHProvidersChanged(props); | |
| EXPECT_CALL(*resolver_, | |
| SetDoHProviders(UnorderedElementsAre( | |
| StrEq("https://dns.google.com"), | |
| StrEq("https://doh.opendns.com/dns-query"), | |
| StrEq("https://dns.quad9.net/dns-query")), | |
| false)); | |
| SetNameServers({"8.8.8.8", "10.10.10.10"}, {"2620:fe::9", "2620:119:53::53"}); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetsDnsRedirectionRule) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // System proxy requests a DnsRedirectionRule to exclude traffic destined | |
| // not to the underlying network's name server. | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0"); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns( | |
| patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _, | |
| kNetnsPeerIPv4Addr.ToString(), _, _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns( | |
| patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _, | |
| kNetnsPeerIPv6Addr.ToString(), _, _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| // System proxy does not call patchpanel on Parallels VM started. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap1", | |
| "eth0")); | |
| // System proxy does not call patchpanel on ARC started. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0", | |
| "eth0")); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Set DNS redirection rule. | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, _, ElementsAre("8.8.8.8"), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, _, ElementsAre("2001:4860:4860::8888"), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->Enable(); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleNewDeviceStarted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Empty active device. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->Enable(); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| // Default device changed. | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0", | |
| {"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, _, ElementsAre("8.8.8.8"), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, _, ElementsAre("2001:4860:4860::8888"), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleGuest) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0")); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Guest started. | |
| auto plugin_vm_dev = | |
| virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0"); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault, | |
| "vmtap0", kNetnsPeerIPv4Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault, | |
| "vmtap0", kNetnsPeerIPv6Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| plugin_vm_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| // Guest stopped. | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_NeverSetsDnsRedirectionRuleOtherGuest) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, | |
| ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0")); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Other guest started. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0", | |
| "eth0")); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Added) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, /*ipv6_addr=*/std::nullopt); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns( | |
| patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _, | |
| kNetnsPeerIPv6Addr.ToString(), _, _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| // Proxy's ConnectedNamespace peer interface name is set to empty and | |
| // RTNL message's interface index is set to 0 in order to match. | |
| // if_nametoindex which is used to get the interface index will return 0 on | |
| // error. | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeAdd, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| msg.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg); | |
| } | |
| TEST_F(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Deleted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| proxy_->lifeline_fds_.emplace(std::make_pair("", AF_INET6), | |
| base::ScopedFD(make_fd())); | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeDelete, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| proxy_->RTNLMessageHandler(msg); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleWithoutIPv6) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, /*ipv6_addr=*/std::nullopt); | |
| // Default device changed. | |
| auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline, | |
| shill::Client::Device::Type::kEthernet, "eth0", | |
| {"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, _, ElementsAre("8.8.8.8"), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnDefaultDeviceChanged(dev.get()); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 1); | |
| // Guest started. | |
| auto plugin_vm_dev = | |
| virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0"); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault, | |
| "vmtap0", kNetnsPeerIPv4Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| plugin_vm_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| // Guest stopped. | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 1); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Added) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"}); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, /*ipv6_addr=*/std::nullopt); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillOnce( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")})); | |
| EXPECT_CALL(*patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, | |
| _, kNetnsPeerIPv6Addr.ToString(), _, _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault, | |
| "vmtap0", kNetnsPeerIPv6Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| // Proxy's ConnectedNamespace peer interface name is set to empty and | |
| // RTNL message's interface index is set to 0 in order to match. | |
| // if_nametoindex which is used to get the interface index will return 0 on | |
| // error. | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeAdd, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| msg.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Deleted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| proxy_->lifeline_fds_.emplace(std::make_pair("", AF_INET6), | |
| base::ScopedFD(make_fd())); | |
| proxy_->lifeline_fds_.emplace(std::make_pair("vmtap0", AF_INET6), | |
| base::ScopedFD(make_fd())); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillOnce( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")})); | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeDelete, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| proxy_->RTNLMessageHandler(msg); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| } | |
| TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kDefault}, ShillDevice()); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillRepeatedly( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")})); | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| // Proxy's ConnectedNamespace peer interface name is set to empty and | |
| // RTNL message's interface index is set to -1 in order to not match. | |
| // if_nametoindex which is used to get the interface index will return 0 on | |
| // error. | |
| net_base::RTNLMessage msg_unrelated_ifindex( | |
| net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd, | |
| 0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */, | |
| AF_INET6); | |
| msg_unrelated_ifindex.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| msg_unrelated_ifindex.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg_unrelated_ifindex); | |
| net_base::RTNLMessage msg_unrelated_scope( | |
| net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd, | |
| 0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */, | |
| AF_INET6); | |
| msg_unrelated_scope.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK)); | |
| msg_unrelated_scope.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg_unrelated_scope); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Set devices created before the proxy started. | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillOnce( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")})); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc, | |
| "arc_eth0", kNetnsPeerIPv4Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc, | |
| "arc_eth0", kNetnsPeerIPv6Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->Enable(); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleNewDeviceStarted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // Guest started. | |
| auto arc_dev = virtualdev(patchpanel::Client::GuestType::kArcContainer, | |
| "arc_eth0", "eth0"); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc, | |
| "arc_eth0", kNetnsPeerIPv4Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc, | |
| "arc_eth0", kNetnsPeerIPv6Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| arc_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 2); | |
| // Guest stopped. | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kRemoved, arc_dev); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherGuest) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| proxy_->ns_peer_ipv6_address_ = kNetnsPeerIPv6Addr; | |
| // Other guest started. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| virtualdev(patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherIfname) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"}); | |
| proxy_->device_ = ShillDevice(); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, kNetnsPeerIPv6Addr); | |
| // ARC guest with other interface started. | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| proxy_->OnVirtualDeviceChanged( | |
| patchpanel::Client::VirtualDeviceEvent::kAdded, | |
| virtualdev(patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Added) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| SetNamespaceAddresses(kNetnsPeerIPv4Addr, /*ipv6_addr=*/std::nullopt); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillOnce( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")})); | |
| EXPECT_CALL( | |
| *patchpanel_client_, | |
| RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc, | |
| "arc_eth0", kNetnsPeerIPv6Addr.ToString(), IsEmpty(), _)) | |
| .WillOnce(Return(ByMove(base::ScopedFD(make_fd())))); | |
| // Proxy's ConnectedNamespace peer interface name is set to empty and | |
| // RTNL message's interface index is set to 0 in order to match. | |
| // if_nametoindex which is used to get the interface index will return 0 on | |
| // error. | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeAdd, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| msg.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Deleted) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| proxy_->lifeline_fds_.emplace(std::make_pair("arc_eth0", AF_INET6), | |
| base::ScopedFD(make_fd())); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillOnce( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")})); | |
| net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress, | |
| net_base::RTNLMessage::kModeDelete, 0 /* flags */, | |
| 0 /* seq */, 0 /* pid */, 0 /* interface_index */, | |
| AF_INET6); | |
| msg.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| proxy_->RTNLMessageHandler(msg); | |
| EXPECT_EQ(proxy_->lifeline_fds_.size(), 0); | |
| } | |
| TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"}, | |
| ShillDevice()); | |
| EXPECT_CALL(*patchpanel_client_, GetDevices()) | |
| .WillRepeatedly( | |
| Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev( | |
| patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")})); | |
| EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0); | |
| // Proxy's ConnectedNamespace peer interface name is set to empty and | |
| // RTNL message's interface index is set to -1 in order to not match. | |
| // if_nametoindex which is used to get the interface index will return 0 on | |
| // error. | |
| net_base::RTNLMessage msg_unrelated_ifindex( | |
| net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd, | |
| 0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */, | |
| AF_INET6); | |
| msg_unrelated_ifindex.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE)); | |
| msg_unrelated_ifindex.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg_unrelated_ifindex); | |
| net_base::RTNLMessage msg_unrelated_scope( | |
| net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd, | |
| 0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */, | |
| AF_INET6); | |
| msg_unrelated_scope.set_address_status( | |
| net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK)); | |
| msg_unrelated_scope.SetAttribute(IFA_ADDRESS, kNetnsPeerIPv6Addr.ToBytes()); | |
| proxy_->RTNLMessageHandler(msg_unrelated_scope); | |
| } | |
| TEST_F(ProxyTest, UpdateNameServers) { | |
| SetUpProxy(Proxy::Options{.type = Proxy::Type::kSystem}, ShillDevice()); | |
| proxy_->device_->network_config.dns_servers = StringsToIPAddressesChecked( | |
| {// IPv4 name servers. | |
| "8.8.8.8", "192.168.1.1", | |
| // IPv6 name servers. | |
| "eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e", "::2"}); | |
| proxy_->UpdateNameServers(); | |
| const std::vector<net_base::IPv4Address> expected_ipv4_dns_addresses = { | |
| net_base::IPv4Address(8, 8, 8, 8), net_base::IPv4Address(192, 168, 1, 1)}; | |
| const std::vector<net_base::IPv6Address> expected_ipv6_dns_addresses = { | |
| *net_base::IPv6Address::CreateFromString( | |
| "eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e"), | |
| *net_base::IPv6Address::CreateFromString("::2")}; | |
| EXPECT_THAT(proxy_->doh_config_.ipv4_nameservers(), | |
| expected_ipv4_dns_addresses); | |
| EXPECT_THAT(proxy_->doh_config_.ipv6_nameservers(), | |
| expected_ipv6_dns_addresses); | |
| } | |
| } // namespace dns_proxy |